スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ローグライクゲームを作ってみよう!  その5 ~移動処理~

今回は、ローグライクゲームの
移動処理の部分を解説して行こうと思います。

※個人的な実装方法です。

■解説順
【1】単純な移動処理
【2】歩数の決定方法
【3】任意の歩数での移動処理



【1】単純な移動処理
とりあえず、色々と思うところがあって、
今回 移動方法をキーフレームアニメーションで実装しました。

※今までは下記のように、座標のインクリメント、デクリメントで対応していた。

switch(m_MoveType){
case MOVE_RIGHT: m_PosX += 1;break;
case MOVE_LEFT: m_PosX -= 1;break;
case MOVE_UP: m_PosY -= 1;break;
case MOVE_DOWN: m_PosY += 1;break;
default:break;
};

//マップチップが32の場合は、x,yがともに32で割り切れる座標かを確認
if((m_PosX % CHIP_LENGTH) == 0 && (m_PosY % CHIP_LENGTH) == 0){
m_MoveType = MOVE_NONE;
}

上記のようなやり方は単純ですが、
問題点がいくつかありました。
-------------------------------------------------------------
問題1:スピードの調整が面倒。
例えば、32ドットのマップチップの場合、
関数が32回実行された際に移動が終了します。

もうちょっと早い方が良いな、と思った場合は、
m_PosX += 2 とか、 m_PosX += 4 とか、 m_PosX += 8とか、
32で割り切れる値の範囲でしか変更が出来ません。
(移動終了判定にて、x,y座標が 割り切れるかどうかで確認している為)

問題2:異なる歩数の場合は?
主人公が1歩歩くたびに、敵が2歩動くとします。

単純に考えて、下記のように一回の処理で
敵の進む幅を2倍にしなければなりません。

主人公 ・・・ m_PosX += 1;m_PosX -= 1;など。
敵   ・・・ m_PosX += 2;m_PosX -= 2;など。

ここらへんはまだ良いのですが、
主人公が1歩歩くたびに、敵が3歩動くとします。

この場合、下記のようにすれば問題が無い様に思えますが、
これだと正常に動きません。

主人公 ・・・ m_PosX += 1;m_PosX -= 1;など。
敵   ・・・ m_PosX += 3;m_PosX -= 3;など。

歩数が32の倍数でなくなってしまってるからです。
(3,6,9,12,15,18,21,24,27,30,33・・・ となり、32を超える。
  マップチップのマス目ぴったりの場所を通り過ぎて行ってしまいます。)
-------------------------------------------------------------
・・・と、言う訳で、
異なる歩数で動く敵が居る仕様の場合、座標のインクリメント、デクリメントじゃ
面倒な事になりそうだと思い、今回やり方を変えたのです。

キーフレームアニメーションは時間で制御します。
式にするとこんな感じ。

現在の座標 = 初期座標 + 進み具合×(移動先の座標 - 初期座標);
※進み具合 ・・・ (現在の時刻 - 移動開始時刻) / 移動時間 で算出。


例えば、x座標 120の場所から220の場所まで 2秒(2000ミリ秒)で移動する場合、
下記のようになります。

double t_Rate = (timeGetTime() - m_StartTime) / 2000;
m_PosX = 120 + (t_Rate * (220 - 120));


//0秒経過
double t_Rate = (0) / 2000; //t_Rateは0.0
m_PosX = 120 + (t_Rate * (220 - 120)); //座標は120になる。
//1秒経過
double t_Rate = (1000) / 2000; //t_Rateは0.5
m_PosX = 120 + (t_Rate * (220 - 120)); //座標は170になる。
//2秒経過
double t_Rate = (2000) / 2000; //t_Rateは1.0
m_PosX = 120 + (t_Rate * (220 - 120)); //座標は220になる。

上記だと固定座標のアニメーションしかしないので、
下記のように、適宜 変数に座標などを保存しておけば、任意のアニメーションが行えます。
(斜め移動も可能)

const DWORD g_MoveTime = 2000;
//中略//
if(m_Move == MOVE_NONE){
if(Key[KEY_INPUT_RIGHT] == 1){
m_StartPosX = m_PosX;
m_NextPosX = m_StartPosX + 32;
m_StartTime = timeGetTime();
m_Move = MOVE_RIGHT;
}
}
//中略//

