見下ろし2Dマップ上でプレイヤーを動かす
前回テキストデータで壁と通路の2Dマップを作成しました。
その内容をベースに作成したマップ上でプレイヤーを動かすというのを実装してみます。
この記事はシリーズものとなっています。
作業に入る前に
作業に入る前にどういう仕様なのか整理してみます。
- 十字キーの入力を受け付ける
- 押された方向にプレイヤーを動かす
- 進行方向が壁の場合は動かない
こんな感じでプレイヤーを動かせれば成功ではないでしょうか。
では作っていきます。
プレイヤースクリプトの作成
スクリプト作成
ではまずプレイヤーを動かすためのC#スクリプトを作成します。
シンプルにPlayerとしました。
作成したPlayerスクリプトをPrefabのPlayerにアタッチします。
入力を受け付ける
次にPlayerスクリプトの中身を作っていきましょう。
まず十字キーの入力を受け取れるようにします。
Update関数内に記述します。
一旦このような感じにしておきます。
方向を定義する
入力は無事受け付けたようですので、続いてPlayerがどの方向を向いているかを定義します。
列挙型を使います。
そしてUpdate関数内で受け取った入力によって方向を変えて行きます。
キーによって方向を変更したことが確認出来ました。
移動の実装
1マス移動の考え方
ではここから実際にプレイヤーを移動させる処理を書いていきます。
1マスずつ移動させたいと思います。
マップは現在(x座標,y座標)という感じで左上から右下にかけて(0,0)(0,1)(0,2)…のように碁盤の目のような感じでVector2Int型で並んでいます。
それにマップチップのサイズをかけて隙間なく埋めていました。
この仕組を使ってプレイヤーを動かして行こうと思います。
入力されたキーがプレイヤーの現在位置から、上ならy方向に+1、右ならx方向に+1、下はyが-1、左はxが-1という感じになると思います。
例えば先程の図でプレイヤーが下に向かうとすればyを-1してマップチップサイズをかければ下に1マス移動するということになります。
プレイヤーの座標を取得する
それでは最初にプレイヤーの座標を取得したいと思います。
そのためにPlayerスクリプトでそれ用の変数を定義します。
次にマップ生成時にPlayerも生成されていますので、前回作ったMapGeneratorスクリプトも修正していきます。
まずPlayerスクリプト
続いてMapGeneratorスクリプトの_createMap関数を修正します。
修正箇所は一つで実際にループ内でマップチップを生成しているときにマップのタイプがプレイヤーの場合はPlayerのcurrentPosに現在のposを代入しています。
これで現在のプレイヤーの座標が取得出来ました。
移動用の配列を準備する
続いて実際に上下左右に移動する際の値を配列で準備しておきます。
このようなInt型の二次元配列を用意しました。
先程作ったDIRECTIONと対応させたいので、この並びになっています。
一つ注意ですが、上を押したらyが+1なのにこの配列だとyが-1になっています。
これは前回MapGeneratorで作成したScreenPosでYの値に対して上下の位置を正しくするためにマイナスをかけていたので、その関係で移動用の配列でもYは上下の値が反転しています。
MapGeneratorを取得しておく
プレイヤーの移動も座標かけるマップチップのサイズを利用しますので、前回作ったMapGeneratorのScreenPos関数を利用したいと思います。
まずMapGeneratorの方を修正していきます。
具体的にはPlayerスクリプトでもMapGeneratorを取得できるようにInstantiate時にMapGenerator(HierarchyではMapManager)の子要素にします。
続いてScreenPos関数をpublicにします。
MapGeneratorはこのような感じで、
続いてPlayerスクリプトのStart関数で親要素(MapManager)からMapGeneratorを取得します。
これでPlayerスクリプトから後で使う、MapGeneratorのScreenPos関数を使用することができます。
念のためマップチップがMapManagerの子要素になっているか確認しておきます。
ちゃんと子要素になっていましたね。
移動用の関数を作る
続いてキーを押されたときの移動用関数を作成します。
また、現在位置(currentPos)から次の位置に移動する用の変数も準備します。
今回は移動用の関数は_move、次の位置の変数はnextPosとしました。
Playerスクリプトはこのようになっています。
_move関数の処理の流れは下記の通りです。
- directionを使ってmove配列からVector2Int型を取得し、currentPosにプラスしてnextPosに代入
- MapGeneratorのScreenPos関数で実際Playerが動く1マス分のゲーム上の位置を自身のlocalPositionに代入し移動
- currentPosにnextPosを代入し上書きして現在置を更新
このような感じです。
無事に1マスづつ動きました。
現状ではご覧の通り、壁を貫通していきますのでこれを直していきましょう。
壁貫通を防止
MAP_TYPE取得用の関数作成
進む方向が壁なのか、通路なのかがわかれば解消できそうです。
前回MapGeneratorでMAP_TYPE型の二次元配列、mapTableというものを作りました。
これを利用すれば進む先のMAP_TYPEが取得できそうです。
まずMapGenerator側でPlayerがMAP_TYPEを取得できる関数を作ります。
GetNextMapTypeとしました。引数にはVector2Int型を取ります。
それと列挙型MAP_TYPEは前回ではprivateになっていましたので、Playerからも取得できるようにpublicに修正します。
Player側でMAP_TYPEを取得する
では最後にPlayer側で進行方向の情報を取得して通路なら移動する実装をしてきます。
_move関数内を少し修正するだけで終わります。
このようになりました。
MapGeneratorのGetNextMapTypeに次の座標nextPosを渡し、戻ってきたMAP_TYPEがWALL以外なら進めるという処理です。
注意点としてMAP_TYPEがGROUNDなら進める、とすると最初に読み込んだマップデータでPLAYERが1,1に存在しますので、そのマス目は通れなくなるため、このようにしました。
では実際に動かしてみます。
壁にガンガンぶつかっているのですが、貫通すること無く、無事に通路だけを移動してくれました。
移動方向によってプレイヤーの画像を変えたり、歩くアニメーションをつけたり、スムーズに1マスを移動させるなどするとより雰囲気が出るかも知れませんね。
ここまでの内容は前回のものも含めてgithubにアップしておきますのでよろしければ参考にしてみて下さい。
github
前回のマップ生成の記事はこちらになります。
いずれこの内容を使って私の大好きなウィザードリィのような疑似3Dの実装をやってみようかと思います。
Unityの学習方法
Unity初心者の方にオススメの学習方法をいくつか紹介します。
Unity本【kindle unlimited】
私も利用しているAmazonが提供する電子書籍読み放題のサービスです。
月額980円で約200万冊以上が読み放題でとてもお得です。
また30日無料体験もできるのが魅力です。当然Unityに関する書籍もたくさん揃っています。
またC#関連の書籍や、プログラミングのものも多数あります。
プログラミング関連の書籍は値段が結構しますので、それらが無料で読めるのはかなりお得なサービスです。
30日無料体験もありますので気に入らなければその期間に解約すれば料金は発生しません。
通常のUnity本
kindle unlimitedは全部のUnity関連書籍が読めるわけではありません。
通常に購入しなければ読めない本もありますので、その中から私が参考になった書籍を紹介します。
Udemy
Udemyの動画講座にもUnity関連のオススメの動画がたくさんあります。
その中でも私が特に気に入っている動画を紹介します。
どの動画も初心者でも完成することが出来るように丁寧に解説しています。
オンラインで学習
質問が出来るおすすめのUnity学習方法ページです。
・Unityでゲーム作りたいけど何から初めたら良いかわからない。
・独学でゲームを完成させられるか不安。
・ゲーム開発でわからないことを一人で解消できない。
・入門書を一通りやってみたが次に何していいかわからない。
こんな人におすすめしたい内容になっています。
最後までご覧頂いてありがとうございました。
はじめまして。
どうか教えていただきたいのです。
1~3ページ目まではすべて行いうまくいきましたが、Input.GetKeyDownでキーボードでPlayerを動かすのではなく、スマートフォンで動かしたいためButtonでPlayerを動かしたいです。
そうするとPlayerがうまく動きません。currentPosがうまく取得できないみたいです。
Buttonで動かす方法を教えていただけませんか?
よろしくお願いいたします。
コメントありがとうございます。
ボタン操作の場合ですが、一例を書いてみます。
まずcanvasにbuttonsという空のゲームオブジェクトを配置して、上右下左のボタンを配置します。
つづいてMapGeneratorに
public GameObject buttons;
と変数を用意します。
続いてPlayerスクリプトを変更していきます。
ボタンを操るのでusing UnityEngine.UI;を追記します。
次にButtonの配列を用意します。
私の場合はcursorsという変数名にしました。
次にStart関数内で
cursors = mapGenerator.buttons.GetComponentsInChildren<Button>();
としてmapGeneratorのbuttons変数から子要素のButtonをすべて取得します。
その次にそれぞれのボタンのonClick時にAddListenerで処理を割り当てます。
私の場合はこんな感じに書きました。
cursors[(int)DIRECTION.UP].onClick.AddListener(() => MoveForward());
cursors[(int)DIRECTION.RIGHT].onClick.AddListener(() => TurnRight());
cursors[(int)DIRECTION.DOWN].onClick.AddListener(() => TurnBack());
cursors[(int)DIRECTION.LEFT].onClick.AddListener(() => TurnLeft());
そして、それぞれの関数を書いていきます。
内容はUpdate関数内に書いてあるキーを入力したものを書いていく感じです。
void MoveForward()
{
mapGenerator.ResetView3D();
_move();
_getMapPositions();
}
void TurnRight()
{
mapGenerator.ResetView3D();
direction++;
_setDirection();
_viewArrow();
_getMapPositions();
}
void TurnBack()
{
mapGenerator.ResetView3D();
direction += 2;
_setDirection();
_viewArrow();
_getMapPositions();
}
void TurnLeft()
{
mapGenerator.ResetView3D();
direction–;
_setDirection();
_viewArrow();
_getMapPositions();
}
少し長くなりましたがこんな感じで動くのではないでしょうか!