본문 바로가기
GGM/엔진

20230315 - 엔진 - RayCast, 애니메이션

by DDongYeop 2023. 3. 16.
728x90

 

우선 Animator창에서 Sub-State Machine을 만들어준다. 

 

Animator의 Layers와 비슷한 역할을 한다고 보면 되지만, 1개의 Layer에서 1개의 에니메이션만 실행이 된다고 생각해둬야한다. 약간 비슷한 것들 모아두고 관리 편하게 하는 용도로 쓰면 좋을거 같다. 

 

이후 저번에 만들어둔 AgentAnimator에 아까 만든 Parameter에 대응하기 위해 추가해준다. 

    private readonly int _attackHash = Animator.StringToHash("attack");
    private readonly int _isAttackHash = Animator.StringToHash("is_attack");

    public event Action OnAnimationEndTrigger = null;
    
    public void SetAttackState(bool value)
    {
        _animator.SetBool(_isAttackHash, value);
    }

    public void SetAttackTrigger(bool value)
    {
        if (value)
            _animator.SetTrigger(_attackHash);
        else
            _animator.ResetTrigger(_attackHash);
    }

 

이후 공격키를 받아오기 위해 지난번 만들어뒀던 AgentInput에 마우스 좌클릭을 추가해준다. 

    public event Action OnAttackKeyPress = null;
    
    private void Update()
    {
        UpdateMoveInput();
        UpdateAttackInput();
    }

    private void UpdateAttackInput()
    {
        if (Input.GetMouseButtonDown(0))
            OnAttackKeyPress?.Invoke();
    }

 

이후 공격하고, 콤보를 쌓는 것까지 해준다. 

using Core;
using UnityEngine;

public class AttackState : CommonState
{
    [SerializeField] private float _keyDelay = 0.5f;
    
    private int _currentCombo = 1; //현재 콤보가 몇인지
    private bool _canAttack = true; //선입력 막기위해 다음 공격 가능 상태인가
    private float _keyTimer = 0; //다음공곡이 이뤄지기까지 기다리는 시간
    // 이시간내로 입력 안 하면 Idle로 돌아감
    
    public override void OnEnterState()
    {
        _agentInput.OnAttackKeyPress += OnAttackHandle;
        _animator.OnAnimationEndTrigger += OnAnimaionEnd;
        _currentCombo = 0;
        _canAttack = true;
        _animator.SetAttackState(true);
        OnAttackHandle(); //처음 1타 들어가도록 
    }

    public override void OnExitState()
    {
        _agentInput.OnAttackKeyPress -= OnAttackHandle;
        _animator.OnAnimationEndTrigger -= OnAnimaionEnd;
        _animator.SetAttackState(false);
        _animator.SetAttackTrigger(false);
    }

    private void OnAnimaionEnd()
    {
        _canAttack = true;
        _keyTimer = _keyDelay; //0.5초 기다리기 시작
    }

    public void OnAttackHandle()
    {
        if (_canAttack && _currentCombo < 3)
        {
            _canAttack = false;
            _currentCombo++;
            _animator.SetAttackTrigger(true);
        }
    }

    public override void UpdateState()
    {
        if (_canAttack && _keyTimer > 0)
        {
            _keyTimer -= Time.deltaTime;
            if (_keyTimer <= 0)
                _agentController.ChangeState(StateType.Normal);
        }
    }
}

 

이후 LayCast에 대해 배웠다. 

일단  RayCast는 (시작할 위치, 어디 방향으로 갈 것인지, 부딪친 오브젝트를 담을 곳, 어느 정도로 길이를 설정 할 것인지, 어떤 Layer일때만 반응 시킬지) 정할 수 있는것이다. 

다음 BoxCast는 (시작할 위치, 어느정도 크기로 생성을 해줄크기, 어디 방향을 향해 갈 것인지, 맞은 것을 담을 변수 넣고, 사각형의 로테이션 값, 어느길이로 갈건지)을 정해주면 된다.

이후 hit이 됐을떄와 안 됐을떄를 나누어 따로 반응을 적용시켜준다. 

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

public class RayCaster : MonoBehaviour
{
    [SerializeField] private float _maxDistance = 5f;
    
