●はじめに●
本プロジェクトはVisualStudio2017が必要です。
無料で手に入りますので持っていない人はこの機にどうぞ。
MicrosoftのVisualStudio2017のダウンロードページから「Community2017」をダウンロードします。
インストールしている最中に以下のようなウィンドウが出てきますので
「C++によるモバイル開発」にチェックを入れてインストールします。
インストールが終わったら画面を閉じます。
●オープンソースプロジェクトのダウンロード●
こちらからダウンロードしてください。Githubはこちら
解凍して出てきたDanmakuBench.slnを開き、Ctrl+F5で実行してコンパイルが通って実行出来ればOKです。
((⇒出来なかったら掲示板で報告してください。
この時Android端末を繋いでいないとエミュレータが立ち上がります。
Androidを開発できる状態でPCに接続しておけば
このように表示されてコンパイル後自動的にAndroidにインストールされて実行されます。
※Androidを開発できる状態にしていない人
Androidは開発者モードにしておくことと、PCにAndroidのUSBドライバーが入っている必要があります。
やり方が分からない人はこの辺を参考にして開発者モードにし、USBデバッグをONにしてください。
PC側は「スマホの型番 USBドライバ」で検索するとダウンロード情報出てきます。
Android Studioをインストールしてしまってもいいでしょう。
●ソースコードを読む前に●
GUIアプリプログラミングとゲームプログラミングの大きな違いは前者が受動駆動なのに対して後者は能動駆動であることです。
・受動駆動
onClick等のイベント発火をトリガーに何か処理し、イベントが発火しないと何もしない
・能動駆動
常に60FPSでループ処理をして毎フレーム全消し全書き。
イベントは何が発火したかを自分から見に行き、その結果に応じて振舞う。
>> ゲームプログラミングの基本形 <<
update();
draw();
の二つに処理を分けます。
draw()は描画のみを行い、それ以外の処理は全てupdate()で行います。
drawに乱数を使用するコードを入れてはならなりません。
こうすることで一時停止などの処理を実現する時、updateをコールせずdrawだけをコールすることで実現でき、
リプレイの早回し処理をしたい時はdrawをスキップしてupdateを繰り返しやればその時の状況がシミュレート可能になります。
スキップしたdrawに一つでも乱数メソッドがあると計算が狂うので入れてはいけません。
drawに必要なデータは全てupdateで計算し、drawはお膳立てされたことをただ純粋に描画するのみにします。
●ソースファイルの見方●
main.cppから始まります。
⇒SysytemMainで管理されていることが分かります。
⇒Looperでグルグル回っていることが分かります。
⇒Looperで最初に生成されているシーンはGameSceneであることが分かります。
⇒Bulletを大量に生成して計算していることが分かります。
・・という順に追って行けば大体理解できるでしょう。。
●シーン管理の仕方●
Looper.cppを見ると各画面(Scene)はスタックにより管理されています。
これは画面切り替え(Scene管理)をしやすくするための工夫です。
このように新しい画面が出来てはスタックにpushする仕組みで、update,drawするのは一番上の画面だけ。
こうすることで、ゲーム画面が終わって一つ前の画面を前の状態で出したいと言う時も簡単です。
本プロジェクトではシーンが重なることはありませんが、シーン管理の基本形として参考にしてもらえたらと思います。
●本プロジェクトコードの見どころ●
・GameSceneはFpsのインスタンスを参照したい。この時LooperはFpsのインスタンスそのもののを渡してはいけない。
(IFpsGettable*)&_fpsにキャストしてからGameSceneに渡すこと。
GameSceneは値の取得しかできない(余計な権限を他モジュールに渡さない)Looper.cpp#43行目参照
・全てのクラスはTaskクラスを継承する。(引数をTask*にすることで何でも渡せるようになる)
→list<Task*>のリストに全てのオブジェクトを突っ込んで全for->draw()すれば型に関係なく全描画出来る等便利。
・1クラス200行を超えない
・一行でも見て何の処理をしているのか意味が分かりにくい物はメソッド化して分かりやすい名前を付ける
・ゲーム全体で保持したいシーン間で共有するデータはonSceneChangedの引数のScenePrmBaseに持たせる(安易にシングルトンにしない)
・英語のお話を語るようにコーディングする。(計算式のオンパレードのようなメソッドはダメ)
・以下の順番でコードを書く
コンストラクタ();
デストラクタ();
update();
draw();
privateメソッド();
※Fpsクラスは学生時代から使いまわしているクラスなので見ないで(汚い)
●余計な権限を渡すことを避けるインターフェイス●
LooperとFpsとSceneのクラス関係は以下のようになっています。
GameSceneクラスがFpsを知りたいのでFpsクラスのgetFps()をコールしたい状況があります。
この時LooperはFpsのインスタンスそのものを渡すことでこれを実現する方法もありますが、
これではGameSceneクラスさんがFpsクラスのpublicな操作全てが出来てしまうので余計な権限を渡してしまい危険です。
GameSceneさんはFpsクラスのgetFpsだけしか呼べないように設計することが大事です。
そんな時は上のクラス図のようにIFpsGettableインターフェイスクラスを利用します。
純粋仮装関数でgetFpsが定義されているインターフェイスを継承してFpsが実装してありますので、
LooperはGameSceneにこのインターフェイスクラスのポインタを渡すことが出来ます。
こうすることでGameSceneはgetFpsしかコールできないので安全で、必要なことも実現できています。
シーンの切り替え方法も同じ要領で実装してあります。
●C++11、C++14非対応のため古いコーディングをしてある部分●
・iteratorを使ったループ文はC++11式for文を本来使う
・ポインタを保持するListはshared_ptrを本来使う
・overrideメソッドはoverride修飾子を本来使う
・ヌルポとの比較はnullptrを本来使う
更新中・・・
●お願い●
管理人はまだまだ若輩者故「このような設計の方が適切だ」等のご意見があれば掲示板やメール等で情報頂ければ幸いです。
オープンソースで学ぶ設計手法トップページへ
C言語何でも質問サイトトップページへ
何でも質問掲示板へ
- Remical Soft -