前回の記事では与えたマップデータからローグライクのダンジョンマップを生成する処理を作りました。
前回の記事:
今回の記事ではダンジョンマップにプレイヤーも生成し、実際にマップの中を移動できるようにしましょう。
プレイヤー用のプレハブを用意する
先にプレイヤーを表すプレハブを作成します。次の手順を行ってください。
- メニューから空のGameObjectを作成し、名前を「PlayerPrefab」にしてください。
- メニューのGameObject > 3DObject > Cylinderをクリックし、GameObjectを作成してください。名前は「Model」にしてください。
- 作成した「Model」は「PlayerPrefab」の子GameObjectにしてください。
- 「Model」の形状は好きなものでOKです。
- ※お好みで好きなマテリアルを設定するのもいいでしょう。
(「Model」は表示するモデルの位置調節に使用します。)
ここまでできたら、シーンの上の「PlayerPrefab」をプレハブ化してください。プレハブ化した後はシーンにある「PlayerPrefab」は削除してください。
マップデータにプレイヤーの位置情報を組み込む
プレハブが作成できたら、マップデータにプレイヤーの位置情報を組み込んでいきます。
組み込む際はシーンの「Map」コンポーネントの「massDataList」フィールドに次のものを追加してください。
1 2 3 4 5 6 |
//プレイヤー prefab = 「PlayerPrefab」プレハブを設定 type = MassType.Player; mapChar = ‘P’; isRoad = false; isCharacter = true; |
マップ上のオブジェクトの基底クラスとなるC#スクリプトの作成
これから実際にプレイヤーの実装に入りたいのですが、その前にマップ上のオブジェクトは同じような処理を行うことが増えるため、それらをまとめるスクリプトを先に作ります。
プレイヤーやこの後の記事で作る敵などは全てこのコンポーネントから派生させて作っていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; class MapObjectBase : MonoBehaviour { [Range(0, 100)] public float MoveSecond = 0.1f; public bool IsNowMoving { get; private set; } = false; public Vector2Int Pos; public Vector2Int PrevPos { get; protected set; } public Direction Forward; Map _map; public Map Map { get => _map != null ? _map : (_map = Object.FindObjectOfType<Map>()); } //位置と前方向を設定するメソッド public void SetPosAndForward(Vector2Int pos, Direction forward) { // PosとForwardの設定し、Transformに反映させる PrevPos = new Vector2Int(-1, -1); Pos = pos; Forward = forward; transform.position = Map.CalcMapPos(Pos); } //移動処理 public virtual void Move(Direction dir) { IsNowMoving = false; var (movedMass, movedPos) = Map.GetMovePos(Pos, dir); if (movedMass == null) return; var massData = Map[movedMass.Type]; if(movedMass.ExistObject) { MoveToExistObject(movedMass, movedPos); } else if (massData.IsRoad) { MoveToRoad(movedMass, movedPos); } else { MoveToNotMoving(movedMass, movedPos); } } protected virtual void MoveToExistObject(Map.Mass mass, Vector2Int movedPos) { StartCoroutine(NotMoveCoroutine(movedPos)); } protected virtual void MoveToRoad(Map.Mass mass, Vector2Int movedPos) { StartCoroutine(MoveCoroutine(movedPos)); } protected virtual void MoveToNotMoving(Map.Mass mass, Vector2Int movedPos) { StartCoroutine(NotMoveCoroutine(movedPos)); } IEnumerator MoveCoroutine(Vector2Int target) { //マップのマス情報を更新する var startMass = Map[Pos.x, Pos.y]; startMass.ExistObject = null; var movedPos = Map.CalcMapPos(target); PrevPos = Pos; Pos = target; var movedMass = Map[Pos.x, Pos.y]; movedMass.ExistObject = gameObject; //モデルの移動処理 IsNowMoving = true; var start = transform.position; var timer = 0f; while(timer < MoveSecond) { yield return null; timer += Time.deltaTime; transform.position = Vector3.Lerp(start, movedPos, timer/MoveSecond); } transform.position = movedPos; IsNowMoving = false; } IEnumerator NotMoveCoroutine(Vector2Int target) { var movedPos = Map.CalcMapPos(target); IsNowMoving = true; var start = transform.position; var timer = 0f; movedPos = Vector3.Lerp(start, movedPos, 0.5f); while (timer < MoveSecond) { yield return null; timer += Time.deltaTime; var t = 1f - Mathf.Abs(timer / MoveSecond * 2 - 1f); transform.position = Vector3.Lerp(start, movedPos, t); } transform.position = start; IsNowMoving = false; } } |
これに合わせて、「Map」スクリプトに以下のメソッドを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public Vector3 CalcMapPos(Vector2Int pos) => CalcMapPos(pos.x, pos.y); public Vector2Int CalcDirection(Direction dir) { switch(dir) { case Direction.North: return new Vector2Int(0, -1); case Direction.South: return new Vector2Int(0, 1); case Direction.East: return new Vector2Int(1, 0); case Direction.West: return new Vector2Int(-1, 0); default: throw new System.NotImplementedException(); } } public (Mass mass, Vector2Int movedPos) GetMovePos(Vector2Int currentPos, Direction moveDir) { var offset = CalcDirection(moveDir); var movedPos = currentPos + offset; if (movedPos.x < 0 || movedPos.y < 0) return (null, currentPos); if (movedPos.y >= MapSize.y) return (null, currentPos); var line = Data[movedPos.y]; if(movedPos.x >= line.Count) return (null, currentPos); var mass = line[movedPos.x]; return (mass, movedPos); } |
さらに、BuildMapメソッドも「MapObjectBase」に対応したものに拡張します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
public void BuildMap(List<string> map) { InitMassData(); var mapSize = Vector2Int.zero; Data = new List<List<Mass>>(); foreach (var line in map) { var lineData = new List<Mass>(); for (var i=0; i<line.Length; ++i) { var ch = line[i]; if (!MapCharDict.ContainsKey(ch)) { Debug.LogWarning("どのマスかわからない文字がマップデータに存在しています。 ch=" + ch); ch = MapCharDict.First().Key; //一応、始めのデータで代用する } var massData = MapCharDict[ch]; var mass = new Mass(); var pos = CalcMapPos(i, Data.Count); if (massData.IsCharacter) { mass.ExistObject = Object.Instantiate(massData.Prefab, transform); var mapObject = mass.ExistObject.GetComponent<MapObjectBase>(); mapObject.SetPosAndForward(new Vector2Int(i, Data.Count), Direction.South); //キャラクターの時は道も一緒に作成する massData = this[MassType.Road]; } mass.Type = massData.Type; mass.MassGameObject = Object.Instantiate(massData.Prefab, transform); mass.MassGameObject.transform.position = pos; mass.ExistObject = null; lineData.Add(mass); } Data.Add(lineData); //マップサイズの設定 mapSize.x = Mathf.Max(mapSize.x, line.Length); mapSize.y++; } MapSize = mapSize; } |
プレイヤーの移動アクションを実装しよう
それでは下準備ができたので、先ほどのスクリプトを継承してプレイヤーを実装していきましょう!
この講座ではプレイヤーは次のアクションを行います。
- 上下左右に移動
また、プレイヤーの移動に合わせてカメラも移動させます。
プレイヤー移動などの制御には「Player」スクリプトを作って使用します。
「Player」スクリプトの内容を次のようにしてください。
まとめ
今回の記事ではローグライクゲームにおけるプレイヤーを作成し、実際にマップ上で移動できるようにしました。
ここで作成した「MapObjectBase」スクリプトは今後キャラクターやイベントを追加するたびに基底クラスとして使用していきます。
今回の記事では以下を行いました。
- マップ上のキャラクター処理をまとめた継承用スクリプトの作成。
- 再生時にマップの開始位置にプレイヤーを登場させるようにした。
- 入力に合わせてプレイヤーをマップ上で移動できるようにした。
それでは次の記事に行ってみましょう。
コメント