====== 当たり判定とオブジェクトの移動 ====== 当たり判定とか大げさなことを言ったが、箱がマスごとに動くので、動く先に何があるかと、自分の座標に何があったかを考えるだけでよい。\\ 自分のマスの現在状態は、\\ - 自分のみ - 自分+ゴール の2種類で、自分以外のマスの状態は - 床(空の状態) - 荷物 - ゴール - 荷物+ゴール - 壁 の4種類である\\ 実はすでにこれをenum OBJNAMEで表してある。\\ ので、ゲーム内のマップのある座標に何があるかはこのOBJNAMEで表す\\ enum OBJNAME { FLOOR, //床(空) WALL, //壁 LUGG, //荷物 GOAL, //ゴール(目的地) HUMAN, //自分 HUMAN_ON_GOAL, //ゴール上の自分 LUGG_ON_GOAL //ゴール上の荷物 }; === 自分が動いた時を考える === 当たり判定的なことを考えると、自分が隣のマス(上下左右どこでも)に動こうとしたときに、隣の状態(オブジェクトは何か)で自分が動けるか動けないかが決まってくる。\\ まず簡単な場合を考えてみよう。\\ 自分がゴールと重なっておらず、隣が床の時が一番簡単でそのまま移動すればよい。(今までやってた消して隣に座標ずらす移動法)\\ * 現在位置:HUMAN 、 隣:FLOOR の時 * 現在位置:HUMAN => 現在位置:FLOOR * 隣:FOOR => 隣:HUMAN 次に簡単なのが、自分がゴールと重なっておらず、隣が壁の時\\ この時は動けないので何もしなければよい。または以下のように、現在と同じ値を入れると動かない\\ * 現在位置:HUMAN 、 隣:WALL の時 * 現在位置:HUMAN => 現在位置:HUMAN * 隣:WALL => 隣:WALL とりあえずこれを書いてみよう!\\ UpdatePlayの中の移動に関する部分を関数にしてしまおう!\\ **void MoveObject(direction _dir, Map& _map);**を作っていきます。\\ この関数は、\\ - プレイヤーの位置を取得 - 引数_dirから、現在位置から度の座標に移動されるか、移動先の予測をする(予測位置) - 現在位置と、予測位置のオブジェクトを判別(OBJNAMEで) - 予測位置のオブジェクトによって、移動(=オブジェクトの入れ替え(書き換え))を行う void UpdatePlay() { int c = _getch(); direction nextDir = GetDirection(c); //この辺から Point crrP = GetPlayerPos(sampleSatge); Point nextP = { crrP.x + dirVector[nextDir].x, crrP.y + dirVector[nextDir].y }; SetObjtoMap(OBJNAME::FLOOR, crrP, sampleSatge); SetObjtoMap(OBJNAME::HUMAN, nextP, sampleSatge);     //この辺までを移動のための関数化 // ↓     //MoveObject(nextDir, sampleStage);みたいに書けるように変更する! } === void MoveObject(direction _dir, Map& _map)の作成 === それでは関数を実際に作っていくが、さきほどの一番簡単な2つのパターンから書いてみよう。\\ オブジェクト移動用関数を作成 //プロトタイプ宣言 void MoveObject(direction _dir, Map& _map); //関数定義 void MoveObject(direction _dir, Map& _map) { //プレイヤーの位置を取得 Point pp = GetPlayerPos(_map); Point nextPos = {今までやってた、移動ベクトル足すやつをここでやって、移動先を計算}; OBJNAME crr = 現在位置(pp)のオブジェクト名を取得; OBJNAME next = 予測位置(nextPos)のおぶえじぇくと名を取得; switch(予測位置のオブジェクトによって処理を変える) { case 床なら: SetObjtoMapで予測位置(nextPos)に自分(HUMAN)をセット; SetObjtoMapで現在位置(pp)に床(FLOOR)をセット; break; case 壁なら:         //何もしないか、同じ位置に同じオブジェクトをセット(何もしないのが楽よ) break; default: break; } } === 実行してみよう! === 関数が完成したので、UpdatePlayがさらにシンプルになる。\\ (何なら、MoveObject(GetDirection%%(%%c%%)%%, sampleStage)にすると2行に…) UpdatePlayをアップデート! void UpdatePlay() { int c = _getch(); direction nextDir = GetDirection(c); MoveObject(nextDir, sampleStage); } 壁のオブジェクトまで移動してその方向へ動こうとすると動かなくなるのがわかる。\\ また、現在は移動先が壁と床のときだけ判別しているので、ほかのオブジェクトの時は何もしない=移動しない、という処理になっている。\\ したがって、床以外のところは歩けない。というプログラムになっているので確認してほしい。\\ マップを書き換えたりして遊んでみるといいよ。\\ ==== ちょっと改良 ==== ここでちょっと改良を加える。何かというと、登場人物の整理を一回しよう\\ enum OBJNAME { FLOOR, //床(空) WALL, //壁 LUGG, //荷物 GOAL, //ゴール(目的地) HUMAN, //自分 HUMAN_ON_GOAL, //ゴール上の自分 LUGG_ON_GOAL //ゴール上の荷物 }; 自分のキャラの状態を考えると実は、自分の状態は、\\ - 自分のみ - 自分+ゴール の2種類がいる。ゴールの上に重なってるときは、移動の処理をちょっと切り替えなければならない。\\ なぜならば、現在は、床の上から、自分が移動するので、残るのは床だが、\\ ゴールの上に自分がいると、自分が移動した後には、ゴールが残る。現在はこれが表現できない。\\ === GetPlayerPosの改良 === そもそも、GetPlayerPosが以下のようになっているので、\\ if (配列が、OBJNAME::HUMANだったら) return(座標を返す); 自分の位置がHUMAN_ON_GOALの時はプレイヤーの位置を取得できない。。。\\ まず、これを修正!難しくはないよね?\\ if (配列が、OBJNAME::HUMANまたは、OBJNAME::HUMAN_ON_GOALだったら) return(座標を返す); === MoveObjectの改良 === ゴール上にプレイヤーがいるときも取得できるようになったので、移動処理を改良する\\ * 現在位置:HUMAN or HUMAN_ON_GOAL 、 隣:FLOOR の時 * 現在位置の処理 - 現在位置:HUMAN の時 => 現在位置:FLOOR - 現在位置:HUMAN_ON_GOAL の時 => 現在位置:GOAL * 隣:FOOR => 隣:HUMAN UpdatePlayの中のswitch-case文を修正 switch (next) { case FLOOR: SetObjtoMapでnextPosにHUMANを設定 if (現在位置のOBJNAMEがHUMAN_ON_GOALだったら) 元の場所にゴールを設置 else SetObjtoMap(FLOOR, pp, _map);//HUMANのみだったら床を設置 break; case WALL: break; default: break; } 書き換え終わったらマップの自分の初期位置を4→5に変更してみよう、自分が移動したときに、HUMAN_ON_GOALをあらわす+⇒.(GOAL)に表示が変わればうまく行っている。\\ でも、ゴールの上に戻ろうとしても戻れない。。。それはnextがGOALのときの処理をまだ書いていないからである。(のはわかるよね)\\ ==== その他の場合の移動を書いていく ==== その他の場合を書いていこう\\ おそらく次に簡単なのは、隣がゴールだった時の処理であろう\\ 自分の隣がゴールか、または、ゴールが2個続いている片側にプレイヤーが乗ってるっていう状態\\ * 現在位置:HUMAN or HUMAN_ON_GOAL 、 隣:GOAL の時 * 現在位置の処理 - 現在位置:HUMAN の時 => 現在位置:FLOOR - 現在位置:HUMAN_ON_GOAL の時 => 現在位置:GOAL * 隣:GOAL => 隣:HUMAN_ON_GOAL ここまで書けばもう、さっきと同じようにUpdatePlayを更新できるよね?\\ UpdatePlayの中のswitch-case文を修正3回目? switch (next) { case FLOOR: SetObjtoMapでnextPosにHUMANを設定 if (現在位置のOBJNAMEがHUMAN_ON_GOALだったら) 元の場所にゴールを設置 else SetObjtoMap(FLOOR, pp, _map);//HUMANのみだったら床を設置 break; case WALL: break; case GOAL: 隣がゴールだった時の移動処理を書く! default: break; } === 実行結果 === さっきまでの状態だとゴールの上に乗れなかったのが、ゴールを通過できるようになったと思う。\\ ゴール上のプレイヤーの表示は'+'だよ。\\ {{:game-engineer:classes:2023:something-else:summertime-special-cource:スクリーンショット_2023-08-10_004244.png?300|}} ;#; [[game-engineer:classes:2023:something-else:summertime-special-cource:costcoman-console-5|その5 当たり判定の続き、今度は荷物を押すへ]] ;#;