GamePlay 3D Framework-3Dゲームのクロスプラットフォーム開発の簡単なスタート

親愛なるハラジテリ!



大学でコンピューターグラフィックコースを聴き、OpenGLで十分に遊んだ後、ゲーム開発に取り掛かり、自分で試す時間であると判断しました。 率直に言って、私は本当に自分のエンジンを書きたくありませんでした。 主な目標は、むしろこれがどのように行われるかを確認し、レッスンを引き出し、選択したエンジンに基づいて何かを作成することでした。 簡単な検索で、オープンなクロスプラットフォームエンジンでは少しきついことがわかりました。 1つはLinux、Windows、またはMac OSに問題があり、もう1つはモバイルOSの新しいバージョンがあり、3つ目はほぼ放棄されています...



ゲームプレイ



このフレームワークの名前はGamePlay 3Dです。 インターネット上にはあまり情報がありませんが、Runetについては何と言えますか。 これは、C ++でゲームをプログラミングするためのC ++で記述されたオープンソースフレームワークであり、これに起因するすべての長所と短所があります。 プロジェクトの作成者は、3Dゲーム用のcocos2dアナログの一種である汎用ツールとして位置付けています。 GamePlay 3Dで執筆を開始するには、OpenGL、GLSL、または3D数学の深い知識は必要ありませんが、良い結果を得る方法がないことは誰もが理解しています。 カットの下で開始する詳細と小さな例。



おそらく、エンジンの主な機能の短いリストから始めましょう。



*まあ、ほとんど1つで...



プロジェクトサイトの機能のより完全なリスト
  • フル機能のOpenGL 3.2+(デスクトップ)およびOpenGL ES 2.0(モバイル)ベースのレンダリングシステム
  • 組み込みの共通シェーダーライブラリを備えたシェーダーベースのマテリアルシステム
  • ライト、カメラ、モデル、パーティクルエミッタ、物理衝突オブジェクトをサポートするノードベースのシーングラフシステム
  • 複数の表面レイヤーとLODを備えたハイトマップベースの地形
  • 宣言的なシーンバインディング(マテリアル)およびノー​​ドアタッチメント(パーティクルエミッタ、物理衝突オブジェクト、3Dオーディオソース)
  • 宣言的粒子システム
  • 使いやすいテキストとスプライトのレンダリング
  • 物理システム(Bullet物理を使用)

    • 車両物理学
    • キャラクター物理学
    • 剛体力学
    • 制約
    • ゴーストオブジェクト
  • テーマ可能な2Dおよび3Dフォームをサポートする宣言的UIシステム。 次の組み込みのコアコントロールとレイアウトが含まれています。

    • ボタン
    • ラベル
    • テキストボックス
    • スライダー
    • チェックボックス
    • ラジオボタン
    • 絶対レイアウト
    • 縦型レイアウト
    • フローレイアウト
  • 骨格キャラクターのアニメーションをサポートする完全に拡張可能なアニメーションシステム
  • WAVおよびOGGをサポートする完全な3Dオーディオシステム
  • 2D / 3D数学および3Dオブジェクトカリングをサポートする完全なベクトル数学ライブラリ
  • マウス、キーボード、タッチ、ジェスチャー、ゲームパッドのサポート
  • Luaスクリプトバインディングおよびバインディングジェネレーターツール
  • AIステートマシン


ここでは、排他的なオープンソースツールを使用して、小さなシーン、いくつかのオブジェクト、物理学、基本的な照明を備えた小さなデモをすばやく簡単に作成する方法を説明します。 使用された資料へのリンクと完成したプロジェクトのアーカイブは、テキスト全体に散らばっており、記事の最後にまとめられています。



Linuxで最初のデモを作成します。 必要なもの:



エンジンのビルドは、Githubからソースコードをダウンロードし、依存関係をインストールし、 ビルドディレクトリでcmake ... &&を実行します (WindowsおよびMac OS Xユーザーは、それぞれ既存のVisual StudioまたはXcodeプロジェクトを開き、ワンクリックでコンパイルします)。 コンパイルが正常に完了したら、フレームワークのルートディレクトリにあるgameplay-newproject。(Sh | bat)スクリプトを実行して、プロジェクトの作成を開始します。 スクリプトはいくつかの質問をし、答えて、指定したディレクトリにあるプロジェクトの完成した魚を取得します。 Demo procketを呼び出して、現在のディレクトリ(エンジンのルートディレクトリ)をパスとして指定しましょう。