double t_Rate = (timeGetTime() - m_StartTime) / g_MoveTime;

//時間が経過しすぎている場合、
//アニメーションがちょうど終了する値に変更する。

if(t_Rate > 1.0){
t_Rate = 1.0;
}
m_PosX = m_StartPosX + (t_Rate * (m_NextPosX - m_StartPosX));

if(t_Rate == 1.0){
m_Move = MOVE_NONE;
}



【2】歩数の決定方法
次は歩数の出し方について。
端的に記載します。

<1> 移動するオブジェクトに、下記のパラメータを持たせる
int m_Speed; //速度
int m_SpeedCount; //歩数を決める際に利用するカウンタ
int m_StepNum; //歩数
<2> 主人公が移動した際、全ての移動するオブジェクトに、
   主人公のSpeedの値をSpeedCountに加算
する
<3> SpeedCount / Speed を、歩数とする (m_Stepに代入する)
<4> SpeedCount % Speed を、SpeedCountに代入する 

以上です。

よく分からないと思うので、実際にどうなるか記載してみます。

<1>初期状態
      主人公  敵A  敵B  敵C
Speed :   30   15   10   20
SpeedCount:   0    0    0    0
Step :   0    0    0    0

<2>主人公のSpeedを、全オブジェクトのSpeedCountに加算
      主人公  敵A  敵B  敵C
Speed :   30   15   10   20
SpeedCount:   30   30   30   30
Step :   0    0    0    0

<3>SpeedCount / Speedを、Stepに代入する
      主人公  敵A  敵B  敵C
Speed :   30   15   10   20
SpeedCount:   30   30   30   30
Step :   1    2    3    1

<4>SpeedCount % Speedを、SpeedCountに代入する
      主人公  敵A  敵B  敵C
Speed :   30   15   10   20
SpeedCount:   0    0    0   10
Step :   1    2    3    1

■2回目の移動■
<2>主人公のSpeedを、全オブジェクトのSpeedCountに加算
      主人公  敵A  敵B  敵C
Speed :   30   15   10   20
SpeedCount:   30   30   30   40
Step :   0    0    0    0

<3>SpeedCount / Speedを、Stepに代入する
      主人公  敵A  敵B  敵C
Speed :   30   15   10   20
SpeedCount:   30   30   30   40
Step :   1    2    3    2

お分かり頂けたでしょうか。

見ての通り、主人公は1歩、敵Aは2歩、敵Bは3歩 進む事がわかります。
また、中途半端なSpeedを持った 敵Cは、SpeedCountの状況に応じて
一歩進んだり二歩進んだりして、
歩数(移動速度)が調整されている
事が分かると思います。

※「歩数(移動速度)は固定にしたい!」という場合は、敵のSpeedを、
  主人公のSpeedCountの値で割り切れる値にしておけばOKです。


【3】任意の歩数での移動処理
ここちょっとややこしいので注意。

例えば3000ミリ秒で主人公が1歩動くとします。
この時、3歩動く敵は、下記のように処理を行います。
---------------------------------------
①1000ミリ秒で一歩動く
②次の移動先を決める
③1000ミリ秒で一歩動く (2000ミリ秒経過)
④次の移動先を決める
⑤1000ミリ秒で一歩動く (3000ミリ秒経過)

---------------------------------------
要は、主人公が移動先を決めて一歩移動している間に、
敵は細かく移動、移動先決定、移動、移動先決定を 歩数の数だけ行う
ことになるのです。

これを実装するコードは下記の通り。

//g_BaseMoveTimeは、主人公が一歩進む際にかかる時間
一歩あたりの移動時間 = g_BaseMoveTime / g_MoverInfo[1].m_Step;
double t_Rate = (double)(経過時間 - (現在の歩数 - 1)*(一歩あたりの移動時間)) / 一歩あたりの移動時間;

