忍者ブログ

神戸電子専門学校ゲームソフト学科の生徒が運営するGESのブログです。

   

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

シーン制御

ども、こんばんわ~

さっき最後まで記事を書いたのに、ブラウザが落ちて記事が消えてしまったZAKIです

涙をこらえながら書き直そうと思います。


さてええぇぇぇぇ今回のおおおおぉぉぉぉテーーーーーーマはああああぁぁぁぁあぁぁぁぁ

シーン制御です。

すんません。マジで悔しいんですorz

さて、シーンとは何でしょうか。

シーンとは、タイトル、キャラセレクト、ゲーム、ゲームオーバー、などなど、ゲームの場面を指します

皆さんはどんな感じで管理していますか?

while(1) {
  if(sceneNo == 0)  {
     Title();
  } else if(sceneNo == 1) {
    CharaSelect();
  } else if(sceneNo == 2) {
    Game();
  } else {
    break;
  }
}

こんな感じですか?

出来てるといえば出来ていますが、Game()関数を実行するとき毎回3回if判定が起こりますね

メインループだけに無駄な処理は避けたいところです

それを防ぐにはいろいろと使い回しの効く憎い奴を使います

そう、ポインタです。

しかも、関数ポインタです。

ああ引かないで待ってがんばるから僕がんばって説明するからぁ

続けて読んでもらえる人。ありがとう。

読んでくれない人、コノヤロウ。

関数ポインタとは関数のアドレスを入れるポインタ変数のことです

関数ポインタの定義はサンプルではmain.hの38行目でやっていますね。

typedef  void *(*LPSCENE)();

この行がそうです

typedefとは新しい型の定義を行います。

この場合はLPSCENEという自作の型を作っています。

説明すると結構長くなるので、詳しくは教科書、ネットで見てみてください。

あ、C言語の教科書あれは必携ですよ

みんな邪険にしますが、あれ見ればほっとんどのことが書いてあるので、実習中は持ってくことをオススメしますよ

あ、すいません。話を戻しますね

残りの部分が関数ポインタの宣言です

この場合は「void *型を返す引数なし」の関数のポインタです。

(*LPSCENE)が型の名前です

typedefが無ければこの部分は、変数名になります

()内の*はポインタであることをさします。

