>> ゲームプログラミング入門トップに戻る

キャラを歩かせよう

今回はキャラを歩かせる方法について説明します。

これまでお見せしてきたキャラの移動は、画像は動いていましたがキャラ自身は動いていませんでした。
ではキャラ自身を動かすにはどうするのでしょうか?
これもパラパラ漫画の原理と一緒で、歩いている画像を切り換えながら描画するだけです。
つまり、歩く動作をしたいくつかに分割した画像が必要になります。

今回は、
REFMAP様が配布しているフリー画像素材を使用します。
URLはhttp://www.tekepon.net/fsmになります。
素晴らしい素材を沢山配布されていますので、是非一度訪れてみてくださいね。
画像はコチラを使います。

上下左右に歩く動作を分割したものを1枚にまとめた透過PNGファイルです。
キャラ以外の部分は透過してるので、透過フラグをTRUEにして描画すればキャラ以外の部分は消えます。
このキャラチップを使う場合はこのサイトからダウンロードせずに、REFMAP様のサイトにいき、ダウンロードしてください。

数で言うと12個分の画像がありますので、これを分割して読み込むことになります。
これも専用の関数があって、LoadDivGraph関数を使います。
引数は以下のようになっています。

1int LoadDivGraph( char *FileName , int AllNum , int XNum , int YNum ,int XSize , int YSize , int *HandleBuf ) ;
2 
3FileName   : 分割読み込みする画像ファイル文字列のポインタ
4AllNum    : 画像の分割総数
5XNum ,YNum : 画像の横向きに対する分割数と縦に対する分割数
6SizeX ,SizeY  : 分割された画像一つの大きさ
7HandleBuf   : 分割読み込みして得たグラフィックハンドルを保存するint型の配列へのポインタ

第一引数はファイル名を指定します。
第二引数は画像の分割総数を指定します。今回ならば12個なので12を指定します。
第三、第四引数は横と縦向きに対する画像の分割数です。
今回であれば、横は3列、縦は4行になっているので、3と4を指定します。
第5、第6引数は分割画像一つ分の大きさを指定します。
第7引数は、分割された画像一つ一つのグラフィックハンドルを保存するint型の配列へのポインタを指定します。
今回は12個分読むので、int image[12]などという配列を作り、そのポインタを渡します。
配列の中には以下のようにグラフィックハンドルが格納されます。数字は配列の添字を表しています。

0 1 2
3 4 5
6 7 8
9 10 11

この関数で画像を読み込んだら、歩いてるかのように順番に画像を切り替えて描画するだけです。
イメージとしては、

ある方向キーを押し続けている間はそのカウンタを増やし、
一定回数分だけカウンタが増えたら、画像を次の動作のものに切り替える

というようにします。

下記のコードを見てください。

