적 캐릭터를 하나 생성하고 MonsterController를 붙여주었다.
따라서 공통된 부분은 부모클래스로 올려주는 등 코드 정리를 수행하였다.
보통 초보들이 개발을 할 때 플레이어의 상태를 bool 변수를 선언하는 경향이 강하다. 현재 이동하는 중인지, 점프 상태인지, 시네마틱 영상을 보고 있는 중인지... 모든 상태를 bool 변수로 선언해서 관리하면 나중에 스파게티 코드가 될 확률이 높다.
public enum CreatureState
{
Idle,
Moving,
Skill,
Dead,
}
따라서 CreatureState enum 타입에 가능한 모든 상태를 정의해두고 CreatureController에 선언해두었다. 기존에 사용하던 _isMoving 변수도 삭제했다.
State 또는 Direction이 바뀔 때 State와 Dir 각각에서 애니메이션 처리해주는 코드를 넣지 말고 공동으로 쓸 수 있는 UpdateAnimation() 함수를 하나 만들어주었다.
protected virtual void UpdateAnimation()
{
if(_state == CreatureState.Idle)
{
switch(_lastDir)
{
case MoveDir.Up:
_animator.Play("IDLE_BACK");
_sprite.flipX = false;
break;
case MoveDir.Down:
_animator.Play("IDLE_FRONT");
_sprite.flipX = false;
break;
case MoveDir.Left:
_animator.Play("IDLE_RIGHT");
_sprite.flipX = true;
break;
case MoveDir.Right:
_animator.Play("IDLE_RIGHT");
_sprite.flipX = false;
break;
}
}
else if(_state == CreatureState.Moving)
{
switch (_dir)
{
case MoveDir.Up:
_animator.Play("WALK_BACK");
_sprite.flipX = false;
break;
case MoveDir.Down:
_animator.Play("WALK_FRONT");
_sprite.flipX = false;
break;
case MoveDir.Left:
_animator.Play("WALK_RIGHT");
_sprite.flipX = true;
break;
case MoveDir.Right:
_animator.Play("WALK_RIGHT");
_sprite.flipX = false;
break;
}
}
else if(_state == CreatureState.Skill)
{
// TODO
}
else
{
// 죽는 모션
}
}
public class CreatureController : MonoBehaviour
{
public float _speed = 5.0f;
protected Vector3Int _cellPos = Vector3Int.zero;
protected Animator _animator;
protected SpriteRenderer _sprite;
CreatureState _state = CreatureState.Idle;
public CreatureState State
{
get { return _state; }
set
{
if (_state == value)
return;
_state = value;
UpdateAnimation();
}
}
MoveDir _dir = MoveDir.Down;
MoveDir _lastDir = MoveDir.Down;
public MoveDir Dir
{
get { return _dir; }
set
{
if (_dir == value)
return;
_dir = value;
if (value != MoveDir.None)
_lastDir = value; // Idle일때 dir 방향 정해주는 용도
UpdateAnimation();
}
}
protected virtual void UpdateAnimation()
{
if(_state == CreatureState.Idle)
{
switch(_lastDir)
{
case MoveDir.Up:
_animator.Play("IDLE_BACK");
_sprite.flipX = false;
break;
case MoveDir.Down:
_animator.Play("IDLE_FRONT");
_sprite.flipX = false;
break;
case MoveDir.Left:
_animator.Play("IDLE_RIGHT");
_sprite.flipX = true;
break;
case MoveDir.Right:
_animator.Play("IDLE_RIGHT");
_sprite.flipX = false;
break;
}
}
else if(_state == CreatureState.Moving)
{
switch (_dir)
{
case MoveDir.Up:
_animator.Play("WALK_BACK");
_sprite.flipX = false;
break;
case MoveDir.Down:
_animator.Play("WALK_FRONT");
_sprite.flipX = false;
break;
case MoveDir.Left:
_animator.Play("WALK_RIGHT");
_sprite.flipX = true;
break;
case MoveDir.Right:
_animator.Play("WALK_RIGHT");
_sprite.flipX = false;
break;
}
}
else if(_state == CreatureState.Skill)
{
// TODO
}
else
{
// 죽는 모션
}
}
void Start()
{
Init();
}
void Update()
{
UpdateController();
}
protected virtual void Init()
{
_animator = GetComponent<Animator>();
_sprite = GetComponent<SpriteRenderer>();
Vector3 pos = Managers.Map.CurrentGrid.CellToWorld(_cellPos) + new Vector3(0.5f, 0.5f);
transform.position = pos;
}
protected virtual void UpdateController()
{
UpdatePosition();
UpdateMoving();
}
void UpdatePosition() // 실질적으론 이동했지만 클라 상에서만 스르륵 이동하는 것처럼 보이도록
{
if (State != CreatureState.Moving)
return;
Vector3 destPos = Managers.Map.CurrentGrid.CellToWorld(_cellPos) + new Vector3(0.5f, 0.5f);
Vector3 moveDir = destPos - transform.position;
// 도착 여부 체크
float dist = moveDir.magnitude; // 거리가 얼마나 남았냐
if (dist < _speed * Time.deltaTime)
{
transform.position = destPos;
_state = CreatureState.Idle; // UpdateAnimation() 호출 안함
if(_dir == MoveDir.None) // 키보드에서 손을 뗐으면
UpdateAnimation();
}
else
{
transform.position += moveDir.normalized * _speed * Time.deltaTime;
State = CreatureState.Moving;
}
}
void UpdateMoving()
{
if (State == CreatureState.Idle && _dir != MoveDir.None) // 유닛 한칸 이동 끝날때까지 입력 못받게
{
Vector3Int destPos = _cellPos;
switch (Dir)
{
case MoveDir.Up:
destPos += Vector3Int.up;
break;
case MoveDir.Down:
destPos += Vector3Int.down;
break;
case MoveDir.Left:
destPos += Vector3Int.left;
break;
case MoveDir.Right:
destPos += Vector3Int.right;
break;
}
if (Managers.Map.CanGo(destPos))
{
_cellPos = destPos;
State = CreatureState.Moving;
}
}
}
}
public class MonsterController : CreatureController
{
protected override void Init()
{
base.Init();
}
protected override void UpdateController()
{
//GetDirInput();
base.UpdateController();
}
// 추후 AI에서 필요
void GetDirInput()
{
if (Input.GetKey(KeyCode.W))
{
Dir = MoveDir.Up;
}
else if (Input.GetKey(KeyCode.S))
{
Dir = MoveDir.Down;
}
else if (Input.GetKey(KeyCode.A))
{
Dir = MoveDir.Left;
}
else if (Input.GetKey(KeyCode.D))
{
Dir = MoveDir.Right;
}
else
{
Dir = MoveDir.None;
}
}
}
public class PlayerController : CreatureController
{
protected override void Init()
{
base.Init();
}
protected override void UpdateController()
{
GetDirInput();
base.UpdateController();
}
void LateUpdate()
{
Camera.main.transform.position = new Vector3(transform.position.x, transform.position.y, -10);
}
void GetDirInput()
{
if (Input.GetKey(KeyCode.W))
{
Dir = MoveDir.Up;
}
else if (Input.GetKey(KeyCode.S))
{
Dir = MoveDir.Down;
}
else if (Input.GetKey(KeyCode.A))
{
Dir = MoveDir.Left;
}
else if (Input.GetKey(KeyCode.D))
{
Dir = MoveDir.Right;
}
else
{
Dir = MoveDir.None;
}
}
}