C++でゲームを作ってみようー基礎編(4/5)

いやー長いですね、やっとゲーム部分を作れます
どんなゲームでしたっけね、仕様書を見返しておきましょう

仕様書

ジャンル :パズルゲーム(たぶんね)
どういう風:荷物を所定の位置に置く、ただし簡単に行かせはしない
必要なもの:プレイヤー、荷物、ゴール・・・
どんな場面があるか:タイトル、ゲーム画面、エンディング

各場面で動くもの

タイトル  :(選択肢)
ゲーム画面 :プレイヤー、荷物、ゴール
エンディング:なし?

どういう風に動くか

選択肢  :キーボードのWで上にSで下に移動、Zで決定
プレイヤー:キーボードのWASDで上下左右に動く、壁には行けない
荷物   :プレイヤーが押したら押した方向に壁がない時に動く
ゴール  :ゲーム開始時に決定されてそれ以降動くことはない

各場面はどうやったら変わるか

タイトル→ゲーム画面  :タイトルではじめるを選ぶ
タイトル→ゲーム終了  :タイトルで終わるを選ぶ
ゲーム画面→タイトル  :プレイヤーが諦めたとき
ゲーム画面→エンディング:クリアした時
エンディング→タイトル :Z押したら


えーっと・・・ゲーム画面ではプレイヤーが荷物を押してゴールに置かせればいいんですね
ところで、この画面を想像するとき、自然と脳内にマップがでてきませんか?
プレイヤーがいて、荷物があって、ゴールがある場所・・・そんなような「空間」です
仕様書には書いてないですけど、プレイヤーの場所、荷物の場所などを自分で定義する必要がありそうです

ここではプレイヤーがいる場所などは2D空間で、マス目状で管理します
キーを1個押すとプレイヤーが1マス動く、そんな感じで行きましょう
えっ、3Dなんてそんなもんやりませんよ、やりたいならUnityでも使ってください

さて、どうやって空間を定義しましょうか
2D空間で、マス目だから・・・二次元配列でやりますか
この配列の中にどこが壁で、どこが道で、どこがゴールかが書いてあるとしましょう
そして、プレイヤーと荷物はその配列の中のどこかの道にいるとします
(マップの配列の中に荷物とかプレイヤーも入れてもいいんですけどね)
プログラムにするとこんな感じですかね

マップ[5][5]={
{壁壁壁壁壁}
{壁ゴ  壁}
{壁   壁}
{壁   壁}
{壁壁壁壁壁]
}

荷物[2]//行,列
プレイヤー[2]//行,列

マップの空白は道、ゴはゴールの意です
で、荷物もプレイヤーも道の上に配置しましょう
例えば・・・

荷物 = {2, 2}
プレイヤー = {3, 3}

ここに置いてみるとしましょう

さて、マップと荷物とプレイヤーを表示させてみます
面倒なんで座標(0,0)から順に精査していって
そこにあるものを表示していきましょうか
ゴールとプレイヤーとかが重なった場合の処理は適当に

for(行の精査){
	for(列の精査){
		今確認してる座標で一致した奴を表示
	}
}

いや雑ですね、素晴らしい
Game()の中にでも書いておきましょうか

じゃあ次にプレイヤーを動かしましょう
キーボードのWASDで上下左右に動き、壁には行けないので
プレイヤーの座標を-1させたり+1させて、後は壁の判定でもしておきましょうか

if(Wが押された)先に壁がなければプレイヤーを上に移動させる
if(Aが押された)先に壁がなければプレイヤーを左に移動させる
・・・

こんな感じに・・・えっ壁の当たり判定の作り方ですか?
まぁ、プレイヤーの位置とマップから判断できそうですよね

あ、プレイヤーは荷物を動かせましたね
じゃあ追加しましょう

if(Wが押された)先に荷物があればプレイヤーを上に移動させ荷物も上に移動させる
if(Aが押された)先に荷物があればプレイヤーを左に移動させ荷物も左に移動させる
・・・

そういえば荷物って壁に向かっては押せませんね
荷物が動かなければプレイヤーも動きませんし、条件を追加しましょうか

if(Wが押された)先に荷物があり荷物が動くならプレイヤーを上に移動させ荷物も上に移動させる
			 荷物がなく壁がなければプレイヤーを上に移動させる
if(Aが押された)先に荷物があり荷物が動くならプレイヤーを左に移動させ荷物も左に移動させる
			 荷物がなく壁がなければプレイヤーを左に移動させる
・・・

ごっちゃごっちゃしてきましたね・・・整理できますか?
似たような処理は関数でまとめましょう
自分はこの部分を10行ちょっとで書けました、もっと短く書けると思いますが・・・
クラス化したりするともうちょっと簡単になるかもしれないですね

はい、ここで動作確認しましょう、return文はGAMEにでもしておきますか
きちんと予想通りの動きはしてますか?初期化とか大丈夫ですか?

私はこんな感じです
f:id:Nsan:20170806204820p:plain


さて、このままだと諦めることもエンディングを迎えることもできませんね
諦める方法は・・・X二回押しにしましょう
エンディングを迎えるにはゴールの位置に荷物があるかどうかで判断できますね

if(Xを二回押した)諦める
if(ゴールに荷物がある)エンディング

どんどん雑になってきましたねー
何気なくXっていう新しいキーを使うことにしてますが
うまいことやってたらこの辺の仕様追加にも対応できます
enumって新しく入れても勝手に値を割り振りしてくれるんですよねーすごいなー
Javaのlengthとかも強いんだよなー


一旦区切ります
あとはもうエンディングを作るだけ