
(#02)UnityでタクティスRPGが作りたい!! - プレイヤー移動編 -
どうもみなさん!今回はプレイヤー移動編やっていきます。
前回から結構プログラムを書き加えました。
Playerクラスを作成
まずは、個々のPlayerが持っている情報を作成します。
説明するところはないですかね。
using UnityEngine;
public class Player : MonoBehaviour
{
//スポーン位置
public Vector3 SpawnPos;
/// <summary>
/// 指定した位置に移動
/// </summary>
/// <param name="pos"></param>
public void MovePostion(Vector2Int pos)
{
transform.position = new Vector3Int(pos.x, pos.y, 0);
}
}
Tileクラスの付け足し
Tileクラスの付け足しをします。
タイルを選択するシステムを作るため、座標データとそこにプレイヤーがいるのかを判定するために、キャラクター情報を変数として管理します。
次に、タイルがクリックされたときに選択状態表すためにセレクト用のオブジェクトをタイルPrefabの子要素に入れて表示・非表示を行います。
using UnityEngine;
public class Tile : MonoBehaviour
{
//** 付け足したもの ↓↓↓ **//
//タイルの座標
[HideInInspector] public Vector2Int TilePos;
//タイルにいるキャラクターの情報
[HideInInspector] public Player PlayerInfo = null;
//セレクト用タイル
[SerializeField] private GameObject _selectTile;
//セレクトタイルを表示
public void ShowSelectTile()
{
_selectTile.SetActive(true);
}
//セレクトタイルを非表示
public void HideSelectTile()
{
_selectTile.SetActive(false);
}
}
TileManagerクラスの書き加え&修正
前回作成した、TileManagerクラスを書き加えます。
結構がらりと変わっています。
前回からの差分のみをコードにしています。
Init関数の部分は『Start文』の中身を書いて、Start文は消しました。
付け足しもしています。
using UnityEngine;
public class TileManager : MonoBehaviour
{
//** 付け足したもの ↓↓↓ **//
/// <summary>
/// 初期処理
/// </summary>
public void Init()
{
//タイルデータ格納用配列の初期化
tileDatas = new Tile[WIDTH, HEIGHT];
for (int i = 0; i < WIDTH; i++)
{
for (int j = 0; j < HEIGHT; j++)
{
//タイルのスポーン位置を計算
Vector3Int pos = new Vector3Int(i - WIDTH / 2, j - HEIGHT / 2, 0);
//タイルをランダムに選択
Tile SelectTile = GetRandomTile();
//タイルをスポーン
var tile = Instantiate(SelectTile, pos, Quaternion.identity, _tileParent);
//** 付け足したもの ↓↓↓ **//
//タイルに現在の座標を記録
tile.TilePos.x = pos.x;
tile.TilePos.y = pos.y;
//スポーンされたタイルを配列に格納
tileDatas[i, j] = tile;
}
}
}
//** 付け足したもの ↓↓↓ **//
/// <summary>
/// 選択した座標のタイルを選択状態にする
/// </summary>
/// <param name="pos"></param>
public void ShowSelectTileFromPosition(Vector2Int pos)
{
//すべてのセレクト状態を外す
AllHideSelectTile();
for (int i = 0; i < WIDTH; i++)
{
for (int j = 0; j < HEIGHT; j++)
{
if (tileDatas[i, j].TilePos == pos)
{
tileDatas[i, j].ShowSelectTile();
break;
}
}
}
}
//** 付け足したもの ↓↓↓ **//
/// <summary>
/// すべてのセレクト状態を外す
/// </summary>
public void AllHideSelectTile()
{
for (int i = 0; i < WIDTH; i++)
{
for (int j = 0; j < HEIGHT; j++)
{
tileDatas[i,j].HideSelectTile();
}
}
}
//** 付け足したもの ↓↓↓ **//
/// <summary>
/// 2次元座標からタイル情報取得
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
public Tile GetTileInfoFromPosition(Vector2Int pos)
{
for(int i = 0; i < WIDTH; i++)
{
for( int j = 0; j < HEIGHT; j++)
{
if (tileDatas[i,j].TilePos == pos) return tileDatas[i,j];
}
}
return null;
}
}
PlayerManagerクラスを作成
ここでは、プレイヤーを管理するため、
プレイヤーをスポーンさせます。
初期化処理では、『GameDirector.Instance』が出てきてますが、この次に紹介します。
using UnityEngine;
public class PlayerManager : MonoBehaviour
{
//タイルマネージャー
private TileManager _tileManager;
//プレイヤーを格納する配列
[SerializeField] private Player[] players = new Player[3];
//プレイヤーデータを格納する配列
public Player[] PlayerDatas = new Player[3];
/// <summary>
/// 初期化処理
/// </summary>
public void Init()
{
_tileManager = GetComponent<TileManager>();
for (int i = 0; i < players.Length; i++)
{
//プレイヤーをスポーン
var player = Instantiate(players[i], players[i].SpawnPos, Quaternion.identity);
//プレイヤーの位置とタイルの位置が一致したタイルを取得
Vector2Int playerPos = GameDirector.Instance.ConvertVector3ToVector2Int(player.SpawnPos);
var tile = _tileManager.GetTileInfoFromPosition(playerPos);
//取得しタイルのPlayerInfoにプレイヤーを格納
tile.PlayerInfo = player;
//プレイヤーデータ配列に格納
PlayerDatas[i] = player;
}
}
}
GameDirectorクラスを作成
ここが本題ですね!
ゲームのすべてを統括するクラスGameDirectorクラスでは、
クリックしたタイルを選択状態にして、そのタイルにプレイヤーがいる場合は、ターンを変更して、プレイヤーを指定したタイルに移動するところまで
書き込みました。以下の通りです。
using UnityEngine;
public class GameDirector : MonoBehaviour
{
//タイルマネージャー
private TileManager _tileManager;
//プレイヤーマネージャー
private PlayerManager _playerManager;
//選択中のキャラクター
Player SelectedPlayer = null;
//ゲームステート
public enum GameState
{
PlayerTurn_Begin,
PlayerTurn_MoveTileSelect,
PlayerTurn_CommandSelect,
PlayerTurn_Attack,
PlayerTurn_End
}
GameState currentGameState = GameState.PlayerTurn_Begin;
/// <summary>
/// シングルトン
/// </summary>
public static GameDirector Instance { get; private set; }
private void Awake()
{
if(Instance == null) Instance = this;
}
private void Start()
{
_tileManager = GetComponent<TileManager>();
_playerManager = GetComponent<PlayerManager>();
//** 初期化 **//
//タイルを生成
_tileManager.Init();
//プレイヤーを配置
_playerManager.Init();
}
private void Update()
{
//タイルがクリックされたら
if(Input.GetMouseButtonDown(0))
{
//クリックした座標をワールド座標に変換
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
//マウス座標をVector2Intに変換
Vector2Int targetPos = ConvertVector3ToVector2Int(mousePos);
switch(currentGameState)
{
case GameState.PlayerTurn_Begin:
//タイルを選択状態にする
_tileManager.ShowSelectTileFromPosition(targetPos);
//選択しタイルにプレイヤーがいるなら
if (SelectedPlayer = _tileManager.GetTileInfoFromPosition(targetPos).PlayerInfo)
{
//ステート変更
ChangeState(GameState.PlayerTurn_MoveTileSelect);
}
break;
case GameState.PlayerTurn_MoveTileSelect:
//選択した位置に移動
if(SelectedPlayer != null && _tileManager.GetTileInfoFromPosition(targetPos).IsMovable)
{
//セレクトタイルを外す
_tileManager.AllHideSelectTile();
//移動
SelectedPlayer.MovePostion(targetPos);
//ステート変更
ChangeState(GameState.PlayerTurn_CommandSelect);
}
break;
}
}
}
/// <summary>
/// ゲームステートを変更
/// </summary>
/// <param name="state"></param>
void ChangeState(GameState state)
{
currentGameState = state;
}
/// <summary>
/// Vector3型からVector2Intに変換
/// </summary>
/// <param name="vector"></param>
/// <returns></returns>
public Vector2Int ConvertVector3ToVector2Int(Vector3 vector)
{
Vector2Int returnVector2 = new Vector2Int(0,0);
returnVector2.x = Mathf.RoundToInt(vector.x);
returnVector2.y = Mathf.RoundToInt(vector.y);
return returnVector2;
}
}
完成動画
完成動画はXにてアップロードしておりますので、お手間おかけしますがよろしお願いいたします。そちらの動画ではちゃんと動作しています。
文章で説明しますと、タイルをクリックすると選択状態になり、プレイヤーをクリックしてほかのタイルをクリックするとそこに移動するようになっております。後、移動不可能タイルには移動できなようになっております。
最後に
今回は、前回よりもコードを書きました。
今回のプログラムで成長した点は、列挙型を使用して現在のゲーム状態を把握できるようにしたことやVector3からVector2Intに変換する処理を書けるようになったことですね。
全体のソースコード公開
Tile.cs
using UnityEngine;
public class Tile : MonoBehaviour
{
//移動可能判定用
public bool IsMovable;
//タイルのID (使うかわからん)
public int ID;
//タイルのタイプ
public TILETYPE TileType;
//タイルの座標
[HideInInspector] public Vector2Int TilePos;
//タイルにいるキャラクターの情報
[HideInInspector] public Player PlayerInfo = null;
//セレクト用タイル
[SerializeField] private GameObject _selectTile;
//セレクトタイルを表示
public void ShowSelectTile()
{
_selectTile.SetActive(true);
}
//セレクトタイルを非表示
public void HideSelectTile()
{
_selectTile.SetActive(false);
}
}
public enum TILETYPE
{
MOVABLETILE,
UNMOVABLETILE
}
Player.cs
using UnityEngine;
public class Player : MonoBehaviour
{
//スポーン位置
public Vector3 SpawnPos;
/// <summary>
/// 指定した位置に移動
/// </summary>
/// <param name="pos"></param>
public void MovePostion(Vector2Int pos)
{
transform.position = new Vector3Int(pos.x, pos.y, 0);
}
}
TileManager.cs
using UnityEngine;
public class TileManager : MonoBehaviour
{
//タイル配列
[SerializeField] private Tile[] tiles;
//タイルの親オブジェクト
[SerializeField] private Transform _tileParent;
//タイルを格納する2次元配列
[HideInInspector] public Tile[,] tileDatas;
//タイルの横幅
public const int WIDTH = 15;
//タイルの縦幅
public const int HEIGHT = 9;
/// <summary>
/// 初期処理
/// </summary>
public void Init()
{
//タイルデータ格納用配列の初期化
tileDatas = new Tile[WIDTH, HEIGHT];
for (int i = 0; i < WIDTH; i++)
{
for (int j = 0; j < HEIGHT; j++)
{
//タイルのスポーン位置を計算
Vector3Int pos = new Vector3Int(i - WIDTH / 2, j - HEIGHT / 2, 0);
//タイルをランダムに選択
Tile SelectTile = GetRandomTile();
//タイルをスポーン
var tile = Instantiate(SelectTile, pos, Quaternion.identity, _tileParent);
//タイルに現在の座標を記録
tile.TilePos.x = pos.x;
tile.TilePos.y = pos.y;
//スポーンされたタイルを配列に格納
tileDatas[i, j] = tile;
}
}
}
/// <summary>
/// 選択した座標のタイルを選択状態にする
/// </summary>
/// <param name="pos"></param>
public void ShowSelectTileFromPosition(Vector2Int pos)
{
//すべてのセレクト状態を外す
AllHideSelectTile();
for (int i = 0; i < WIDTH; i++)
{
for (int j = 0; j < HEIGHT; j++)
{
if (tileDatas[i, j].TilePos == pos)
{
tileDatas[i, j].ShowSelectTile();
break;
}
}
}
}
/// <summary>
/// すべてのセレクト状態を外す
/// </summary>
public void AllHideSelectTile()
{
for (int i = 0; i < WIDTH; i++)
{
for (int j = 0; j < HEIGHT; j++)
{
tileDatas[i,j].HideSelectTile();
}
}
}
/// <summary>
/// 2次元座標からタイル情報取得
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
public Tile GetTileInfoFromPosition(Vector2Int pos)
{
for(int i = 0; i < WIDTH; i++)
{
for( int j = 0; j < HEIGHT; j++)
{
if (tileDatas[i,j].TilePos == pos) return tileDatas[i,j];
}
}
return null;
}
/// <summary>
/// TYPEからタイルデータを取得
/// </summary>
/// <param name="tileType"></param>
/// <returns></returns>
public Tile GetTileByType(TILETYPE tileType)
{
foreach(Tile tile in tiles)
{
if(tile.TileType == tileType) return tile;
}
return null;
}
/// <summary>
/// ランダムにタイルを取得
/// </summary>
/// <returns></returns>
private Tile GetRandomTile()
{
int rnd = Random.Range(0, 100);
switch(rnd)
{
case < 10:
return GetTileByType(TILETYPE.UNMOVABLETILE);
case < 100:
return GetTileByType(TILETYPE.MOVABLETILE);
}
return null;
}
}
PlayerManager.cs
using UnityEngine;
public class PlayerManager : MonoBehaviour
{
//タイルマネージャー
private TileManager _tileManager;
//プレイヤーを格納する配列
[SerializeField] private Player[] players = new Player[3];
//プレイヤーデータを格納する配列
public Player[] PlayerDatas = new Player[3];
/// <summary>
/// 初期化処理
/// </summary>
public void Init()
{
_tileManager = GetComponent<TileManager>();
for (int i = 0; i < players.Length; i++)
{
//プレイヤーをスポーン
var player = Instantiate(players[i], players[i].SpawnPos, Quaternion.identity);
//プレイヤーの位置とタイルの位置が一致したタイルを取得
Vector2Int playerPos = GameDirector.Instance.ConvertVector3ToVector2Int(player.SpawnPos);
var tile = _tileManager.GetTileInfoFromPosition(playerPos);
//取得しタイルのPlayerInfoにプレイヤーを格納
tile.PlayerInfo = player;
//プレイヤーデータ配列に格納
PlayerDatas[i] = player;
}
}
}
GameDirector.cs
using UnityEngine;
public class GameDirector : MonoBehaviour
{
//タイルマネージャー
private TileManager _tileManager;
//プレイヤーマネージャー
private PlayerManager _playerManager;
//選択中のキャラクター
Player SelectedPlayer = null;
//ゲームステート
public enum GameState
{
PlayerTurn_Begin,
PlayerTurn_MoveTileSelect,
PlayerTurn_CommandSelect,
PlayerTurn_Attack,
PlayerTurn_End
}
GameState currentGameState = GameState.PlayerTurn_Begin;
/// <summary>
/// シングルトン
/// </summary>
public static GameDirector Instance { get; private set; }
private void Awake()
{
if(Instance == null) Instance = this;
}
private void Start()
{
_tileManager = GetComponent<TileManager>();
_playerManager = GetComponent<PlayerManager>();
//** 初期化 **//
//タイルを生成
_tileManager.Init();
//プレイヤーを配置
_playerManager.Init();
}
private void Update()
{
//タイルがクリックされたら
if(Input.GetMouseButtonDown(0))
{
//クリックした座標をワールド座標に変換
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
//マウス座標をVector2Intに変換
Vector2Int targetPos = ConvertVector3ToVector2Int(mousePos);
switch(currentGameState)
{
case GameState.PlayerTurn_Begin:
//タイルを選択状態にする
_tileManager.ShowSelectTileFromPosition(targetPos);
//選択しタイルにプレイヤーがいるなら
if (SelectedPlayer = _tileManager.GetTileInfoFromPosition(targetPos).PlayerInfo)
{
//ステート変更
ChangeState(GameState.PlayerTurn_MoveTileSelect);
}
break;
case GameState.PlayerTurn_MoveTileSelect:
//選択した位置に移動
if(SelectedPlayer != null && _tileManager.GetTileInfoFromPosition(targetPos).IsMovable)
{
//セレクトタイルを外す
_tileManager.AllHideSelectTile();
//移動
SelectedPlayer.MovePostion(targetPos);
//ステート変更
ChangeState(GameState.PlayerTurn_CommandSelect);
}
break;
}
}
}
/// <summary>
/// ゲームステートを変更
/// </summary>
/// <param name="state"></param>
void ChangeState(GameState state)
{
currentGameState = state;
}
/// <summary>
/// Vector3型からVector2Intに変換
/// </summary>
/// <param name="vector"></param>
/// <returns></returns>
public Vector2Int ConvertVector3ToVector2Int(Vector3 vector)
{
Vector2Int returnVector2 = new Vector2Int(0,0);
returnVector2.x = Mathf.RoundToInt(vector.x);
returnVector2.y = Mathf.RoundToInt(vector.y);
return returnVector2;
}
}