130 likes | 310 Views
プロジェクト演習 Ⅳ インタラクティブゲーム 制作 プログラミング 4. 2011/11/8 マルチスレッド と 真・ストリーミング再生. 今日の内容. マルチスレッドプログラム 1 つの プログラム内で 2 カ所 以上 「 同時に実行」できる程度の能力 重い処理で画面や入力受付を止めたく ない 時 や、設計次第では高速化にも寄与 音楽再生の改善 ロード中に音が止まらなくなるよ! やったね!. マルチスレッドとは. 1 つの プログラムの中で複数の関数が 同時に ( 並列して ) 動かせる技術のこと
E N D
プロジェクト演習Ⅳインタラクティブゲーム制作プログラミング4プロジェクト演習Ⅳインタラクティブゲーム制作プログラミング4 2011/11/8 マルチスレッドと 真・ストリーミング再生
今日の内容 • マルチスレッドプログラム • 1つのプログラム内で2カ所以上「同時に実行」できる程度の能力 • 重い処理で画面や入力受付を止めたくない時や、設計次第では高速化にも寄与 • 音楽再生の改善 • ロード中に音が止まらなくなるよ! • やったね!
マルチスレッドとは • 1つのプログラムの中で複数の関数が同時に(並列して)動かせる技術のこと • 今まで皆さんが書いていたプログラムは「実行している箇所」は1カ所だけだった • シングルスレッド • マルチスレッドの導入により、メインループを回しつつ、他のスレッドで重い処理をさせることができるようになる
例えば // いつものメインループ while(true) { // 画面描画や入力受付 update(); // BGM再生 play(); // この中で重たいロードが発生 loadHeavy(); // ここで数秒間処理が止まると当然update()側に処理が渡らない! // 当然play()も呼ばれないので曲再生が止まる }
何故音楽が止まるのか? • OpenALでは「キューバッファ」と呼ばれる方式によってストリーミング再生している • play()の中で更新をしているので、長時間play()が呼ばれないとバッファを使い切って止まる 数秒分の 波形データ 再生時間 数秒分の 波形データ
こんなことが出来るようになる! // こっちはメインループ while(true) { // 画面描画や入力受付 update(); // BGM再生 if(再生開始?) playStart(); // 後は投げっぱなしでおk // この中で重たいロードが発生 if(読み込む?) loadStart(); if(読み込み中?) { 今何%くらいかを取得; 画面に表示して待たせる; } } // 別スレッドを作成 void playMain() { while(true) { play(); if(ほげほげ)break; } } // それとは別に作ってもいい void loadMain() { loadHeavy(); }
メリット・デメリット • 重い処理をメインループから外して処理の高速化、効率化が見込める • バックグラウンド読み込み、ローディングの進捗表示、ネットワーク処理、BGMのストリーミング再生など夢は広がるばかり • データのアクセスに制約が付く • バグった時の対応難度が跳ね上がる • 苦労して別スレッドにしてもその割にはリターンが少ない、あるいは悪化することもある
今回のサンプルはWindows限定コード • boostというライブラリを使うと、機種依存せずにスレッド処理が可能 • ただし、ライブラリのサイズが膨大かつ、コンパイル速度が低下するなどの影響も • boostはおすすめしたいのですが、今回は学習の利便性をとってWindowsネイティブの実装にしました
使い方 • Javaライクにしてあります • ThreadBaseクラスを継承してMyThreadクラスを作る • main()をオーバーライドして、別スレッドで行う処理を実装する • MyThreadオブジェクトを作り、start()をコール • main()を直接コールしてはだめ • 止める時はend()をコール
別スレッドで操作しているデータの参照 • 必ず受け渡し用のメンバ関数を作る • そのメンバ関数内でlock()~unlock()を忘れずにコールする • lock()して • 返値用の変数にデータをコピーしたりして • unlock()する • これを怠ると、データの読み書きがかちあってSUGOIことになる
コンストラクタとデストラクタを使ったテクニックコンストラクタとデストラクタを使ったテクニック • lock()~unlock()でわざわざ関数全体を囲うのが面倒 • あるスコープに入った時と出た時に行う処理を楽に書きたい • じゃあ→こんなんどうでしょう? class fkut_CriticalSection { public: fkut_CriticalSection(void) { fkut_ThreadBase::lock(); }; ~fkut_CriticalSection() { fkut_ThreadBase::unlock(); }; }; // このクラスの変数をスコープ内に宣言するだけでおkになる // 型名が長いのが嫌ならtypedefを typedeffkut_CriticalSectionfkut_CS
Segaの偉い人曰く… • プログラムは基本シングルスレッドで書くモノである • マルチスレッドプログラムで起きる挙動は人間の理解を超えている • 本当にどうしても必要な場合に、最低限の利用に留めることをおすすめする • だ、そうです
アドバイス • よく分からなかったら使わなくてもいい • 無理して使ってもドツボにはまるだけなので • これが出来ないと作れない「遊び」というのは存在しない • スマートにはなるけど • 使いどころは • BGM再生用に1本 • データロード用に1本 • データロードしつつゲーム本体を動かすのは厳禁 • 進捗表示や待機アニメ程度にする • ぐらいにしておこう