詳しくは教科書およびネットで(ry

関数ポインタ変数の宣言はmain.cppの18,19行目で行っています。

これをシーンの制御に使うので、これをシーン変数と呼びましょう

それじゃ、シーン制御の全体の流れを説明します

  1. シーン変数に最初に処理をする関数のアドレスを入れる(main.cpp 95行目)
  2. シーン変数の中の関数を実行する(main.cpp 112行目)
  3. 2で実行した関数の戻り値をシーン変数に代入しなおす(main.cpp 112行目)
  4. 中身がNULLならループを終了(main.cpp 113行目)
  5. 関数のアドレスなら2へ戻る

と、こんな具合になります

main.cppの112行目pNowLoop()の前に(LPSCENE)としているのはキャストと言う処理です

この場合の意味はpNowLoop()の戻り値(void *型)をLPSCENE型にしろってことです。

戻り値を代入する変数と戻り値の型が全然違うのであわせてやるわけです

ちなみに、void *型は何でも型です

どの型のアドレスでも入れれます

それを任意の型へキャスト変換して使うというのは、ポインタ系の処理ではよくあることなのですが、

型が自由に変えれてしまうため、あまり好ましくはありません

できるなら、使わないほうがいいのですが、いい方法が見つからなかったので使っています

良い子のみんなはいい方法を探してみてね

あとは、各シーン関連の関数で次に処理をさせる関数のアドレスを返してやります

関数のアドレスをさす場合は関数名のみ(()を付けない)とするとアドレス参照できます。

変数でよくやる「&a」と同じような意味です

さて、この方式の場合の制約は、

全てのシーン関連関数はシーン変数の型定義に従わなければならない。(この場合はvoid *を返す引数なし関数)

関数の戻り値は次の処理をする関数のアドレスでなければならない。

です。

このルールを守っておけばこの方法を使うことが出来ます

ちなみに、サンプルではシーン変数を二つ使っています

pNowReleaseは常に初期化に対応した開放関数を保持しておきます

こうすると、初期化処理で失敗して関数でNULLを返した場合、

メイン関数の最後で任意の関数の開放を呼ぶことが出来ます。

そうして、開放忘れを防いでるわけですね

ただ、この場合、正常にループを抜けた時は2回開放関数が呼ばれることになります

それ用の処理を開放関数にしておかないと、同じ変数を2回開放することになっちゃいますよ

追記:dglib始めました。

いきなりVSでって何を考えてるんだ僕は…orz

dglib版をアップしておきました

VS版とは少し違いますが、やり方は一緒です

dglib版の分割コンパイルバージョンでは、各シーンでアニメーションの基本ぽいことをやっています

悩んでいる人は一見の価値ありかも



ふぅ、一応、シーン制御の項目は以上です

前回以上にめちゃ長いですね

分けて丁寧に説明したほうがよかったんですが、

なにぶん、時間が……orz

質問はいつもどおり、コメント、リアル(実習室など)で受け付けています

わからなかったら突撃してみてください

それでは、今回はこの辺で。

あでゅーノシ

 

拍手[0回]

PR

COMMENT

NAME
TITLE
MAIL(非公開)
URL
EMOJI
Vodafone絵文字 i-mode絵文字 Ezweb絵文字
COMMENT
PASS(コメント編集に必須です)
SECRET
管理人のみ閲覧できます

古い記事にコメしてみる

  • by 1年ボーイ
  • 2012/04/26(Thu)02:28
  • Edit
始めまして1年ボーイです
突然ですが俺の使ってるシーン変更を見てください
1.scene_baseというクラスつくる
2.それを継承してtitle_scene,game_sceneなどを作る
3.scene_base型のポインタ配列をつくる(*scene[])
4.そのポインタ配列に2で作ったクラスのオブジェクトのアドレスを入れる
5.メインループ内でscene->act(int)回して戻り値で切り替える

自分としてはこの方法が大好きです
戻り値がintになるのでゲームシーンからタイトルシーンなどが簡単にできるのが長所だと思います
この方法をどう思いますか?厳しい評価をして欲しいです。

No Title

  • by Yana#P
  • 2012/04/26(Thu)18:16
  • Edit
>1年ボーイさん
生成したインスタンスは終了時にまとめて解放するんだと思うけど、それだとメモリの使い方に無駄ができてしまうなぁ・・・
PCならまだいいとしても、家庭用ゲーム機だと使えるメモリは限られてくるから無駄はなくしたい
だからこの記事に書かれてるような方法にも慣れておくといいと思う

ちなみにシーンマネージャってのを作るともっと楽にできるよ

No Title

  • by リーダ和田
  • 2012/04/26(Thu)21:15
  • Edit
1年ボーイ様
コメントありがとうございます。
継承を利用したシーンの制御ですね。
私も知ったときは感動を覚えました。

予めnewで確保したシーンをポインタ配列に入れているのは、タイトルのシーンの間に各シーンを確保していて少し違和感がある。

一年生において継承を利用してのシーン制御している時点で
かなりのアドバンテージです。
もしラボ(北野館3階ゲームラボ1)に良かったら来られませんか?
是非ソースなどを見たいです!

yana#p様
シーンに使っていたインスタンスを終了時にまとめて開放するのは、
普通ではないでしょうか?
それとも、溜め込んで再利用して使うのでしょうか?

そのへんの詳しいコメントもあったら嬉しかったです。

No Title

  • by Yana#P
  • 2012/04/27(Fri)00:37
  • Edit
>リーダ和田さん
そうかなぁ・・・
俺はマネージャの中で
Update()←シーン更新関数
の戻り値を次のシーンのインスタンスへのポインタ(シーン遷移がない場合はthis)にして、返ってきたポインタが現在のシーンのポインタと一致しない場合は現在のシーンの解放処理とインスタンス解放を行った後でその戻り値を次のシーンとしてポインタ変数に代入してるけど・・・

もしかして生成したインスタンス=各シーンで使われている画像とかの変数って解釈させちゃったかな?
そうなら申し訳ない


時折俺「何言ってんだ?」って事言い出すからなぁ・・・w
後で考えて「おかしいぞw」ってのはよくある話
直さないとww

さすが先輩パネー

  • by 1年ボーイ
  • 2012/04/27(Fri)03:04
  • Edit
メモリとか全然考えたことなかった
問題点からして、各シーンのインスタンス生成をするタイミングを変えたらいいんでしょうか?
ポインタ配列をポインタ変数に変更して、最初に使うシーンをインスタンス化して回す
戻り値を見て、例えば999以外だったらシーン変更で、前のシーンを解放し戻り値に対応したシーンのインスタンス生成して上書きみたいな?
解放とかにわか知識なんですいません
>リーダ和田
人にみせるのは恥ずかしいので、もう少し勉強してからにします。
>Yana#P
シーンマネージャってのを勉強してみます。

いまだにゲームメインループのFPSを一定にする方法がどれがいいのか分かりません。ググっても古い記事ばかりでガッカリです。先輩たちの方法が知りたいです

No Title

  • by リーダ和田
  • 2012/04/27(Fri)14:49
  • Edit
>>>1年ボーイ
FPS制御は大西先生の2Dの教科書の最後らへん
に乗っていた気がします。

シーンマネージャはシングルトンパターンを利用したクラスです。
一つ以上のインスタンスを管理するクラスをマネージャといいます(多分)


はい、またお願いいたします。

No Title

  • by Yana#P
  • 2012/04/27(Fri)17:41
  • Edit
>1年ボーイ
そういう感じ
ついでに俺は更新関数の戻り値の型をscene_baseにして、シーンが変化しない場合はthisを、変化する場合は次のシーンのインスタンスをnewして返すようにしてる。

たくさん質問する子ほど優秀やねん

  • by 1年ボーイ
  • 2012/04/30(Mon)17:56
  • Edit
前のシーンで得た情報を、後のシーンに使いたい場合どうすれば最適なのでしょうか?
グローバルは使ったらかっこわるいですよね
親クラスであるscene_baseに前もって場所を確保してそれを子クラスである各シーンから参照したりすればいいのでしょうか?

No Title

  • by Yana#P
  • 2012/04/30(Mon)20:15
  • Edit
>1年ボーイさん
別のシーンから参照したい値とそのアクセサをstaticで作っておいて、参照したいときに参照すればいいと思う
例えばgame_sceneの中でスコアの加算などを行って、result_sceneでその値を参照する場合、
(result_scene.cpp内)
//スコア取得
unsigned int Score = game_scene::GetScore();
って感じで

No Title

  • by Yana#P
  • 2012/04/30(Mon)20:19
  • Edit
ごめん。一応これも書いとく

(game_scene.h内)
class game_scene : public base_scene {

public:
/* 色々省略 */
static unsigned int GetScore(){ return m_Score; } //スコア取得

private:
/* 色々省略 */
static unsigned int m_Score; //スコア

};

(game_scene.cpp内)
unsigned int game_scene::m_Score = 0;


(何故DWORD使わないの?って人がいるかも知れないけど、一年ボーイ君がDWORDを知ってるかどうかが分からないから一応こっちにしておいた)

No Title

  • by リーダ和田
  • 2012/04/30(Mon)21:34
  • Edit
yana#p様へ
確かにその方法で解決しますがスマートでない気がします。

私、個人の意見ですが、
子クラスから子クラスにアクセスするのに違和感がある。それと、
static による解決だと、渡したいデータが増えてしまった場合、
staticの変数・アクセサが増えるデメリットがあります。

次のシーンにデータを送るためだけのクラスを作り、
シーンの管理クラスに持たせるほうが自然かと思います。
struct SceneData
{
int score;
int hit;
int life;
};

これでも解決しますが、
次のシーンへのデータの種類が増える度にこういう構造体が増えて
管理クラスで保持していといけないため、
各シーンクラスで次のシーンに必要な情報を構造体でまとめておいて
管理クラスにこういうのを作って
boost::any m_NextData;
に突っ込めば
ひとつにまとめれると思うのですがどうでしょう?

boost::anyのURLです。http://www.kmonos.net/alang/boost/classes/any.html

No Title

  • by Yana#P
  • 2012/05/01(Tue)00:05
  • Edit
>リーダ和田さん
なるほど。

boostはまだ勉強してないなぁ・・・

僕は夜行性

  • by 1年ボーイ
  • 2012/05/01(Tue)03:46
  • Edit
なるほろ、staticってのはクラス変数てきな感じですよな。Rubyから入ったからごっちゃやねん
いろいろ方法があってややこしや
管理クラスってこの場合はscene_baseですかね
親クラスでboost::anyを使って変数を宣言しておく。前のシーンで次のシーンで使う情報を構造体でそこに記憶する。子クラスである各シーンでそれを参照する。こういうことですかね?

ブログ内検索

最新コメント

[01/29 人面犬]
[10/01 8ch]
[09/12 uncle]
[09/10 某卒業生]
[06/07 uncle]

カレンダー

04 2024/05 06
S M T W T F S
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

テスト

Copyright ©  -- GESブログ --  All Rights Reserved
Design by CriCri / Photo by Geralt / powered by NINJA TOOLS / 忍者ブログ / [PR]