マーマレードはマルチプラットフォームアプリケーションを作成するための優れたツールであることをお伝えします。 LG TVとモバイルデバイス(Android、Bada、iOS、Symbian、およびWindowsMobileベース)の両方のアプリケーションを作成できます。
この記事には以下が含まれています。
- Marmalade SDKでアプリケーションを作成するプロセス。
- 投影構造を使用した凸多角形の衝突検出
- 衝突に対する反応の決定(最小経路に沿ったオブジェクトの出力)。
序文のビット。 一度、1つのプロジェクトで、さまざまなオブジェクトの衝突チェックを実装する必要がありました。 そして、衝突やオブジェクトの交差のチェックなしではゲームがほとんどできないという事実にもかかわらず、そのようなタスクの既製のソリューションと簡単な例を見つけるのはそれほど簡単ではないという事実に非常に驚きました。 特に多かれ少なかれ明確な説明で。 自分で実装する必要がありました。
この記事のすべてのコードはC ++で書かれていることを事前に警告したいと思います。
いずれかのサイトで、投影構造を使用した衝突チェックの説明が見つかりました。 この方法は比較的単純で十分に高速です。 おそらく最大の欠点は、凸ポリゴンにのみ適していることです。 しかし、この問題は、凹型ポリゴンを2つ以上の凸型ポリゴンに分割することで解決できます。
それでは、簡単なものから始めましょう-マーマレードプロジェクトを作成します。
マーマレードでは、すべてがmkbファイルで始まるため、検討してください。 プロジェクトフォルダーにファイルtest2d.mkbとソースフォルダーを作成します。 mkbファイルに、次のように記述します。
#!/usr/bin/env mkb
files {
[Source]
(source)
main.cpp
entity.cpp
entity.h
}
subprojects {
iw2d
}
タグ「files」では、プロジェクトのファイルのリストを記述します。 ソースフォルダーには3つのファイルしかありません(括弧の内容はこれを示しています)。 「subprojects」タグでは、使用するマーマレードライブラリをリストします。 この場合、2Dグラフィックスを操作するためのライブラリは1つだけです。
「ソース」フォルダーに3つの空のファイル(main.cpp、entity.cpp、およびentity.h)を作成し、test2d.mkbを実行します。 その結果、Marmaladeは開発環境に必要なデータ(私の場合、これはVisual Studioの「build_test2d_vc10」フォルダー)とプロジェクト設定の「data」フォルダーを生成します。
プロジェクトが作成されたので、コードを書き始めましょう。
主なタスクは次のとおりです。
- ステージ上にいくつかのオブジェクトを作成します。
- オブジェクトの1つの制御を実装します(制御はキーボードによって実行されます:矢印-移動、1および2-回転)。
- 制御されたオブジェクトとシーン内の他のオブジェクトとの衝突チェックを実装し、衝突に対する適切な応答を提供します。
したがって、main.cppファイルのコードを検討します。このファイルでは、初期化、必要な形状とオブジェクトの作成、アプリケーションのメインサイクルの実装、およびアプリケーションの終了時にデータクリーニングを実行します。
#include "iw2d.h" #include "iwArray.h" #include "entity.h" #include "s3eKeyboard.h" #include "s3eDevice.h" #include <time.h> #include <s3e.h> #define FPS 30 #define MOVE_SPEED 100 #define OBJECT_DIMENSION 50 /***************************************************************/ int main(int argc, char* argv[]) { s3eResult result = S3E_RESULT_SUCCESS; /* 2D */ Iw2DInit(); /* */ uint32 width = Iw2DGetSurfaceWidth(); uint32 height = Iw2DGetSurfaceHeight(); /* "" */ int wall_size = MAX(width, height); /* */ CIwArray<CEntity*> arr_objects; /* 3 */ /* */ CIwSVec2 m_Verts1[5]; CIwSVec2 m_Verts2[4]; CIwSVec2 m_Verts3[4]; /* 1 */ m_Verts1[0] = CIwVec2(-OBJECT_DIMENSION, -OBJECT_DIMENSION); m_Verts1[1] = CIwVec2(OBJECT_DIMENSION, -OBJECT_DIMENSION); m_Verts1[2] = CIwVec2(OBJECT_DIMENSION, 0); m_Verts1[3] = CIwVec2(0, OBJECT_DIMENSION); m_Verts1[4] = CIwVec2(-OBJECT_DIMENSION, OBJECT_DIMENSION); /* 2 */ m_Verts2[0] = CIwVec2(-OBJECT_DIMENSION/2, OBJECT_DIMENSION + OBJECT_DIMENSION/2); m_Verts2[1] = CIwVec2(OBJECT_DIMENSION/2, OBJECT_DIMENSION/2); m_Verts2[2] = CIwVec2(OBJECT_DIMENSION/2, -OBJECT_DIMENSION/2); m_Verts2[3] = CIwVec2(-OBJECT_DIMENSION/2, -OBJECT_DIMENSION/2); /* 3: */ m_Verts3[0] = CIwVec2(-5, wall_size/2); m_Verts3[1] = CIwVec2(5, wall_size/2); m_Verts3[2] = CIwVec2(5, -wall_size/2); m_Verts3[3] = CIwVec2(-5, -wall_size/2); /* , */ /* , */ CEntity *main_object = new CEntity(CIwSVec2(width/2, height/2), &m_Verts2[0], 4, 0xFFff0000); arr_objects.append(main_object); /* , . */ CEntity *tmp_object = new CEntity(CIwSVec2(width/2, height/2), &m_Verts1[0], 5, 0xFF0000ff); tmp_object->SetAngle(IW_ANGLE_PI/3); arr_objects.append(tmp_object); tmp_object = new CEntity(CIwSVec2(50, 50), &m_Verts1[0], 5, 0xFF0000ff); tmp_object->SetAngle(IW_ANGLE_PI/2); arr_objects.append(tmp_object); /* 4 "" */ tmp_object = new CEntity(CIwSVec2(0, height/2), &m_Verts3[0], 4, 0xFFff0000); arr_objects.append(tmp_object); tmp_object = new CEntity(CIwSVec2(width, height/2), &m_Verts3[0], 4, 0xFFff0000); arr_objects.append(tmp_object); tmp_object = new CEntity(CIwSVec2(width/2, 0), &m_Verts3[0], 4, 0xFFff0000); tmp_object->SetAngle(IW_ANGLE_PI/2); arr_objects.append(tmp_object); tmp_object = new CEntity(CIwSVec2(width/2, height), &m_Verts3[0], 4, 0xFFff0000); tmp_object->SetAngle(IW_ANGLE_PI/2); arr_objects.append(tmp_object); /* */ int time_between_frames = 1000/FPS; /* */ uint32 timer = (uint32)s3eTimerGetMs(); while(result == S3E_RESULT_SUCCESS) { /* */ s3eKeyboardUpdate(); /* . . */ if (s3eDeviceCheckQuitRequest()) break; /* . */ uint32 now_time = (uint32)s3eTimerGetMs(); int delta = now_time - timer; if (delta < 0) delta = 0; /* */ main_object->Move(CIwSVec2::g_Zero); main_object->Rotate(0); /* */ if ( (s3eKeyboardGetState(s3eKeyLeft) & S3E_KEY_STATE_DOWN) ) { main_object->Move(CIwSVec2(-MOVE_SPEED, 0)); } else if ( (s3eKeyboardGetState(s3eKeyRight) & S3E_KEY_STATE_DOWN) ) { main_object->Move(CIwSVec2(MOVE_SPEED, 0)); } else if ( (s3eKeyboardGetState(s3eKeyUp) & S3E_KEY_STATE_DOWN) ) { main_object->Move(CIwSVec2(0, -MOVE_SPEED)); } else if ( (s3eKeyboardGetState(s3eKeyDown) & S3E_KEY_STATE_DOWN) ) { main_object->Move(CIwSVec2(0, MOVE_SPEED)); } if ( (s3eKeyboardGetState(s3eKey1) & S3E_KEY_STATE_DOWN) ) { main_object->Rotate(-IW_ANGLE_PI / 2); } else if ( (s3eKeyboardGetState(s3eKey2) & S3E_KEY_STATE_DOWN) ) { main_object->Rotate(IW_ANGLE_PI / 2); } /* */ Iw2DSurfaceClear(0xffffffff); /* */ for(int i=0; i < (int)arr_objects.size(); ++i) { arr_objects[i]->Update(delta); } /* (). . */ main_object->SetColour(0xff00ff00); /* , . */ int try_count = 10; bool b_collide; do{ b_collide = false; /* */ for(int i = 0; i < (int)arr_objects.size(); ++i) { /* */ if(arr_objects[i] != main_object){ /* . vec_reaction , . */ CIwVec2 vec_reaction = main_object->CollideRect(arr_objects[i], true); if(!vec_reaction.IsZero()){ main_object->SetColour(0xff000000); b_collide = true; } } } --try_count; /* , "" , . */ } while(b_collide && try_count > 0); /* */ for(int i=0; i < (int)arr_objects.size(); ++i) { arr_objects[i]->Render(); } /* */ Iw2DSurfaceShow(); /* . */ int32 wait_time = (uint32)time_between_frames - ((uint32)s3eTimerGetMs() - now_time); if(wait_time<0)wait_time = 0; timer = now_time; s3eDeviceYield(wait_time); } /* */ for(int i=0; i < (int)arr_objects.size(); ++i) { delete arr_objects[i]; } /* */ arr_objects.clear_optimised(); /* 2d . */ Iw2DTerminate(); return 0; }
複雑なことは何もないようです。 ここで、次の機能が実装されるオブジェクト自体を見てみましょう。
- オブジェクトには形状と色があります。
- オブジェクトは位置を変更(移動)および回転できます。
- オブジェクトは他のオブジェクトとの衝突に応答できます。
- オブジェクトは自分自身を描画できます。
オブジェクトのクラス(entity.hファイル)の説明を見てみましょう。 オブジェクトの機能を明確に説明します。
#pragma once #include "iw2d.h" #include "iwArray.h" #include "IwMath.h" class CEntity{ private: CIwMat2D m_MatLocal; /* */ iwangle angle; /* */ iwangle angle_velocity; /* */ CIwSVec2 m_MoveVelocity; /* */ uint32 m_NumPoints; /* */ CIwSVec2 *m_Points; /* */ CIwVec2 *m_PointsMod; /* */ CIwVec2 *m_Axis; /* */ uint32 colour; public: /* */ CEntity(CIwSVec2 position, CIwSVec2 *verts, int num_verts, uint32 colour = 0xff000000); /* */ virtual ~CEntity(); /* */ void Update(int speed); /* */ void Render(); /* */ void SetAngle(iwangle angle) {this->angle = angle % IW_ANGLE_PI;}; /* */ void Rotate(iwangle rot_angle) { angle_velocity = rot_angle; } /* */ void Move(CIwSVec2 move_velocity) { m_MoveVelocity = move_velocity; } /* */ void SetColour(int colour){this->colour = colour;} /* */ const CIwVec2 *GetVerts(){ return m_PointsMod; } /* */ int GetNumVerts() {return m_NumPoints;} /* . */ CIwVec2 CollideRect(CEntity *other, bool b_uncollide = false); private: /* */ void UpdateVerts(); /* */ /* */ /* b_revert - . */ bool TestOverlaps(CEntity *other, CIwVec2 &min_axis, int &min_t, int b_revert); /* */ void GetInterval(const CIwVec2 *verts, int count, CIwVec2 axis, int &min, int &max); };
クラス自体の実装を見てみましょう。 メソッドCollideRectおよびTestOverlapsには特に注意を払う必要があります 。
#include "entity.h" CEntity::CEntity(CIwSVec2 position, CIwSVec2 *verts, int num_verts, uint32 colour ): m_MatLocal(CIwMat2D::g_Identity), m_MoveVelocity(CIwSVec2::g_Zero), angle(0), angle_velocity(0) { this->colour = colour; this->m_NumPoints = num_verts; m_Points = NULL; m_PointsMod = new CIwVec2[m_NumPoints]; m_Axis = new CIwVec2[num_verts]; m_MatLocal.t = position; m_Points = verts; } CEntity::~CEntity() { if(m_PointsMod)delete m_PointsMod; delete m_Axis; } void CEntity::Update(int speed) { angle += (angle_velocity * speed) / 1000; angle = angle % IW_ANGLE_2PI; m_MatLocal.SetRot(angle, false); if(!m_MoveVelocity.IsZero()) { m_MatLocal.t += ( m_MoveVelocity * speed) / 1000; } UpdateVerts(); } void CEntity::UpdateVerts() { if(!m_Points) return; /* */ for(uint32 i = 0; i < m_NumPoints; ++i) { m_PointsMod[i] = m_MatLocal.TransformVec(m_Points[i]); } /* */ for(uint32 i = 0; i < m_NumPoints; ++i) { /* */ m_Axis[i] = m_PointsMod[(i + 1) % m_NumPoints] - m_PointsMod[i]; /* */ m_Axis[i].Normalise(); /* */ m_Axis[i] = CIwVec2(-m_Axis[i].y, m_Axis[i].x); } } void CEntity::Render() { /* */ Iw2DSetColour(colour); if(!m_Points)return; /* */ Iw2DSetTransformMatrix(m_MatLocal); /* */ Iw2DFillPolygon(m_Points, m_NumPoints); /* */ Iw2DSetColour(0xff00ffff); Iw2DFillArc(CIwSVec2::g_Zero, CIwSVec2(5,5), 0, IW_ANGLE_2PI); } CIwVec2 CEntity::CollideRect(CEntity *other, bool b_uncollide) { /* , */ int min_t(0); CIwVec2 min_axis(CIwFVec2::g_Zero); /* , */ /* .. , */ /* , , . */ if(!TestOverlaps(other, min_axis, min_t, false))return CIwVec2::g_Zero; if(!other->TestOverlaps(this, min_axis, min_t, true))return CIwVec2::g_Zero; /* , */ if(b_uncollide){ CIwVec2 fvec = min_axis * min_t; /* */ m_MatLocal.t += fvec; /* . */ UpdateVerts(); } return min_axis * min_t; } bool CEntity::TestOverlaps(CEntity *other, CIwVec2 &min_axis, int &min_t, int b_revert) { /* , */ const CIwVec2 *other_corner = other->GetVerts(); int other_corner_count = other->GetNumVerts(); /* */ for (uint32 i = 0; i < m_NumPoints; ++i) { int aMin; int aMax; int bMin; int bMax; /* */ GetInterval(m_PointsMod, m_NumPoints, m_Axis[i], aMin, aMax); GetInterval(other_corner, other_corner_count, m_Axis[i], bMin, bMax); /* , */ if ((aMax <= bMin) || (bMax <= aMin)) { /* . */ return false; } /* . , , */ /* */ int t = 0; if(aMax >= bMin) { t = bMin - aMax ; if(min_axis.IsZero() || ABS(min_t) > ABS(t)){ min_t = t * (b_revert?-1:1); min_axis = m_Axis[i]; } } if(bMax >= aMin) { t = bMax - aMin; if(min_axis.IsZero() || ABS(min_t) > ABS(t)){ min_t = t * (b_revert?-1:1); min_axis = m_Axis[i]; } } } return true; } void CEntity::GetInterval(const CIwVec2 *verts, int count, CIwVec2 axis, int &min, int &max) { min = max = axis.Dot(verts[0]); for (int i = 1; i < count; i++) { int value = axis.Dot(verts[i]); min = MIN(min, value); max = MAX(max, value); } }
これで、アプリケーションの準備ができました。 コンパイルして結果を確認します。
アプリケーションは非常にシンプルであり、いくつかのエラーで動作することに注意してください。 削除するには、たとえば、フロートを使用できます。 大まかな、しかしより高速なプライマリ衝突チェックを追加することも良いでしょう。 衝突に対するオブジェクトの反応を変更することもできます(たとえば、衝突中にオブジェクトがバウンドするように)。
批判、コメント、追加、修正は歓迎します。
この記事を書いているとき、衝突検出アルゴリズムに関する1つのリソースが非常に有用であることが判明しました。 優れた視覚的フラッシュの例。