
우선 저번에 만든거 null 체크를 추가 하고 진행해야 할듯 싶다. 모르고 타일 프리팹을 지워버렸더니 새로 만들었는데 오류가 뜨더라. AddComponent로 추가할수 있는듯 싶다.
void MakeNode (int x, int y)
{
var worldPos = new Vector3 (x * interval, 0, y * interval);
var node = Instantiate<GameObject> (tile, worldPos , tile.transform.rotation);
node.transform.parent = this.transform;
node.name = "Node : (" + x + ", " +y + ")";
var getNode = node.GetComponent<TileNode>();
if (getNode == null)
{
node.AddComponent<TileNode>();
getNode = node.GetComponent<TileNode>();
}
getNode.Coordinate = new Vector2 (x, y);
if (Physics.CheckSphere (node.transform.position, nodeRadius, LayerMask.GetMask ("Obstacle", "Player", "Enemy")))
{
getNode.Walkable = false;
}
}
구현해야 할게 좀 많긴하다. 장애물 방어도 구현해야 하고 그런데 일단은 적이 아군 유닛을 인지하지 않았을 때는 그냥 이동을 하고 인지했을 때는 공격 모드로 면하는 상태 머신을 구현해야 할듯 싶다. 약어로 FSM 이라고 하는듯.
이걸 구현하는 방법으로 유니티 애니매이션을 활용하는 방법이 있긴 한데. 이번에는 코드로 제어하는 방법을 쓸 생각이다. 그러기 위해서는 객체지향의 패턴중에 상태패턴에 대해서 알아봐야 겠다.
그 게임 머시기 보라색 책을 예전에 사두긴 했는데. 막상 필요해서 찾을려고 하니까 안보인다.
http://boycoding.tistory.com/110
이걸 보면서 공부해야 겠다. C++ 이라 감이 잘 안오는것도 있긴하다. 일단 enum을 활용하는것은 턴 상태를 정의할때 해본적이 있으니 그때 경험을 살려서 만들어 봐야 겠다.
일단은 테스트니 인지 센서를 좁게해서 바로 근처에 있어서 대략 2칸 정도 밖이면 아직 만나지 않은걸로 해야 겠다. 일단 저번에 랜덤으로 공격하게 만든 코드를 삭제 해야 할듯 싶다. 2칸 이내에 플레이어 유닛이 보이지 않으면 그냥 이동을 하고 센서가 플레이어 유닛들을 인지를 하면 콘솔창에 메세지들 호출하게 해야 할듯 싶다.
오래간만에 보는 거라. 저번에 코드를 어떤 구조로 짰었는지 가물 가물하다. 이거 팀원을 구해서 모델링이나 코딩이나 하나를 전담해야 하는게 아닌가 싶기도 하네.
이거 저번에 만들어 놓은 랜덤하게 공격하는 걸 지워야 하는데. 어딧지.
...
...
...
게임 메니저에 있었네. test 어쩌고로 해 두었다. 그래서 여기다 해 두었나 보네. 에너미 클레스로 가서 작업을 해야 겠다.
void CheckPlayerTurn (List<Player> waitingPlayerList)
{
if (waitingPlayerList.Count != 0)
{
currentPlayer = waitingPlayerList[0];
SelectPlayer (currentPlayer);
return;
}
else
{
currentPlayer = null;
turnState = Turn.EnemyTurn;
print("EnemyTurn");
// TestEnemyAI();
return;
}
플레이어 맴버가 다 행동력을 소진했는데 에너미 턴으로 넘어가지 않는 버그가 있고 또 플레이어 이동 관련해서 움직일때 또 클릭을 하면 엉뚱한데로 이동하는 버그 또한 있다. 일단 본격적인 작업에 앞서서 이런 것들에 대한 마무리가 좀 필요할듯 싶다.
일단 우선 클릭시 생기는 버그를 잡아야 겠다. 이전엔 콜라이더를 켜고 끄는 걸로 문제를 해결 했었는데 그게 또다른 버그를 야기하고 캐릭터를 선택하고 명령을 내리고 바로 타 캐릭터를 선택해서 또 이동을 내려서 거의 동시에 이동하게 하는 엑스컴의 시간절약 팁을 사용할수가 없다는 거다.
일단 지금 이동이 끝이 나야 좌표를 이동하도록 해놯는데 먼저 도착지점의 좌표를 옴기게 해야 겠다. 그래야 타 캐릭터를 클릭했을때도 중복해서 클릭을 하는 우를 범하지 안게 할수 있기 때문이다.
void Awake()
{
player = GetComponent<Player>();
board = FindObjectOfType<Board>();
gameManager = FindObjectOfType<GameManager>();
}
public void MoveUnit (Vector3 destinationPos, float delayTime = 0f)
{
StartCoroutine (MoveRoutine (destinationPos, delayTime));
}
public List<Vector3> MakeTransform (List<TileNode> way){
var transfromList = new List<Vector3>();
foreach (var node in way)
{
transfromList.Add (GameUtility.CoordinateToTransfom (node.Coordinate));
}
return transfromList;
}
// start
public void IndicateUnit (List<TileNode> way)
{
way.Reverse();
StartCoroutine (Indicator (MakeTransform (way)));
}
IEnumerator Indicator (List<Vector3> way) {
// start
// foreach (var player in gameManager.playerList)
// {
// player.GetComponent<CapsuleCollider>().enabled = false;
// }
yield return null;
if (way == null)
{
print ("No Way");
}
// do
foreach (var destination in way)
{
MoveUnit (destination);
while (isMoving == true) {
yield return null;
}
}
yield return null;
// end
// foreach (var player in gameManager.playerList)
// {
// player.GetComponent<CapsuleCollider>().enabled = true;
// }
board.ResetBoard();
// player.activeOff.Invoke();
isMoving = false;
}
지금 현재는 이동이 다 끝나면 보드를 리셋하는 방식으로 코딩이 되어 있다. 우선 쉽게 생각할 수 있는 방식이 일단 클릭을 하면 거기에 보이지 않은 오브젝트를 생성을 하고 맵을 리셋 해서 중복 클릭 막는거다. 그리고 이동이 끝나면 그 보이지 않는 오브젝트를 삭제 하는거. 구지 보이지 않을 필요가 없고 아에 깃발을 생성해서 거기로 갈꺼라고 알려주는 것도 나쁘지 않는 방식인듯 싶다. 가령 뭉처야 하는 경우는 그 근처에 클릭을 해야 하니 말이다. 플레이어에게 나름의 도움을 주는 인터페이스 인듯 싶다. 일단은 실물과 다르지 않는 오브젝트로 하고 나중에 쉐이더를 적용해서 인터페이스 느낌이 나도록 바꿔야 할듯 싶다.
간단하게 블랜더로 깃발를 만들어서 추가했다. 좀더 멋진 디자인은 추후에 고민해 봐야 할듯 싶다.
심플한게 괜찮은듯 싶다. 나중에 쉐이더를 입히면 그럴듯 할듯 싶다. 플레이어 무브 클래스로 가서 손을 봐준다.
IEnumerator Indicator (List<Vector3> way) {
// start
var destinationFlag = Instantiate (destinationPoint, way[way.Count -1], Quaternion.identity);
board.ResetBoard();
yield return null;
if (way == null)
{
print ("No Way");
}
// do
foreach (var destination in way)
{
MoveUnit (destination);
while (isMoving == true) {
yield return null;
}
}
yield return null;
// end
DestroyImmediate (destinationFlag);
board.ResetBoard();
// player.activeOff.Invoke();
isMoving = false;
}
나중에 이동경로를 알려주는 걸 만들때도 응용을 할수 있을듯 하다. 이렇게 해놓으면 똑같은 자리를 중복해서 클릭하는 걸 막아주는 효과가 있다.
문제가 하나 있는데 이동을 하고 나서 타 캐릭터가 이제는 비게된 자리로 오게 되는걸 막는다는 거다. 일단 일시적으로 콜라이더를 꺼야 되지 않을까 싶다.
IEnumerator Indicator (List<Vector3> way) {
// start
var destinationFlag = Instantiate (destinationPoint, way[way.Count -1], Quaternion.identity);
player.GetComponent<CapsuleCollider>().enabled = false;
board.ResetBoard();
yield return null;
if (way == null)
{
print ("No Way");
}
// do
foreach (var destination in way)
{
MoveUnit (destination);
while (isMoving == true) {
yield return null;
}
}
yield return null;
// end
DestroyImmediate (destinationFlag);
player.GetComponent<CapsuleCollider>().enabled = true;
board.ResetBoard();
// player.activeOff.Invoke();
isMoving = false;
}
해당 문제는 깔끔하게 해결됬다. 이제 필요한건 이동중에 다른 타일을 클릭을 하면 이동이 꼬이게 되는 버그를 해결해야 할듯 싶다. 이걸 어떻게 해결해야 할지 바로 해법이 안떠오른다. 아마도 예전에 코드구조가 어땠는지 까먹었기 때문이 아닌가 싶다..
그게 이동하는 동안에는 타일 클릭을 막아야 한다. 또 그런식으로 원천 봉새를 해놓으면 두번째 캐릭이 바닥타일을 클릭하지 못할까 걱정이 된다. 상태를 만들어서 무빙 상태인 경우에만 바닥 타일을 클릭하지 못하게 해야 할듯 싶다.
지금 이전에 만들어 놓은 상태가 대기 활성화 종료인데. 활성화 상태를 좀더 세부적으로 만들어 놓아야 할듯 싶다.
플레이어 클래스에 일단 이걸 추가해 준다.
public enum PlayerActiveState
{
Ready, Moving
}
public PlayerActiveState playerActiveState = PlayerActiveState.Ready;
플레이어 무버에서 여기에 상태를 바꿔주는 작업을 해주고 바닥타일을 클릭했을때 대기 상대인지 움직이고 있는지 확인을 해서 대기 상태에서만 타일이 활성화 되도록 하면 되지 않을까 싶다. 생각하는 대로 되는지 확인을 해봐야 할듯 싶다.
일단 코드를 살펴보니 isMoving이라는 Bool을 이전에 만들어 둔거 같다. 이 자리에 넣으면 될듯 하다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour {
public Vector3 destination;
public GameObject destinationPoint;
public EaseType easetype;
public float moveSpeed = 3f;
public float turnSpeed = 1f;
public float iTweenDelay = 0f;
public bool isMoving = false;
Board board;
Player player;
GameManager gameManager;
void Awake()
{
player = GetComponent<Player>();
board = FindObjectOfType<Board>();
gameManager = FindObjectOfType<GameManager>();
}
public void MoveUnit (Vector3 destinationPos, float delayTime = 0f)
{
StartCoroutine (MoveRoutine (destinationPos, delayTime));
}
public List<Vector3> MakeTransform (List<TileNode> way){
var transfromList = new List<Vector3>();
foreach (var node in way)
{
transfromList.Add (GameUtility.CoordinateToTransfom (node.Coordinate));
}
return transfromList;
}
// start
public void IndicateUnit (List<TileNode> way)
{
way.Reverse();
StartCoroutine (Indicator (MakeTransform (way)));
}
IEnumerator Indicator (List<Vector3> way) {
// start
var destinationFlag = Instantiate (destinationPoint, way[way.Count -1], Quaternion.identity);
player.GetComponent<CapsuleCollider>().enabled = false;
board.ResetBoard();
yield return null;
if (way == null)
{
print ("No Way");
DestroyImmediate (destinationFlag);
player.GetComponent<CapsuleCollider>().enabled = true;
board.ResetBoard();
player.playerActiveState = PlayerActiveState.Ready;
}
// do
foreach (var destination in way)
{
MoveUnit (destination);
while (player.playerActiveState == PlayerActiveState.Moving) {
yield return null;
}
}
yield return null;
// end
DestroyImmediate (destinationFlag);
player.GetComponent<CapsuleCollider>().enabled = true;
board.ResetBoard();
// player.activeOff.Invoke();
player.playerActiveState = PlayerActiveState.Ready;
}
IEnumerator MoveRoutine (Vector3 destinationPos, float delayTime) {
player.playerActiveState = PlayerActiveState.Moving;
yield return new WaitForSeconds (delayTime);
gameObject.MoveTo(destinationPos, moveSpeed, delayTime, easetype);
gameObject.LookTo(destinationPos, turnSpeed, delayTime, easetype);
while (Vector3.Distance (destinationPos, transform.position) > 0.01f) {
yield return null;
}
iTween.Stop (gameObject);
transform.position = destinationPos;
player.playerActiveState = PlayerActiveState.Ready;
}
}
바꿔 줘야 할게 좀 많아서 통채로 긁어왔다. 그다음으로 타일 노드 클래스에서.
void OnMouseDown()
{
if (gameManager.currentPlayer != null)
{
if (walkable && gameManager.currentPlayer.playerTurnState == PlayerTurnState.Active && gameManager.currentPlayer.currentVigor > 0)
{
if (gameManager.currentPlayer.playerActiveState == PlayerActiveState.Ready)
{
Moving();
}
}
}
}
이렇게 바꿔봤는데 과연 문제 없이 될런지 오랜만에 코딩을 하니 떨린다.
잘된다. 괜한 걱정이었다.
플레이어 이동에 관한 문제를 해결했으니 본격적으로 인공지능을 만들어야 할 차래인듯 싶다. 우선은 에너미 무버 클래스를 만들어야 할듯 싶다. 일단 움직이게 하는 것 부터 구현하고 그다음에 센서를 달아서 플래이어가 근처에 있는지 체크를 하도록 하는 걸 만들어야 할듯 싶다. 그 다음에 상황을 판단해서 적절한 행동을 취하도록 하는게 해결해야 될 문제인듯 싶다.
'턴제제작' 카테고리의 다른 글
엄패 (0) | 2018.09.23 |
---|---|
씬시티 느낌 (0) | 2018.09.07 |
캐릭터의 이동 (0) | 2018.09.04 |
턴제 인디게임 개발 (0) | 2018.08.28 |
프로토 타입 (1) | 2018.07.24 |
WRITTEN BY
,