001#include "DxLib.h"
002 
003// プログラムは WinMain から始まります
004int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow )
005{
006    ChangeWindowMode(TRUE);
007 
008    if( DxLib_Init() == -1 )        // DXライブラリ初期化処理
009    {
010        return -1 ;         // エラーが起きたら直ちに終了
011    }
012 
013    //キー取得用配列
014    char key[256];
015 
016    //x座標
017    int x=300,y=240;
018 
019 
020 
021    //グラフィックハンドル格納用配列
022    int gh[12];
023 
024 
025    //画像読み込み
026    LoadDivGraph("charall.png",12,3,4,49,66,gh);
027 
028    //移動係数
029    float move=1.0f;
030 
031    //横方向と縦方向のカウント数。
032    int xcount=0,ycount=0;
033    //添字用変数
034    int ix=0,iy=0,result=0;
035 
036    while(ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && GetHitKeyStateAll(key)==0){
037 
038        if(key[KEY_INPUT_LEFT]==1 || key[KEY_INPUT_RIGHT]==1){
039             
040            if(key[KEY_INPUT_UP]==1 || key[KEY_INPUT_DOWN]==1){
041                //移動係数を0.71に設定
042                move=0.71f;
043            }else{
044                //斜めじゃなければ1.0に設定
045                move=1.0f;
046            }
047        }else if(key[KEY_INPUT_UP]==1 || key[KEY_INPUT_DOWN]==1){
048            move=1.0f;
049        }
050         
051         
052        if(key[KEY_INPUT_LEFT]==1){
053            x-=(int)4*move;
054        }
055        if(key[KEY_INPUT_RIGHT]==1){
056            x+=(int)4*move;
057 
058        }
059        if(key[KEY_INPUT_UP]==1){
060            y-=(int)4*move;
061 
062        }
063        if(key[KEY_INPUT_DOWN]==1){
064            y+=(int)4*move;
065 
066        }
067 
068        //左キーが押されてて、かつxcountが0以上なら0にしてから1引く。
069        //それ以外は1引く
070        if(key[KEY_INPUT_LEFT]==1){
071            if(xcount>0)
072                xcount=0;
073            --xcount;
074                 
075        }
076        //右キーが押されてて、かつxcountが0以下なら0にしてから1足す。
077        //それ以外は1引く
078        if(key[KEY_INPUT_RIGHT]==1){
079            if(xcount<0)
080                xcount=0;
081            ++xcount;
082        }
083        //上キーが押されてて、かつycountが0以上なら0にしてから1引く。
084        //それ以外は1引く
085        if(key[KEY_INPUT_UP]==1){
086            if(ycount>0)
087                ycount=0;
088            --ycount;
089        }
090        //下キーが押されてて、かつycountが0以下なら0にしてから1足す。
091        //それ以外は1足す
092        if(key[KEY_INPUT_DOWN]==1){
093            if(ycount<0)
094                ycount=0;
095            ++ycount;
096        }
097 
098 
099        //カウント数から添字を求める。
100        ix=abs(xcount)%30/10;
101        iy=abs(ycount)%30/10;
102 
103        //xカウントがプラスなら右向きなので2行目の先頭添字番号を足す。
104        if(xcount>0){
105            ix+=3;
106            result=ix;
107        }else if(xcount<0){
108            //マイナスなら左向きなので、4行目の先頭添字番号を足す。
109            ix+=9;
110            result=ix;
111        }
112 
113        //yカウントがプラスなら下向きなので、3行目の先頭添字番号を足す。
114        if(ycount>0){
115            iy+=6;
116            result=iy;
117        }else if(ycount<0){
118            //1行目の先頭添字番号は0なので何もする必要なし。(分かりやすくするために書いときました)
119            iy+=0;
120            result=iy;
121        }
122 
123        //斜め移動の場合は横顔を優先
124        if(move==0.71f)
125            result=ix;
126 
127 
128        //描画
129        DrawGraph(x,y,gh[result],TRUE);
130 
131 
132        //押されてなければカウントをゼロにする。
133        if(key[KEY_INPUT_LEFT]!=1 && key[KEY_INPUT_RIGHT]!=1){
134            xcount=0;
135        }
136        if(key[KEY_INPUT_UP]!=1 && key[KEY_INPUT_DOWN]!=1){
137            ycount=0;
138        }
139 
140 
141        if(key[KEY_INPUT_ESCAPE]==1){
142 
143            break;
144        }
145         
146    }
147 
148    DxLib_End() ;               // DXライブラリ使用の終了処理
149 
150    return 0 ;              // ソフトの終了
151}

下記のようにキャラを歩かせることができます。


まずコードの説明を行います。
まず、LoadDivGraphで12分割の画像を読み込んでいます。
横、縦それぞれ四つずつです。
画像の大きさが147×266なので、一つの画像の大きさは約49×66です。

