*目次 [#y067346b] #contents * gamelibとは何か [#s556f7a3] この授業のために開発された簡易的なゲームライブラリとアプリケーションのプログラムです。 ~ ライブラリとアプリケーションを分けることで、 ~ 統一されたシステムをベースにした柔軟なゲーム開発が可能になります(はず…)。 ~ ゲームシステム(ヒットポイント管理、ゲームクリア・オーバー条件、ステージマップ管理、敵キャラ行動プログラム)等は ~ ライブラリに任せ、具体的なステージ構成はアプリケーション側にプログラムしましょう。 ~ 難易度の異なる複数のステージを作ったりする際には、ライブラリで構築した関数や構造体を使い、 ~ 使用する関数やパラメータ等を変更する事で実現します。 ~ ライブラリに任せる部分とアプリケーションに任せる部分を切り分けてプログラムする設計が重要です。 ~ 最初は提供されているライブラリやアプリケーションのプログラムを修正する事で課題を達成し、 ~ 次にライブラリを本格的に変更して独自のゲームシステムを構築しましょう。 * gamelibのディレクトリ構造 [#ic9a84e2] ** application [#e02faab1] 具体的なゲームアプリケーションはここに作成します。 ~ shooting_gameディレクトリにサンプルとなる以下の基本ファイルが保存されています。 ~ main.c shooting_game.c shooting_game.h Makefile exec.sh ** include [#w7fb3b1b] - include/gamelib/ gamelibのヘッダファイルのディレクトリです。 以下のファイルが含まれています。 gamelib.h game_map.h self_agent.h enemy_agent.h interaction.h - include/ode/ Open Dynamics Engine(ODE)のヘッダファイルのディレクトリです。 - include/drawstuff/ ODEに付属しているODEを簡易に実行し、表示する環境のヘッダファイルのディレクトリです。 ~ ** src [#zf884f51] ソースファイルのディレクトリです。 - src/ode-0.12/ あるいは src/ode-0.11.1/ ODEのソースコードの圧縮ファイルを解凍すると現れるディレクトリです。 ~ 物理シミュレーション、衝突判定、表示や操作のためのライブラリ等がおさめられています。 ~ MAKEスクリプトを実行する事で自動的にコンパイルとインストールが行われます。 - src/gamelib/ 本ライブラリの本体のソースコードが保存されています。 ~ 以下のファイルが存在します。 gamelib.c game_map.c self_agent.c enemy_agent.c interaction.c Makefile * プログラム説明 [#afb835ca] ** アプリーケーションソースコード [#ode9390b] - Makefile makeを行う際の情報を書き込む。 - メイン関数を記述するファイル application/shooting_game/main.c --drawstuffへの関数等の登録 dsFunctions fn; // drawstuffに登録する情報を格納する構造体を宣言 fn.version = DS_VERSION; // drawstuffのバージョンを登録する。 fn.start = &start; //物理シミュレーション開始時に実行される関数(start)を登録する。 fn.step = &simLoop;//各ステップ毎に実行される関数(simLoop)を登録する。 fn.command = &command;//キーボードのキーが押された時に実行される関数(command)を登録する。 fn.stop = &stop;//シミュレーション終了時に実行される関数(stop)を登録する。 fn.path_to_textures = "./textures/";//テクスチャの画像ファイルを保存しているディレクトリの場所(./textures)を登録する。 -- ODEによる物理シミュレーションの実行 dInitODE2(0); // ODEの初期化(詳細は参考サイトか参考文献で) dsSimulationLoop (argc,argv,600,500,&fn); //物理シミュレーションと表示を開始(画面サイズ600x500) dCloseODE(); // ODEの修了処理 画面サイズを小さくすると処理が軽くなりますので、 ~ 演習室PCでは遅くなる場合に試してみて下さい。 - ステージ定義等を記述するファイル application/shooting_game/shooting_game.c application/shooting_game/shooting_game.h ODEを簡易に実行するdrawstuffに登録するコールバック関数を定義します。 ~ 定義された関数はmain関数の中で登録している。 -- 物理シミュレーション開始時に一度だけ呼び出される関数 void start(); 以下、内容解説 initialize_gamelib(); // gamelibの初期化。必ず最初に行う。 dAllocateODEDataForThread(dAllocateMaskAll); // ODEの初期化 float default_view_position[3] = {0.0f,0.0f,3.0f}; float default_view_direction[3] = {0.0f,30.0f,0.0f}; dsSetViewpoint (default_view_position, default_view_direction); // マップの作成 // マップの座標(x,y) x:西→東(0→2)、y:南→北(0→2) と設定 make_dead_end_walls(0,0, FROM_NORTH ); make_t_junction_walls(0,1, FROM_EAST ); make_dead_end_walls(0,2, FROM_SOUTH ); make_turn_walls(1,0, FROM_EAST_TO_NORTH ); make_cross_road_walls(1,1); make_turn_walls(1,2, FROM_SOUTH_TO_EAST ); make_turn_walls(2,0, FROM_NORTH_TO_WEST ); make_turn_walls(2,1,FROM_WEST_TO_SOUTH ); make_dead_end_walls(2,2, FROM_WEST ); self_agent = make_self_agent( 0,0, 100,M_PI/2 ); /*セルフエージェントを初期化。構造体のデータはself_agent.hで定義されている。 */ set_first_person_view_point( self_agent ); /* ファーストパーソンビューに切り替える */ enemy_agent0 = make_enemy_agent(0,1, 10); // enemy_agent 0番の定義(ポインタが代入される) enemy_agent1 = make_enemy_agent(0,2, 10); // enemy_agent 1番の定義(ポインタが代入される) -- シミュレーションステップ毎に呼び出される関数 void simLoop( int pause ); 以下はアップデート内容 update_gamelib(); /* gamelib全体のアップデート。主にODEの処理。中身はgamelib.cに記述*/ if( NULL != self_agent ){ /* self_agentポインタがNULLでない事を確認*/ update_self_agent( self_agent ); /*自機のアップデート。中身はself_agent.cに記述*/ /* 視点切り替え処理。view_point_switchのラベルに従って視点切り替え */ if( view_point_switch == BEHIND_VIEW ){ /* Behind mode */ set_behind_view_point( self_agent ); visualize_self_agent( self_agent ); }else{ /* First person view mode*/ set_first_person_view_point( self_agent ); } visualize_self_agent_bullets( self_agent ); } if( enemy_agent0 != NULL && 0 == enemy_agent0->hit_point ){ /* もしenemy_agent0のポインタがNULLでなく、enemy_agent0のヒットポイントが0の時 */ destroy_enemy_agent( enemy_agent0 ); // enemy_agent0のメモリを解放 enemy_agent0 = NULL; // ポインタにNULLを代入 } // ゲーム終了処理 if( NULL == enemy_agent0 && NULL == enemy_agent1 ){ printf("Congraturations! Game Clear!\n"); stop(); exit(0); } if( get_self_agent_hit_point() <= 0 ){ printf("You lose! Game Over!\n"); stop(); exit(0); } -- キーボードからの入力を受け付ける関数 void command( int c ); -- 終了処理を行う関数 void stop(); **ライブラリソースコード [#rb97bf25] - ゲームシステム全体を管理する関数を記述するファイル include/gamelib/gamelib.h src/gamelib/gamelib.c -- プログラム中に必要な資産を管理する仕組み ~ 一旦全てのデータはGameObject構造体に格納されて管理される。 struct GameObject{ enum ObjectType object_type; void* data; }; enum ObjectTypeはGameObjectに格納されたデータがどのような種類かを示している。 ~ この値に基づいて "void* data" をキャストすることが可能になる。 enum ObjectType{ GROUND, WALL, SELF_AGENT, SELF_AGENT_BULLET, ENEMY_AGENT, }; voidポインタはどのようなポインタでも格納できる万能なポインタである。 ~ ただし、ポインタで指し示している先がどのようなデータか把握していないとデータを読む事ができない。 ~ ~ キャストとは、ある型の変数を別の方の変数として使用するC言語の仕組みである。 ~ 以下のように用いる。 void* void_p; double* double_p double_p = (double*) void_p; int i = 10; double d; d = (double)i; -- gamelib.cに記述された関数 --- プログラムの最初にシステム全体を初期化するための関数 void initialize_gamelib(); --- プログラムの最後にシステム全体の最終処理をするための関数 void finalize_gamelib(); --- 毎回のアップデートをする関数 void update_gamelib(); 主にODEの物理シミュレーションのアップデートを行う。 dSpaceCollide (space,0,&nearCallback); /* ODEの衝突計算 */ dWorldStep (world,SIMULATION_TIME_STEP); /* ODEの力学シミュレーション1ステップ */ dJointGroupEmpty (contactgroup); /* ODEの衝突情報の開放 */ --- ODEで2つの物体が近づいて、衝突しそうになった時(衝突している時)に呼び出されるコールバック関数 void nearCallback( void *data, dGeomID geom1, dGeomID geom2 ); Self Agent と Enemy Agentの間等の衝突処理を行う。 ~ - 自機エージェント構造体定義と関数を記述するファイル include/gamelib/self_agent.h src/gamelib/self_agent.c ~ --自機の情報を格納する構造体 struct SelfAgentData{ dBodyID body_ID; /* ODEの物理計算に必要なID(実際はポインタ) */ dGeomID geom_ID; /* ODEの衝突計算に必要なID(実際はポインタ)*/ int hit_point; /* 自機のヒットポイント*/ dReal direction_angle; /* 自機の方向。東が0ラジアン */ dVector3 pulling_force; /* 自機を移動させるための力をセットする変数 */ }; ---direction_angleの角度と向き 東: 0 [rad] ( 0[deg]) 北: M_PI/2 [rad] ( 90[deg]) 西: M_PI [rad] (180[deg]) 南: -M_PI/2 [rad] (-90[deg]) --自機から発射される弾丸の情報を管理する構造体。 struct SelfAgentBulletData{ dBodyID body_ID; dGeomID geom_ID; int countdown_timer; /* 発射されると初速度を与え、タイマーに値をセットする。 ステップ毎にタイマーをカウントダウンして、0になったら位置をリセットする。 値が0の場合、物理シミュレーションと衝突判定を行わない。 */ }; --自機のデータを生成してポインタを返す関数 struct SelfAgentData* make_self_agent( int map_x, int map_y, int initial_hit_point, dReal initial_direction_angle); --自機のポインタを安全に消去する関数 void destroy_self_agent(struct SelfAgentData* self_agent_data); --自機をアップデートする関数 void update_self_agent(struct SelfAgentData* self_agent_data); -- 自機の背後から視点に切り替える関数 void set_behind_view_point(struct SelfAgentData* self_agent_data); --自機の一人称視点に切り替える関数 void set_first_person_view_point(struct SelfAgentData* self_agent_data); --自機を操作するコマンドを入力して、必要な処理を行う関数 void command_self_agent( int command, struct SelfAgentData* self_agent_data ); --自機から弾丸を発射する関数 void self_agent_shoot_gun(struct SelfAgentData* self_agent_data); --自機を可視化する関数 void visualize_self_agent(struct SelfAgentData* self_agent_data); --自機の弾丸を可視化する関数 void visualize_self_agent_bullets(struct SelfAgentData* self_agent_data); --自機の位置を返す関数 const dReal* get_self_agent_position(struct SelfAgentData* self_agent_data); --自機のヒットポイントを返す関数 int get_self_agent_hit_point(struct SelfAgentData* self_agent_data); 以下の値を直接参照する事もできるが、 ヒットポイントの処理について何らかの変更を行いたい時に関数化しておくと便利。 self_agent_data->hit_point; --自機のヒットポイントを増減する関数 void change_self_agent_hit_point( struct SelfAgentData* self_agent_data, int hit_point ); 正の値で増加し、負の値で減少する。 以下のように直接操作する事もできるが、 ヒットポイントの処理について何らかの変更を行いたい時に関数化しておくと便利。 self_agent_data->hit_point --; - 敵エージェント構造体定義と関数を記述するファイル include/gamelib/enemy_agent.h src/gamelib/enemy_agent.c --敵機の情報を格納する構造体 struct EnemyAgentData{ int hit_point; dBodyID body_ID; dGeomID geom_ID; }; --敵機のデータを生成してポインタを返す関数 struct EnemyAgentData* make_enemy_agent( int map_x, int map_y, int initial_hit_point ); --敵機を可視化する関数 void visualize_enemy_agent( struct EnemyAgentData* enemy_agent_data ); --敵機をアップデートする関数 void update_enemy_agent( struct EnemyAgentData* enemy_agent_data ); --敵機を安全に消去する関数 void destroy_enemy_agent( struct EnemyAgentData* enemy_agent_data ); --敵機のヒットポイントを返す関数 int get_enemy_agent_hit_point( struct EnemyAgentData* enemy_agent_data ); 以下の値を直接参照する事もできるが、 ヒットポイントの処理について何らかの変更を行いたい時に関数化しておくと便利。 enemy_agent_data->hit_point; --敵機のヒットポイントを増減する関数 void change_enemy_agent_hit_point( struct EnemyAgentData* enemy_agent_data, int hit_point ); 以下のように直接操作する事もできるが、 ヒットポイントの処理について何らかの変更を行いたい時に関数化しておくと便利。 self_agent_data->hit_point --; - ゲームマップの構造体定義と関数を記述するファイル include/gamelib/game_map.h src/gamelib/game_map.c --マップの情報を格納する構造体 ---壁の情報を保存 struct WallData{ dGeomID geom_ID; /* ODEの衝突計算に使うID*/ dBodyID body_ID; /* ODEの物理計算に使うID。実際には壁は移動しないがbodyIDが無いとdGeomIDを設定できないため */ dJointID fixed_joint_ID; /*空間に壁を固定するためのジョイント。固定するだけなのでfixedジョイントを用いる*/ }; struct MapData{ dReal center_position[2]; //[0]->x, [1]->y int wall_size; struct GameObject wall_object[MAP_ONE_SQUIRE_MAX_WALL_SIZE]; /*現在、"WALL"しか代入されないためこの情報は使用されていないが、必要に応じて意味のある情報に書き替えても良い*/ struct WallData wall_data[MAP_ONE_SQUIRE_MAX_WALL_SIZE]; /*voidのポインタとして管理する事で、様々なオブジェクトを格納してマップ要素を構成する事は可能。 そのオブジェクトが何かはwall_objectで指示すれば良い*/ }; --マップの初期化をする関数 void initialize_map(); --マップの情報を安全に消去する関数 void destroy_map(); --直線的な通路を生成する関数 int make_strait_walls( int map_x, int map_y, enum DIRECTION direction ); --曲がる通路を生成する関数 int make_turn_walls( int map_x, int map_y, enum DIRECTION direction ); --T字路を生成する関数 int make_t_junction_walls( int map_x, int map_y, enum DIRECTION direction ); --交差点を生成する関数 int make_cross_road_walls( int map_x, int map_y); --行き止まりを生成する関数 int make_dead_end_walls( int map_x, int map_y, enum DIRECTION direction ); --game_map.cの内部で使用する壁を生成する関数 int make_wall( dReal wall_center_position_x, dReal wall_center_position_y, dReal wall_direction_x, dReal wall_direction_y, dReal wall_length, struct GameObject* wall_object ); --game_map.cの内部で使用する2次元ベクトルを回転する関数 void rotate_90deg_clockwise( dReal* vec2 ); void rotate_90deg_anticlockwise( dReal* vec2 ); void rotate_180deg( dReal* vec2 ); --マップを可視化する関数 void visualize_map(); - インタラクションのための関数を記述するファイル include/gamelib/interaction.h src/gamelib/interaction.c 自機と敵、弾と敵がぶつかった時の処理プログラム。 --自機の弾丸と敵機が接触したときの処理関数 void self_agent_bullet_hits_to_enemy_agent( struct EnemyAgentData* enemy_agent_data ); --自機と敵機が接触したときの処理関数 void contact_self_agent_and_enemy_agent( struct SelfAgentData* self_agent_data, struct EnemyAgentData* enemy_agent_data );