ただの日記の1ページ
最近(2018年らへん)のゲームだと、Tipsみたいなのを表示して
長ったるいロード時間を誤魔化してますね(?
まぁ、3Dモデルって重いからね・・・
音楽みたいにストリームっぽく荒いポリゴンから順に細かくしていけばいいのにね
主題:NowLoading...みたいな画面を作りたい
長いロードをする度に、画面が固まったり操作ができなくなるのは
ゲームをやってる人も作ってる人も不満たらたらである
さて、次のようにnewやら3Dモデルの読み込みなどの
計算量とかが重い処理を実装すると普通に画面が固まる
while(true){ ~なんか 更新(); 描画(); 重い処理(); //<-この処理が終わるまで画面は動かない ~次 }
重い処理が終わらない限り次には進まない、
つまり描画内容は更新されないため、固まるのは当然である
今やりたいことは、重い処理をしつつNowLoadingみたいな文字を表示したり
tipsを見せるという処理である
これをスレッド(thread)を使って実装する
スレッドがどういうものかはそこら辺に沢山資料があるので省略する
方法1
static ロード状態=ロード始まってない; static 画面=ゲーム画面 スレッド t = null; while(true){ 画面.ゲーム更新など; 画面.ゲーム描画など; if(ロード開始命令がきた){ switch(ロード状態){ case ロード始まってない: t = new スレッド(重い処理()); t.start(); ロード状態=ロード始まった; 画面=ロード画面; break; case ロード始まった: break; case ロード終わった: t.join(); 画面=次のゲーム画面; break; } } } //--------------------------- void 重い処理(){ for(100回){ Class a[i] = new Class();} すごい重い(); ロード状態=ロード終わった; }
上記はロードしたいタイミングで重い処理を副スレッドに投げる方法である
現在の画面を更新描画しつつ、裏でロードするにはこれが最適かもしれない
RPGでNPCが「ちょっと待ってて」とか言って準備してる様子を描画してる最中に
この方法でロードとかすれば自然にロードできるかもしれない
方法2
bool ロード終わった=false; void main(){ スレッド t = new スレッド(ロード画面描画更新処理); t.start(); 重い処理(); ロード終わった=true; t.join(); } void ロード画面描画更新処理(){ while(!ロード終わった){ ロード画面描画; } }
これはロード画面を副スレッドに投げる方法である
方法1に比べてシンプルに見えるが、主スレッドが止まっているということを
留意する必要がある
実装方法によっては、コントローラーの状態が更新されない、描画が更新されない
などといった障害も生じるのではないかと考えられる
方法3
Class ロードスタックキュー{ std::vector<Object> ロード待ち; void ロードして(Object); void アンロードして(Object); Object これ欲しい(id); void ロード処理関数(); } スレッド t = new スレッド(ロードスタックキュークラス.ロード処理関数); t.start(); ゲームメインループ; //メインループ内で上記クラスに対してオブジェクトを使う前にロードをお願いしたりする //欲しくなったらキューからもらう t終了処理
この方法は、3Dゲーム系のロード処理方法ではないかという推測である
やりたいことは、次に使うであろうものを予測して
リソースやメモリ使用予約が入り次第メモリにスタックしていく
ロードが完了したらメインループ内で使える許可を出す
当然、いらなくなったらすぐに捨てる
おわりに
自分の中では方法1が安定策である
なお、当然ではあるが上記のまま実装しても面倒なことが起きるだけである
よく考えて、今の状況にあった選択肢を取る必要がある
nsan.hatenablog.com
上記記事においてloadを分けた理由は
load関数にスレッドを利用し、重い処理を分別するため、である
メンバ関数にスレッドを適用するには、多少まどろっこしいことをやる必要があるが・・・
また、非同期処理なため、mutexやsynchronizedなどを使うと
nullとか発生しにくくなるのではないかと思う
編集日
2019/04/12 ちょっと編集