    private void OnDrawGizmos()
    {
        RaycastHit hit;

        bool isHit = Physics.Raycast(transform.position, transform.forward, out hit, _maxDistance, 1 << LayerMask.NameToLayer("Enemy"));
        isHit = Physics.BoxCast(transform.position, transform.lossyScale * 0.5f, transform.forward, out hit, transform.rotation, _maxDistance);
        
        if (isHit)
        {
            Gizmos.color = Color.red;
            Gizmos.DrawRay(transform.position, transform.forward * hit.distance);
            Gizmos.DrawWireCube(transform.position + transform.forward * hit.distance, transform.lossyScale);
        }
        else
        {
            Gizmos.color = Color.green;
            Gizmos.DrawRay(transform.position, transform.forward * _maxDistance);
        }
    }
}

 

이후 위에서 배운 것들을 사용하면

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

public class AgentMovement : MonoBehaviour
{
    [SerializeField] private float _moveSpeed = 8f, _gravity = -9.8f;
    private CharacterController _characterController;
    private Vector3 _movementVelocity;

    public Vector3 MovementVelocity => _movementVelocity;
    private float _verticalVelocity; 

    private void Awake()
    {
        _characterController = GetComponent<CharacterController>();
    }

    private void Update()
    {
        UpdateMovement();
    }   

    private void UpdateMovement()
    {
        float horizontal = Input.GetAxisRaw("Horizontal");
        float vertical = Input.GetAxisRaw("Vertical");

        _movementVelocity = new Vector3(horizontal, 0, vertical);
    }

    private void CalculatePlayerMovement()
    {
        _movementVelocity.Normalize(); 

        _movementVelocity *= _moveSpeed * Time.fixedDeltaTime;
        _movementVelocity = Quaternion.Euler(0, -45f, 0) * _movementVelocity;
        if (_movementVelocity.sqrMagnitude > 0)
            transform.rotation = Quaternion.LookRotation(_movementVelocity);
    }

    private void FixedUpdate()
    {
        CalculatePlayerMovement(); 

        if (_characterController.isGrounded == false) 
            _verticalVelocity = _gravity * Time.fixedDeltaTime;
        else 
            _verticalVelocity = _gravity * 0.3f * Time.fixedDeltaTime;

        Vector3 move = _movementVelocity + _verticalVelocity * Vector3.up;
        _characterController.Move(move);
    }

}
using UnityEngine;

public class Attack : MonoBehaviour
{
    [SerializeField] private float _maxDistance = 5f;
    private bool isOn = false;
    
    private void OnDrawGizmos()
    {
        RaycastHit hit;
        bool isHit = Physics.BoxCast(transform.position, transform.lossyScale * 0.5f, transform.forward, out hit, transform.rotation, _maxDistance);
        
        if (isHit)
        {
            Gizmos.color = Color.red;
            Gizmos.DrawRay(transform.position, transform.forward * hit.distance);
            Gizmos.DrawWireCube(transform.position + transform.forward * hit.distance, transform.lossyScale);
            if (Input.GetKeyDown(KeyCode.Space) || Input.GetButton("Fire1"))
            {
                hit.collider.GetComponent<Rigidbody>().AddForce(transform.forward * 500f);
            }
        }
        else
        {
            Gizmos.color = Color.green;
            Gizmos.DrawRay(transform.position, transform.forward * _maxDistance);
        }
    }
}

 

아래에 것처럼 특정키를 눌렀을때 날라가게 하는 것을 만들 수 있다. 

 

이후 원래하던 프로젝트로 돌아와서 Define에 MainCamera을 넣어주고 

    public class Define
    {
        private static Camera _mainCam = null;

        public static Camera MainCam
        {
            get
            {
                if (_mainCam == null)
                    _mainCam = Camera.main;
                return _mainCam;
            }
        }
    }

 

이후 아까 사용했던 Ray와 아까만든 mainCamera를 담는 변수를 이용하여 스크린을 클릭을 하는 형태를 만들어준다. 

    public Vector3 GetMouseWorldPosition()
    {
        Ray ray = MainCam.ScreenPointToRay(Input.mousePosition);

        RaycastHit hit;
        bool result = Physics.Raycast(ray,  out hit, MainCam.farClipPlane, _whatIsGround);
        if (result)
            return hit.point;
        else 
            return Vector3.zero;
    }

 

728x90

댓글