if(t_Rate == 1.0){
g_MoverInfo[1].m_MoveID = MOVE_NONE;
//現在の歩数を増やす
g_MoverInfo[1].m_NowStep += 1;

//現在の歩数 > 移動する歩数になったら、移動終了。変数を初期化
if(g_MoverInfo[1].m_NowStep > g_MoverInfo[1].m_Step){
g_MoverInfo[1].m_NowStep = 1;
}

}


単純に移動する場合は、t_Rateが1.0になれば終了なのですが、
歩数が一歩以上の場合は、複数回 移動処理が行われるので、
t_Rateが1.0になる度に、現在の歩数を1ずつ増やし、決められた移動する歩数を
超えれば終了
、というような処理が追加されております。

あと、t_Rateを算出する式もややこしいので、実際に数値を代入していって見ましょう。

//一歩目 途中
double t_Rate = (double)(500 - (1 - 1)*(1000)) / 1000; //t_Rateは0.5
//一歩目 終了
double t_Rate = (double)(1000 - (1 - 1)*(1000)) / 1000; //t_Rateは1.0

//二歩目 途中
double t_Rate = (double)(1500 - (2 - 1)*(1000)) / 1000; //t_Rateは0.5
//二歩目 終了
double t_Rate = (double)(2000 - (2 - 1)*(1000)) / 1000; //t_Rateは1.0

//三歩目 途中
double t_Rate = (double)(2500 - (3 - 1)*(1000)) / 1000; //t_Rateは0.5
//三歩目 終了
double t_Rate = (double)(3000 - (3 - 1)*(1000)) / 1000; //t_Rateは1.0

以上です。


【ソースコード】Rogue3.txt
【使用画像】
MapChip1.png
Human.png
※今回、上記の画像を使用しております。
 上記の画像もダウンロードするか、もしくは (64×32)が4×4の主人公の画像と、
 (32×32)が2×2のマップチップの画像を用意すれば動作すると思います。


※注意点※
【1】g_MoveBaseTimeは、歩数で割り切れる値にしてください。
(3歩動く敵が居る場合は、3000、6000など。
 例えば1000にしてしまうと、一歩あたり333.3333と、割り切れない時間で移動することになり、
 誤差が生まれ不具合が発生します。(断言。) )

デフォルトでは600にしてます。1,2,3,4,5,6などで割り切れる数なので、
1~6歩で動く敵は実装できます。(一度に6歩動くチートな敵なんて 普通は居ないでしょうが)

【2】この記事で解説していない部分があります
・画像の読み込み
・マップの定義、読み込み
・当たり判定
・アニメーション (人が歩いているように見せるための、画像切り替え)

今回内容を「移動」に絞ったので、上記の部分の解説は省略しています。
「意味分からん」みたいな所とかあったら突っ込みを入れていただければ解説/修正をします(_ _;)


【3】描画順の管理を行っていません。
今回 主人公 → 敵 の順で描画しています。
なので、敵が前面に来たら主人公は隠れます。

なので、画像が不自然に重なっても今回は無視してください。

【4】ソースコードがやや長い&最適な構造ではありません。
本当は分割コンパイルとかすべきなのですが、
ファイルを一つダウンロードしてコンパイルすればすぐ実行できる方が良いだろう、
と思い、意図的にクラス化などをしていない変数などがあります。

なので、アルゴリズムの部分だけに着目して、
他の変数の定義の部分は、自己流に改造するのが一番良い
と思います。


次回は・・・
根本の描画システムについて解説します。

※「クラスまみれのゲームプログラミング入門」みたいな感じ
【参考URL】クラスまみれのゲームプログラミング入門

描画システムの後は、
基本のステータスの実装と、敵との戦闘処理を作っていきます!
スポンサーサイト

テーマ : ゲーム製作 関連 - ジャンル : ゲーム

コメント

No title

やっとこさここまで追いつきました。

とても勉強になります。
続きも楽しみにしております。

No title

ありがとうございます・・・!
更新が遅れて申し訳ないです(´ `;)

(反省を活かして、次に解説記事を書く時は
一通り記事を用意しておいてから更新しようと思ってます)

2月上旬には 次の記事を書きたいと思います・・・!
お待ちください・・・!v-393
コメントの投稿
管理者にだけ表示を許可する



上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。