[Unity] Attack

이정석·2023년 7월 18일
0

Unity

목록 보기
15/22

Attack

특정 캐릭터가 공격(Attack)이나 스킬(Skill)사용하고자 할 때 여러가지의 입력을 받을 수 있다. 공격하는 커맨드를 입력하고 대상을 마우스로 지정한다거나, 커맨드입력 없이 마우스 클릭만으로 공격을 명령할 수 있다.

마우스로만 공격을 지시한다고 할 때 클릭한 목적이 이동인지 공격인지 어떻게 구분할 수 있을까?

layerMask를 이용해 클릭한 대상의 Layer가 Monster인지 Ground인지 확인하자!

1. 공격방법

공격하는 과정의 구현은 구현하고자 하는 내용에 따라 다를 수 있다.

  1. 공격하고자 하는 대상이 사정거리보다 먼 위치에 있다면?
  2. 한번의 입력에 한번만 공격할 것인가 계속 공격할 것인가?
  3. 공격대상이 움직이거나 마우스커서가 벗어난다면?

어떤 대상을 공격할지에 대한 정보(GameObject)를 가지고 있어야 한다.

GameObject _lockTarget;

2. 상태전환

캐릭터의 상태(Die, Idle, Moving, Skill)에 따라 실행해야 할 애니메이션이 다르다. 이동하거나 스킬, 공격모션을 취할 때마다 특정 애니메이션을 실행하는 것이 아닌 캐릭터의 State를 변경함으로 State에 맞는 애니메이션을 실행하도록 한다.

  Animator anim = GetComponent<Animator>();
  switch(_state)
  {
      case PlayerState.Die:
          break;
      case PlayerState.Idle:
          anim.CrossFade("WAIT", 0.1f);
          break;
      case PlayerState.Moving:
          anim.CrossFade("RUN", 0.1f);
          break;
      case PlayerState.Skill:
          anim.CrossFade("ATTACK", 0.1f, -1, 0);
          break;
  }

Animator Component는 특정 GameObject의 애니메이션을 제어하는데 사용되는 컴포넌트로 위 코드에는 다른 상태로의 부드러운 전환을 위해 CrossFade를 사용했다. 아래 3개의 기능 외에도 여러가지 기능이 존재한다.

  1. Play: 특정 애니메이션을 재생하는 함수로 현재 실행중인 애니메이션이 중지되고 새로운 애니메이션이 실행된다.
  2. CrossFade: 현재 애니메이션 상태에서 다른 애니메이션 상태로 부드럽게 전환하는 함수로 여러 오버로딩을 지원한다.
  3. SetBool, SetFloat, SetInteger: 애니메이션 컨트롤러의 매개변수값을 설정하는 함수로 여기서 설정되는 값에따라 다음 애니메이션을 정할 수 있다.

공격 및 이동

마우스올 공격을 명령하면 공격대상이 있는 위치까지 이동할 필요가 있다. 이 과정은 다음과 같이 나타낼 수 있다.

  1. 사용자가 마우스를 눌렀을 때 누른 대상이 Ground라면 누른 지점을 캐릭터의 이동지점으로 바꾼다.
  2. 사용자가 누른 대상이 Monster라면
    • 누른대상의 Position을 캐릭터의 이동지점으로 한다.
    • 캐릭터의 공격대상을 변경한다.
  3. 캐릭터의 State를 Moving으로 바꿔준다.

1. 마우스 클릭

우선, 마우스가 클릭될 때 가지고 있는 정보는 목표지점과 공격대상이다.

	Vector3 _destPos;
	GameObject _lockTarget;

마우스를 클릭할 때 LayerMask를 이용해 Monster인지 판별한 뒤에 공격대상을 변경한다.

    RaycastHit hit;
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    bool raycastHit = Physics.Raycast(ray, out hit, 100.0f, _mask);
    case Define.MouseEvent.PointerDown:
        {
            if (raycastHit)
            {
                _destPos = hit.point;
                State = PlayerState.Moving;

                if (hit.collider.gameObject.layer == (int)Define.Layer.Monster)
                    _lockTarget = hit.collider.gameObject;
                else
                    _lockTarget = null;
            }
        }

2. 캐릭터 이동 및 공격

기존에 존재했던 캐릭터의 이동은 다음과 같은 단계로 나타낼 수 있으며 아래 단계는 매 프레임마다 적용된다.

  1. 목표지점과 현재위치 사이의 거리가 일정거리보다 크다면, 목표지점까지 Speed만큼 움직인다.
  2. 움직이는 방향을 바라본다.

위 단계에서, 공격대상이 존재한다면 공격상태로 전환되어야 하기 때문에 다음과 같은 단계를 추가한다.

  1. 공격대상이 존재하고 현재위치와 목표지점사이가 공격 사정거리보다 짧다면 공격 State로 전환한다.
  2. 목표지점과 현재위치 사이의 거리가 일정거리보다 크다면, 목표지점까지 Speed만큼 움직인다.
  3. 움직이는 방향을 바라본다.
    if(_lockTarget != null)
    {
        float distance = (_destPos - transform.position).magnitude;
        if (distance <= 1.7)
        {
            State = PlayerState.Skill;
            return;
        }
    }

위 코드에서 1.7이 공격 사정거리에 해당하는 값이다.


마우스 커서

처음에 언급한 상황인 마우스를 누르고 있지만 공격대상이 움직이거나 마우스커서가 움직여 대상을 벗어날 때에 공격을 계속 진행하고자할 때 이에대한 처리가 필요하다.

기존의 방법은 공격이 한번 끝났을 때 Idle State로 전환하도록 하였는데

	State = PlayerState.Idle;

마우스커서 구현을 위해 추가적인 Mouse Action을 State전환의 조건으로 두어야할 필요가 있다. 이를위해 _stopSkill이라는 bool변수를 두어 true일 때 Idle상태로 전환, 즉 공격을 중지한다.

	bool _stopSkill = false;
    if (_stopSkill)
    {
        State = PlayerState.Idle;
    }
    else
    {
        State = PlayerState.Skill;
    }

1. _stopSkill = false

_stopSkill이 false일 때는 공격을 계속 진행할 때이다. 즉, 처음 공격명령을 내리는 Raycasting과정에 _stopSkill을 false로 설정한다.

    RaycastHit hit;
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    bool raycastHit = Physics.Raycast(ray, out hit, 100.0f, _mask);
    case Define.MouseEvent.PointerDown:
        {
            if (raycastHit)
            {
                _destPos = hit.point;
                State = PlayerState.Moving;
                _stopSkill = false;

                if (hit.collider.gameObject.layer == (int)Define.Layer.Monster)
                    _lockTarget = hit.collider.gameObject;
                else
                    _lockTarget = null;
            }
        }

2. _stopSkill = true

_stopSkill이 true가 될 때는 몬스터가 죽어 사라지거나 공격불능상태가 되었을 때와 같이 많은 상황이 있지만 현재 설정할 내용은 Mouse PointerUp이다. Mouse PointerUp이 발생하는 부분에 다음과 같은 코드를 작성함으로 구현할 수 있다.

    case Define.MouseEvent.PointerUp:
        _stopSkill = true;
        break;
profile
게임 개발자가 되고 싶은 한 소?년

1개의 댓글

comment-user-thumbnail
2023년 7월 19일

아주 유익한 내용이네요!

답글 달기