次にxcountとycountを宣言しています。
xcountは、右に移動するとxcountを1足して、左に移動すると1引くようにします。
ycountは、上に移動するとycountを1引いて、下に移動すると1足すようにします。
また、右か左のどちらも押されていない場合はxcountは0にし、
上下のどちらも押されていない場合はycountを0にするようにします。

キーの移動量を足したあとのif文の説明をします。
ここではカウントを足す処理を行ってます。
左キーの場合を考えてみましょう。
左キーの場合はxcountが0以上なら0にしてから1引くようにしています。
何故0にするのか?と疑問に思うと思いますが、
xcountの値だけで今はどちらを向いているかを表せるようにするためです。
0にしてから1引くということはマイナスになります。
マイナスということは左を向いている、とカウントを見るだけで判断できるわけです。
0以上でなければ、単純に1引くだけしかしてません。
0以上じゃないということは、すでに左を向いているというので、カウンタを足すだけです。

これを右、上下でも同じコードを書いておきます。
上下の場合は上キーを押すとカウンタが減り、下キーで増えるようにしています。
次の、

//カウント数から添字を求める。
ix=abs(xcount)%30/10;
iy=abs(ycount)%30/10;

のコードですが、まずabs関数を使って絶対値を求めています。
左向きや上向き移動の時はカウンタがマイナスなので絶対値を求めて正の数にします。
次に30で割ってその余りを求めてます。
割り算の余りというのは、割った数より小さい値しか返しません。
しかも割ってるカウンタというのは絶対値を求めているので、一つずつ増えていきます。
0÷30=余り0
1÷30=余り1
2÷30=余り2



と順番に余りが増えていくことになります。
そしてカウンタが30になるとまた余りが0に戻ります。
31からはまた余り1になって、また余り29まで行って0に戻る、という計算を、
カウンタが増え続ける限り繰り返すことになります。
しかもその値を10で割っています。
0~9を10で割ると、結果は0
10~19を10で割ると、結果は1
20~29を10で割ると、結果は2



という結果が得られます。
カウンタは1ループで1回しか増えません。
余りは最大で29までなので、結果の最大値は2までです(29÷10=2)
しかもカウントが30を越えたら、また0からループします。
つまり、10カウントごとに、0~2までの値を取得し続けることが出来ます。
これを画像の添字として使えば、
10カウントごとに画像が切り替わり、かつ永遠とループさせることができるわけです。

その次の、

//xカウントがプラスなら右向きなので2行目の先頭添字番号を足す。
if(xcount>0){
ix+=3;
result=ix;
}

という部分は添字の先頭の数合わせです。
例えばxカウントがプラスなら右向きです。
右向きの画像は、画像全体で言うと2行目がそうですよね?
2行目の先頭の添字番号は3です。
この数字を先程求めた添字に足すことで、
3~5までの数字が永遠と取得できることになります。
これを各方向で同じことをしているだけです。
resultという変数にその添字を入れています。

次の、

//斜め移動の場合は横顔を優先
if(move==0.71f)
result=ix;

は斜め移動の時の画像を横向き縦向きどちらに指定するかの処理を行ってます。
斜め移動ということは上下左右どちらのキーも押されているので、
横向きの画像、縦向きの画像どちらも指定できます。
移動係数moveが0.71fということは斜め移動しているので、
そのときには横向きの顔を優先するので、
横向きの画像の添字を表すixを代入しているわけです。

こうして求まった添字を使って、グラフィックハンドルの配列ghに指定し、
DrawGraph関数で描画することで、まるで歩いているように描画することが可能になるわけです。

最後に、「押さなければカウントを0にする」と書いてありますが、
何も押してないときにカウントが前のままだと動き出しがおかしくなるので、
何も押してないときはカウントをゼロにしているだけです。

ちょっと難しいですが、理解できましたでしょうか?
今回の説明は以上です。

次回はキャラをジャンプさせてみます。

>> 【キャラをジャンプさせよう】に進む
>> ゲームプログラミング入門トップに戻る