Posted in: Unity, 프로젝트 When the Spring comes

19. 플레이어 회피 기능 추가

플레이어 회피 기능을 만들었다. 처음에는 빠른 속도로 캐릭터 이미지가 이동하는 방식으로 제작하려고 했다. 플레이어 게임 오브젝트가 거리를 10만큼 움직이며 공격을 회피한다고 했을 때 게임 오브젝트의 transform.position을 바꾸어서 10단계로 나눠서 빠르게 이동시켰는데 버벅대는 것 같았다. 나중에는 플레이어 게임 오브젝트가 순간이동하는 걸로 바꾸었다.

스크립트를 추가해야 되나 고민했는데 어쨌든 플레이어의 이동과 관련된 것이니 PlayerMove 스크립트에 만들었다.

void FixedUpdate()
{
    dodgeTimeCheck += Time.deltaTime;

    if(Input.GetKey(KeyCode.J) &&
        !GameManager.GetInstance().playerIsDash &&
        dodgeTimeCheck >= dodgeTime)
    {
        GameManager.GetInstance().playerIsDash = true;

        dodgeTimeCheck = 0;

        Dodge();
    }
    else if(dodgeTimeCheck >= dodgeTime)
    {
        GameManager.GetInstance().playerIsDash = false;
    }
}

우선 회피 기능은 언제든지 할 수 있는 기능이 되면 안 된다. 더 빠르게 이동하거나 공격을 피하는 기능이 회피인데 마음껏 사용할 수 있다면 회피로 이동하는 것이 더 빠를 테고 기본 이동이 필요가 없어지는 것이다.

그래서 나는 dodgeTime 변수로 회피 쿨타임을 지정하였다. dodgeTimeCheck 변수는 시간이 흐른 만큼 더해진다. dodgeTime 보다 dodgeTimeCheck가 커야 회피를 사용할 수 있고 회피를 사용하면 dodgeTimeCheck는 0을 대입하여 초기화한다. 회피를 한 번 사용하면 dodgeTime 만큼 시간이 흘러야 다시 사용할 수 있는 것이다.

public void Dodge()
{
    RaycastHit hit;

    if(Physics.Raycast(transform.position, directionVec.normalized, out hit, dodgeDistance))
    {
        GameObject hitTarget = hit.collider.gameObject;

        transform.position = hit.point;
    }
    else
    {
        transform.position += directionVec.normalized * dodgeDistance;
    }
}

회피 기능은 게임 오브젝트가 원하는 거리(dodgeDistance)만큼 순간이동하는 기능이다. 여기서 중요한 것은 게임 오브젝트가 통과하면 안 되는 벽을 통과해 넘어가는 것이다.

여러 가지 방법이 있겠지만 나는 Raycast를 사용하여 회피하기 전에 ray를 발사하고 회피 거리 안에 다른 게임 오브젝트가 있다면 충돌한 위치로 순간이동되게 하였다. 결과는 대만족.

나중에 회피 이펙트 스프라이트를 받으면 회피 이펙트도 추가해야 되겠지.. 그리고 에너지 스탯을 추가하여 회피 기능을 사용하면 에너지 스탯이 소비되고 에너지 스탯이 부족하다면 회피 기능을 사용 못 하게 추가하여야 한다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMove : MonoBehaviour
{
    private PlayerStat playerStat;
    public float jumpVelocity;
    private float inputX;
    private float inputZ;
    private float attackAngle;
    public Vector3 directionVec;

    private Rigidbody rigidbody;
    public float dodgeDistance;
    public float dodgeTime;
    private float dodgeTimeCheck;
    


    private void Start() {
        playerStat = GetComponent<PlayerStat>();

        directionVec = new Vector3(0, 0, -2.0f);
        attackAngle = 180.0f;
        GameManager.GetInstance().attackAngle = attackAngle;

        rigidbody = GetComponent<Rigidbody>();
    }

    private void FixedUpdate() {

        dodgeTimeCheck += Time.deltaTime;

        if(!GameManager.GetInstance().playerIsBasicAttack &&
           !GameManager.GetInstance().playerIsDash) 
            Move();

        if(Input.GetKey(KeyCode.J) &&
            !GameManager.GetInstance().playerIsDash &&
            dodgeTimeCheck >= dodgeTime)
        {
            GameManager.GetInstance().playerIsDash = true;
            dodgeTimeCheck = 0;
            Dodge();
        }
        else if(dodgeTimeCheck >= dodgeTime)
        {
            GameManager.GetInstance().playerIsDash = false;
        }

        Direction();
    }

    public void Move()
    {
        inputX = Input.GetAxisRaw("Horizontal");
        inputZ = Input.GetAxisRaw("Vertical");

        Vector3 velocity = new Vector3(inputX, jumpVelocity, inputZ);

        transform.position += velocity * playerStat.speed * Time.deltaTime;

        if(velocity == new Vector3(0,0,0)) GameManager.GetInstance().playerIsMove = false;
        else GameManager.GetInstance().playerIsMove = true;
    }


    public void Dodge()
    {
        RaycastHit hit;

        Debug.DrawRay(transform.position, directionVec.normalized * dodgeDistance);

        if(Physics.Raycast(transform.position, directionVec.normalized, out hit, dodgeDistance))
        {
            GameObject hitTarget = hit.collider.gameObject;

            transform.position = hit.point;
        }
        else
        {
            transform.position += directionVec.normalized * dodgeDistance;
        }
    }

    public void Direction()
    {
        if (inputX == -1 && inputZ == 1)//UpLeft
        {
            directionVec = new Vector3(-1.14f, 0, 1.14f);
            attackAngle = 315.0f;
        }
        if (inputX == 0 && inputZ == 1)//Up
        {
            directionVec = new Vector3(0, 0, 2.0f);
            attackAngle = 0.0f;
        }
        if (inputX == 1 && inputZ == 1)//UpRight
        {
            directionVec = new Vector3(1.14f, 0, 1.14f);
            attackAngle = 45.0f;
        }
        if (inputX == -1 && inputZ == 0)//Left
        {
            directionVec = new Vector3(-2.0f, 0, 0);
            attackAngle = 270.0f;
        }
        if (inputX == 0 && inputZ == 0)//Idle
        {
            directionVec = new Vector3(0, 0, 0);
        }
        if (inputX == 1 && inputZ == 0)//Right
        {
            directionVec = new Vector3(2.0f, 0, 0);
            attackAngle = 90.0f;
        }
        if (inputX == -1 && inputZ == -1)//DownLeft
        {
            directionVec = new Vector3(-1.14f, 0, -1.14f);
            attackAngle = 225.0f;
        }
        if (inputX == 0 && inputZ == -1)//Down
        {
            directionVec = new Vector3(0, 0, -2.0f);
            attackAngle = 180.0f;
        }
        if (inputX == 1 && inputZ == -1)//DownRight
        {
            directionVec = new Vector3(1.14f, 0, -1.14f);
            attackAngle = 135.0f;
        }

        if(directionVec != new Vector3(0, 0, 0))
            GameManager.GetInstance().playerDirection = directionVec;
        GameManager.GetInstance().attackAngle = attackAngle;
    }

    void OnDrawGizmos()
    {
        Gizmos.color = Color.magenta;
        Gizmos.DrawLine(transform.position,
                        GameManager.GetInstance().playerDirection.normalized + transform.position);
    }

    private void OnTriggerStay(Collider other) {
        if(other.gameObject.layer == LayerMask.NameToLayer("Obstacle"))
            Debug.Log("보험 아줌마 졸라 짜증나");
    }
}

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다