미니 RPG 만들기(3)_공격

개발조하·2024년 1월 3일
0

Unity

목록 보기
25/30
post-thumbnail

1. 사정거리 내 적 공격

2. 공격 - Idle 애니메이션 전환

  • PlayerAnimController 컨트롤러에 Attack 애니메이션 추가
    ㄴ bool타입으로 attack 변수 추가
    ㄴ Has Exit Time 해제
    ㄴ attack == true일 경우 Attack 애니메이션으로 전환
    ㄴ attack == false일 경우 WAIT / RUN 애니메이션으로 전환

❌ Attack 애니메이션 실행 시 앞으로 조금씩 전진함 ;;
애니메이션 자체가 갖고 있는 움직임으로 이를 무시하려면 Animator의 Applay Root Motion을 해제한다.

💡 Animator anim = GetComponent<Animator>(); _state = = PlayerState.Moving;을 계속 세트로 작성해줘야 하는 번거로움을 없애고자 코드 정리

  • PlayerState State 프로퍼티 생성
    public PlayerState State
    {
        get { return _state; }
        set
        {
            _state = value;

            Animator anim = GetComponent<Animator>();
            switch (_state)
            {
                case PlayerState.Die:
                    anim.SetBool("attack", false);
                    break;
                case PlayerState.Idle:
                    anim.SetFloat("speed", 0); 
                    anim.SetBool("attack", false);
                    break;
                case PlayerState.Moving:
                    anim.SetFloat("speed", _stat.MoveSpeed);
                    anim.SetBool("attack", false);
                    break;
                case PlayerState.Skill:
                    anim.SetBool("attack", true);
                    break;

            }
        }
    }
  • OnMouseEvent() 수정
    기존에 OnMouseEvent()에서는 Idle, Moving일 때만 실행되어야 하는 코드로 작성해서, Die일 때는 return;되도록 했었다. 하지만 Skill이 생겼기 때문에 혼란을 줄 수 있으니 아예 'OnMouseEvent_IdleRun()' 메서드로 분리시키자.
    bool _stopSkill = false;
    void OnMouseEvent(Define.MouseEvent evt)
    {
        switch (State)
        {
            case PlayerState.Die:
                break;
            case PlayerState.Idle:
                OnMouseEvent_IdleRun(evt);
                break;
            case PlayerState.Moving:
                OnMouseEvent_IdleRun(evt);
                break;
            case PlayerState.Skill:
                {
                    if(evt == Define.MouseEvent.PointerUp)
                        _stopSkill = true;
                }
                break;
        }
    }

    void OnMouseEvent_IdleRun(Define.MouseEvent evt)
    {
        RaycastHit hit;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        bool raycastHit = Physics.Raycast(ray, out hit, 100.0f, _mask);
        //Debug.DrawRay(Camera.main.transform.position, ray.direction * 100.0f, Color.red, 1.0f);

        switch (evt)
        {
            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;
                        }
                    }
                }
                break;
            case Define.MouseEvent.Press:
                {
                    if (_lockTarget == null && raycastHit)
                    {
                        _destPos = hit.point;
                    }
                }
                break;
            case Define.MouseEvent.PointerUp:
                _stopSkill = true;
                break;
        }
    }
  • OnHitEvent()
    void OnHitEvent()
    {
        Debug.Log("OnHitEvent");

        if (_stopSkill)
        {
            State = PlayerState.Idle;
        }
        else
        {
            State = PlayerState.Skill;
        }
    }
  • UpdateSkill()
    private void UpdateSkill()
    {
        if(_lockTarget != null)
        {
            Vector3 dir = _lockTarget.transform.position - transform.position;
            Quaternion quat = Quaternion.LookRotation(dir);
            transform.rotation = Quaternion.Lerp(transform.rotation, quat, 20 * Time.deltaTime);
        }
    }

ㄴ 적을 바라보고 공격 시 player 위치 회전값 설정

3. 마우스 누르고 있으면 계속 공격

  • Attack 애니메이션의 Loop Time 켜주기

❌ 마우스 PointerUp 상태가 되면 Attack 애니메이션에서 바로 Idle로 전환되지 않고, 잠깐 Run 애니메이션으로 전환됐다가 Idle이 실행된다.
ㄴ 현재 Attack - RUN이 우선순위에 있기 때문에 attack == false라는 동일 조건 하에서는 RUN으로 먼저 전환되는 로직이기 때문이다.

ㄴ 그렇다고 Animator에 또 파라미터를 추가해서 조건을 달아주는 것도 매우 번거롭다.

  • 해결방법 1: anim.Play("애니메이션명");
    ㄴ animator창에서 노드로 애니메이션을 연결하지 않고 코드로 애니메이션을 바로 실행!

anim.Play("RUN"); 이렇게 바로 애니메이션을 호출할 수 있다. 하지만 이렇게 되면 애니메이션 간의 전환이 단절되어보여서 부자연스럽다는 단점이 있다. 이를 보완하는 것이 'CrossFade()'메서드이다.

  • 해결방법 2: anim.CrossFade("WAIT", 0.1f);
    ㄴ 블랜드트리로 애니메이션 전환을 엮지 않고 코드로 블랜드 효과를 낼 수 있는 메서드이다. ㄴnormalizedTimeOffset : The time of the state을 0으로 설정
    ㄴ Loop을 켜주지 않아도 자동으로 애니메이션의 0초로 돌아가서 다시 시작한다.
    public PlayerState State
    {
        get { return _state; }
        set
        {
            _state = value;

            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;

            }
        }
    }

-> 즉, 애니메이션의 전환에 꼭 파라미터가 필요한 것은 아니다!

4. Player 무기 장착

ㄴKnight의 sword를 프리팹화하여 유니티짱의 오른손에 위치시킴

📄참고자료
[인프런] c#과 유니티로 만드는 MMORPG 게임 개발 시리즈_3. 유니티 엔진
Unity Documentation_Animator.CrossFade

profile
Unity 개발자 취준생의 개발로그, Slow and steady wins the race !

0개의 댓글