今日のひらしょー本の目次
http://snoopopo.hatenablog.com/entry/2015/04/27/114854
今回は ひらしょー本の説明を読む→自分でコード組んでみる→ひらしょーさんのコード見る
の流れでやったので、けっこう違う部分があるけど,javaっぽい書き方をしたりひとりよがりになものになりそうなので、 ひらしょーさんのコードはよく読んできっちり理解しておこう.
#ひらしょーさんのソースは、 ( 01_FirstGame\NimotsuKun2\main.cpp ) より抜粋
参照で別名にして書くのを簡単にしてる?
・ひらしょーさん
Array2D< Object >& o = mObjects;
↑のように mObjects を o という参照に入れてる。=別名にしてる。
それ以降は o( x, y ) といった感じで呼べてて短くていい感じだからやったのかな.
僕のソースだと mObjects( x, y )とそのまま書いてる.
更新対象を絞ってる
・ひらしょーさん
//A.その方向が空白またはゴール。人が移動。 if ( o( tx, ty ) == OBJ_SPACE ){ o( tx, ty ) = OBJ_MAN; o( x, y ) = OBJ_SPACE; //B.その方向が箱。その方向の次のマスが空白またはゴールであれば移動。 }else if ( o( tx, ty ) == OBJ_BLOCK ){ //2マス先が範囲内かチェック int tx2 = tx + dx; int ty2 = ty + dy; if ( tx2 < 0 || ty2 < 0 || tx2 >= w || ty2 >= h ){ //押せない return; } if ( o( tx2, ty2 ) == OBJ_SPACE ){ //順次入れ替え o( tx2, ty2 ) = OBJ_BLOCK; o( tx, ty ) = OBJ_MAN; o( x, y ) = OBJ_SPACE; } }
・僕のコード
//移動不可能チェック if( Object::OBJ_WALL == mObject( tpx, tpy ) ){ //移動出来ない return; }else if( Object::OBJ_BLOCK == mObject( tpx, tpy ) ){ //ターゲットプレイヤー位置がブロックで、ターゲットブロック位置が壁かブロックの場合 if( Object::OBJ_BLOCK == mObject( tbx, tby ) || Object::OBJ_WALL == mObject( tbx, tby ) ){ //移動出来ない return; } } //プレイヤー座標の更新 mObject( px, py ) = Object::OBJ_SPACE; //ターゲットプレイヤー座標の更新 if( Object::OBJ_SPACE == mObject( tpx, tpy ) ){ mObject( tpx, tpy ) = Object::OBJ_MAN; }else if( Object::OBJ_BLOCK == mObject( tpx, tpy ) ){ mObject( tpx, tpy ) = Object::OBJ_MAN; //ターゲットブロックの位置も更新 if( Object::OBJ_SPACE == mObject( tbx, tby ) ) { mObject( tbx, tby ) = Object::OBJ_BLOCK; } }
このデータの状態を更新する場所だけのはなしじゃないのだけど、
ひらしょーさんのコードの方が、更新対象を確実に絞って更新してるのに対して僕のコードはなんというか 更新しないものを最初にチェックして弾いて、それ以外を更新してる感じ.というかそういう脳になってる.
実は最初のチェックが漏れてて変な動きをしたので、更新対象だけを確実に絞ってあとはスルーすればいいの考え方に変えていこう.
列挙型は数値だから添字になる
ひらしょーさんのコードではswitchを使ってるけど、NimotsuKun2の前verのNimotsuKunでは enum Objectを数値とみたててchar配列の順番と合わせてとってたから、 今回でゴール状態を別の配列に持つように変わってもこういう風にしてみた.
個人的にはenumを配列の添字として使うのはわかりやすいからけっこう好きだ.
・僕のコード
const char font[] = {' ', 'p', 'o', '#'}; const char goalfont[] = {'.', 'P', 'O'}; for( int y = 0; y < mStageY; ++y ){ for( int x = 0; x < mStageX; ++x ){ Object obj = mObject( x, y ); bool goal = mGoal( x, y ); if ( true == goal) { cout << goalfont[obj]; //列挙型は数値型なので、順番を合わせればとれる。 }else{ cout << font[obj]; //列挙型は数値型なので、順番を合わせればとれる。 } } cout << endl; }
縦横サイズのとり方
・ひらしょーさん
mWidth = mHeight = 0; //初期化 //現在位置 int x = 0; int y = 0; for ( int i = 0; i < size; ++i ){ switch ( stageData[ i ] ){ case '#': case ' ': case 'o': case 'O': case '.': case 'p': case 'P': ++x; break; case '\n': ++y; //最大値更新 mWidth = max( mWidth, x ); mHeight = max( mHeight, y ); x = 0; break; } }
とくに解説などで触れてないけれど、 この //最大値更新 の部分で std::max という標準ライブラリを使ってるみたいだ.
この辺りはライブラリに何があるのかよくわからないので追々…
文字列のループは最後のNULL文字で…
c++では文字列 char[] の最後に必ずNULL文字がつく、というはなしがあったから物珍しさで 文字列のstageデータでループする処理は全て以下のように書いてしまったけど.
・僕のコード
const char* i = stage; int x = 0; int y = 0; while( *i != '\0'){ //char[]は最後に \0 が入っている ~(略)~ }
やっぱりforの方がよかったかな.と思う.
ファイル読込のときにファイルのサイズ取ってるから、 ひらしょーさんのコードでは、for文でファイルサイズの文だけループするように書いてる.
自分がwhileよりforの方がいいと思うのは、EffectiveJavaから知った以下の理由。
・ループ変数が使える
・コピペでの間違いが起こりにくい
http://snoopopo.hatenablog.com/entry/2015/02/07/170842
ここ↑でも少し書いてた.