プロジェクト



作成されたプロジェクトには、ゲームのコンパイルと開始に必要なすべてのファイル、およびVisual StudioとXcodeのプロジェクトファイルが既に含まれています。 Linuxユーザーは、自分でIDEのプロジェクトを設定する必要があります。 設定全体は、ゲームのソースを含むディレクトリの指定、プロジェクトへのエンジンライブラリの追加などで構成されます。 これについては説明しませんが、通常のvim、cmake、makeユーティリティを使用します。 プロジェクトをコンパイルして実行し、何があるかを確認してから、プロジェクトの構成を理解し、少しドラマを追加します。

cd Demo/build cmake .. make
      
      





結果のバイナリを実行します。

 cd ../bin/linux ./Demo
      
      





すべてが正常であれば、次のように表示されます。

画像



では、VS、Xcode、およびcmakeプロジェクトファイルを除いて、ゲームフォルダーには何が表示されますか?

game.config-許可、フルスクリーンまたはウィンドウなどの基本設定

res / -ゲームリソースを含むディレクトリ

res / box.gpb-ネイティブ形式でエンコードされたゲームシーン(カメラと光源を備えたキューブ)

box.dae-未加工形式のゲームシーン、この場合はCOLLADA形式

coloured。(frag | vert)-キューブマテリアルで使用されるシェーダー

res / box.material-キューブマテリアルの説明

src / -ソース

src / Demo(cpp | h) -基本クラス

android /-Android用のゲームを構築するためのプロジェクト

bin / -コンパイルされたバイナリ



しかし、回転キューブを使用した完成した例は単純すぎて退屈です。さらに、エンジンをもう少し掘り下げて、さらにキューブとボールを追加して、物理的特性と材料を決定します。



結果として得られるものは次のとおりです。





まず、Blenderでシーンを作成します。キューブは、完全な幸福のために、いくつかのボールのすぐ上にある特定の表面にぶら下がります。 ゲームでは、Blenderとは異なり、高さはzではなくy軸によって決定されるという事実に注意することが重要です(実際、問題ではありませんが、GamePlay 3Dのy軸で高さが決定されない場合、いくつかの問題があるようです。実験の純度のためにyを使用します)。 ゲームコードからアクセスして相互に区別できるように、何らかの方法ですべてのオブジェクトに名前を付ける必要があります。 単純に行います。すべてのキューブは、 boxX 、balls ballXXは任意の数と呼ばれます。 彼らが舞い上がる表面は床と呼ばれます。 カメラをカメラと呼び、回転させて、必要なものがレンズに収まるようにします。 シーンは準備ができており、ゲームプレイエンコーダーユーティリティが理解できる形式でエクスポートできます。 FBXとCOLLADAの2つのオプションから選択できます。 この段階では簡単であるため、COLLADAを選択します(詳細は記事の最後にあります)。



結果は次のようになります。

ブレンダー



怠zyな人やブレンダーを手に持っていない人のために
Blenderのシーン: dl.dropbox.com/u/64710641/boxes.blend

COLLADAのシーン: dl.dropbox.com/u/64710641/boxes.dae

GamePlayBundleに変換されたシーン: dl.dropbox.com/u/64710641/boxes.gpb




これが、ゲームプレイエンコーダーの出番です。 このユーティリティは、さまざまな消化できないファイル形式をフレームワークのネイティブバイナリ形式に変換します。 XMLのシーンのパーサーに余分な時間とメモリを費やしたり、3Dでベクターフォントをレンダリングしたりしたくないのですか。 そのようなオブジェクトを事前に解析し、最適化されたバイナリ形式で保存して、後でメモリに単純にロードすることをお勧めします。 そのため、手首を軽く振ってBlenderからエクスポートしたscene.daeファイルをscene.gpbに変換し、このファイルをプロジェクトのres /サブディレクトリに配置します。

 path/to/GamePlay/bin/linux/gameplay-encoder scene.dae
      
      





回転キューブを使用した最初の例とは異なり、シーン内のオブジェクトを手動で検索してそのプロパティを決定することはありません。 シーンを記述するファイルを作成するだけで、フレームワークはそれ自体ですべてを実行します。 したがって、game.sceneシーン記述ファイル:

 scene boxes { //     path = res/boxes.gpb //    activeCamera = camera //     floor node floor { //  ,     material = res/game.material#floor //   ,   collisionObject res/game.physics#floor } //   node boxes { //  url,          boxX url = box* //  material = res/game.material#box //  collisionObject = res/game.physics#box } //    node ball { url = ball* material = res/game.material#ball collisionObject = res/game.physics#ball } //      physics { //     y gravity = 0.0, -9.8, 0.0 } }
      
      





示されたマテリアルと物理的プロパティは、天井からではなく、対応するgame.materialとgame.physicsファイルから取得されます。



game.material:

 //       material colored { technique { //      pass 0 { //     ,  //      u_worldViewProjectionMatrix = WORLD_VIEW_PROJECTION_MATRIX u_inverseTransposeWorldViewMatrix = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX //         ... //      renderState { cullFace = true depthTest = true } //        vertexShader = res/shaders/colored.vert fragmentShader = res/shaders/colored.frag } } } //   .     colored material box : colored { //       u_diffuseColor = 0.8, 0.2, 0.2, 1.0 } //    material ball : colored { u_diffuseColor = 0.2, 0.2, 0.8, 1.0 } //   material floor : colored { u_diffuseColor = 0.2, 0.8, 0.2, 1.0 }
      
      





シェーダーの変数がどこからともなく表示されるわけではないことに注意してください。 シェーダーコードに記述されている内容に注意を払い、必要に応じてマテリアルの対応する変数を決定する必要があります。 フレームワークには、さまざまなタイプの照明、テクスチャリングなどを実装する既製のシェーダーの小さなライブラリが付属しています。 オブジェクトを1色で着色する簡単なオプションを使用しました。 この作業を行うには、シェーダーライブラリ全体をpath / to / GamePlay / gameplay / res / shadersからres / shadersのプロジェクトにコピーするだけです。



game.physics:

 //     collisionObject box { //   -   type = RIGID_BODY //  bounding box -  shape = BOX //   mass = 2.0 } //     collisionObject ball { type = RIGID_BODY //  bounding box -  shape = SPHERE mass = 1.0 } //    ,     collisionObject floor { type = RIGID_BODY shape = BOX mass = 0 }
      
      





可能な物理パラメーターの値については、ドキュメントで詳しく説明しています。



石鹸は、ゲームコードでscene.gpbをダウンロードするのはあなた次第です。

基本クラスDemo、継承クラスGameがあります。 ゲームのライフサイクルは次のとおりです。

  1. initialize()メソッドでのゲームの初期化
  2. 更新メソッドの各フレームの前に世界の状態を更新します(floatlapseTime)。
  3. renderメソッドでのシーンのレンダリング(floatlapseTime);
  4. finalize()メソッドでアプリケーションを終了します


initialize()メソッドを変更し、 update(float exceededTime)メソッドを空にします。



通常、 initialize()メソッドでは、シーンファイル、スクリプトをロードし、ゲームの初期状態を設定します。 だから私たちはやる:

 void Demo::initialize() { //        game.scene, //         _scene = Scene::load("res/game.scene"); //  - .         Blender, //            _lightNode = Node::create("directionalLight1"); //      ,    Light* light = Light::createDirectional(_scene->getActiveCamera()->getNode()->getForwardVector()); //    light->setColor(1.0, 1.0, 1.0); //        _lightNode->setLight(light); //         _scene->getActiveCamera()->setAspectRatio((float)getWidth() / (float)getHeight()); //     ,     _scene->visit(this, &Demo::initializeScene); }
      
      





initializeScene()メソッドを追加します 。このメソッドは、作成した光源を使用して各シーンオブジェクトに照明パラメーターを割り当てます。 ここのコメントは不要だと思います。

 bool Demo::initializeScene(Node* node) { Model* model = node->getModel(); if (model) { Material* material = model->getMaterial(); if (material && material->getTechnique()->getPassByIndex(0)->getEffect()->getUniform("u_lightDirection")) { material->getParameter("u_ambientColor")->setValue(_scene->getAmbientColor()); material->getParameter("u_lightColor")->setValue(_lightNode->getLight()->getColor()); material->getParameter("u_lightDirection")->setValue(_lightNode->getForwardVectorView()); } } return true; }
      
      





initializeScene()メソッドの署名とプライベート変数Node * _lightNodeをDemo.hに追加することを忘れないでください。

update(floatlapseTime)メソッドは空のままになります。 この例では、世界の状態を変更しません;物理学はすべてを行います。



Demo.hおよびDemo.cppの完全なリスト
 #ifndef TEMPLATEGAME_H_ #define TEMPLATEGAME_H_ #include "gameplay.h" using namespace gameplay; /** * Main game class. */ class Demo: public Game { public: /** * Constructor. */ Demo(); /** * @see Game::keyEvent */ void keyEvent(Keyboard::KeyEvent evt, int key); /** * @see Game::touchEvent */ void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex); protected: /** * @see Game::initialize */ void initialize(); /** * @see Game::finalize */ void finalize(); /** * @see Game::update */ void update(float elapsedTime); /** * @see Game::render */ void render(float elapsedTime); private: /** * Draws the scene each frame. */ bool drawScene(Node* node); bool initializeScene(Node* node); Scene* _scene; Node* _lightNode; }; #endif
      
      





 #include "Demo.h" // Declare our game instance Demo game; Demo::Demo() : _scene(NULL), _lightNode(NULL) { } void Demo::initialize() { _scene = Scene::load("res/game.scene"); _lightNode = Node::create("directionalLight1"); Light* light = Light::createDirectional(_scene->getActiveCamera()->getNode()->getForwardVector()); light->setColor(1.0, 1.0, 1.0); _lightNode->setLight(light); // Set the aspect ratio for the scene's camera to match the current resolution _scene->getActiveCamera()->setAspectRatio((float)getWidth() / (float)getHeight()); _scene->visit(this, &Demo::initializeScene); } void Demo::finalize() { SAFE_RELEASE(_lightNode); SAFE_RELEASE(_scene); } void Demo::update(float elapsedTime) { } void Demo::render(float elapsedTime) { // Clear the color and depth buffers clear(CLEAR_COLOR_DEPTH, Vector4::zero(), 1.0f, 0); // Visit all the nodes in the scene for drawing _scene->visit(this, &Demo::drawScene); } bool Demo::initializeScene(Node* node) { Model* model = node->getModel(); if (model) { Material* material = model->getMaterial(); if (material && material->getTechnique()->getPassByIndex(0)->getEffect()->getUniform("u_lightDirection")) { material->getParameter("u_ambientColor")->setValue(_scene->getAmbientColor()); material->getParameter("u_lightColor")->setValue(_lightNode->getLight()->getColor()); material->getParameter("u_lightDirection")->setValue(_lightNode->getForwardVectorView()); } } return true; } bool Demo::drawScene(Node* node) { // If the node visited contains a model, draw it Model* model = node->getModel(); if (model) { model->draw(); } return true; } void Demo::keyEvent(Keyboard::KeyEvent evt, int key) { if (evt == Keyboard::KEY_PRESS) { switch (key) { case Keyboard::KEY_ESCAPE: exit(); break; } } } void Demo::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex) { switch (evt) { case Touch::TOUCH_PRESS: break; case Touch::TOUCH_RELEASE: break; case Touch::TOUCH_MOVE: break; }; }
      
      





コンパイルする前にcmakeを実行して、アセンブリに新しいファイルを含めることを忘れないでください。 コンパイルし、完成したバイナリを実行し、キューブとボールが指定した色を持ち、指定した物理法則の影響を受けて表面に落ち、作成した光源のパラメーターに従って照明される様子を観察します。 やった!



クロスプラットフォーム



他のプラットフォーム用のゲームのアセンブリへの小さな余談。



Android: Android SDKとNDKをダウンロードし、Apache Antをインストールする必要があります。 SDKおよびNDKへのパスはPATHに含まれている必要があります。 プロジェクトのandroidディレクトリに移動し、 update project -t 1 -pを実行します。 -s && ndk-buildを実行して、完成したapkを取得します。

Mac OS X / iOS: Xcodeでプロジェクトファイルを開き、ビルド、実行します。

Windows: Visual Studioプロジェクトファイルを開き、ビルド、実行します。

Blackberry Playbook:サイトの説明を参照してください。 残念ながら、私はPlaybookの所有者ではなく、状況についてコメントすることはできません。 しかし、以来 フレームワークの作者はもともとPlaybookで書いたので、問題はないはずです。



短所


それらなしでどこに?



これまでに遭遇した最大の問題は、ブレンダーとゲームプレイエンコーダーです。 最初のものは、法線マップをCOLLADAにエクスポートする方法を知らないため、バンプマッピングを忘れることができます。 フレームワークのユーザーの1人によって書かれたパッチがありますが、このパッチはまだBlenderコードに含まれておらず、その組み込みはまだ計画されていません。 このパッチを使ってBlenderを再構築するのは簡単な作業ではありません。今のところ成功していません。 もちろん、COLLADAの代わりにFBXを使用できます。 しかし、もう1つの問題があります。ゲームプレイエンコーダーを再構築するのはそれほど簡単ではありません。 既製のバイナリとして提供され、FBXサポートなしで正常に動作します。 FBXは独自仕様です。 FBX SDKはオートデスクのWebサイトから無料でダウンロードできますが、FBXをサポートするゲームプレイエンコーダーを構築することも大きな頭痛の種です。 古いバージョンのopencolladaが使用されており、libpcrecppの新しいバージョンへのリンクを拒否している場合、FBX SDKがいたずらな場合など そして、Ubuntu 12.04でこれがまだ可能であるなら、より最近のLinuxで-悲しみ。 解決策は、バンプマッピングが必要な場所でMayaまたは3DsMaxを使用するか、他の方法を使用することです。 または、Blenderまたはゲームプレイエンコーダの構築に時間を費やします。



ドキュメント-それは美しいです。 すべてのクラスとメソッドは十分に文書化されています。gameplay-apiディレクトリには、HTMLによって生成されたdoxygenがあります。 しかし、チュートリアルはほとんどありません。 このフレームワークには、gameplay-samplesディレクトリにいくつかのゲームの例が付属しています。 それらのいくつかの作成については、サイトのドキュメントで説明されていますが、完全にはほど遠いです。 これらの例で何が起こっているのか、そしてその理由を理解するには少し時間がかかるかもしれません。



軽微なバグ。 プロジェクトは明らかに非常に若いです。 小さなバグがある場合もありますが、すぐに修正されます。 作成者は、プールのリクエストとパッチを積極的に受け入れており、一般にコミュニティフレンドリーです。



まとめ



この例では、GamePlay 3Dが提供するもののほんの一部に触れました。 物理エンジンを使用した車両とキャラクターの同じ単純なシミュレーション、アニメーションとサウンドの単純なプログラミング、さまざまなデバイスからの入力の基本的な制御、さまざまなプラットフォームの構成の便利な分離に至るまで、他にも多くの利点があります。 最近、Habréに関する記事が、Android向けのゲームを書くのがどれほど難しいかについて説明しました。これは、とりわけ、さまざまな電話がハードウェアテクスチャ圧縮にさまざまなチップを使用しているためです。 たとえば、この問題を解決するために、異なるチップの対応するテクスチャへのパスを持つ異なる構成ファイルを作成できます:atcチップを搭載したデバイスのgame.atc.config、dxtのgame.dxt.config、pvrのgame.pvr.config、game。 PC用のpng.confを選択すると、エンジンは特定のデバイスに必要な構成を選択しますが、それ以上の質問はありません。



皆さんがこの記事を楽しんで、誰かがこのすばらしいフレームワークの開発に参加するか、次の(または最初の)プロジェクトを書くことを決定することを願っています。 投稿は非常に膨大であることが判明したため、資料を詳しく見て、デモに対話性を追加し、もう一度音をねじ込んでみましょう。



プロジェクトサイト: gameplay3d.org



Blenderのプロジェクトとシーンソースでアーカイブします: dl.dropbox.com/u/64710641/Demo_complete.zip

Blenderのシーン: dl.dropbox.com/u/64710641/boxes.blend

COLLADAのシーン: dl.dropbox.com/u/64710641/boxes.dae

GamePlayBundleに変換されたシーン: dl.dropbox.com/u/64710641/boxes.gpb



結論として、プロジェクトフォーラムで開発を公開したユーザーからのいくつかの例です。

流体力学実験


ConductのFeather Duster BB10アプリ


GamePlay 3D + Kinect

BBGamePlay + OpenNIを使用したARボディースーツ


名前のないKinectゲーム



All Articles