플레이어 동작 시스템

전체 플레이 영상

전체 코드

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public static PlayerController instance;

    // 외부 참조
    PlayerManager playerManager;
    PlayerSkillController skillController;

    [Header("카메라 위치")]
    [SerializeField] private Transform camPos;
    private Camera mainCam;
    private CharacterController cc;
    [HideInInspector] public Animator anim;

    [Header("이동 설정")]
    float walkSpeed = 0.0f;
    float runSpeed = 0.0f;
    public float jumpForce = 10.0f;
    public float gravity = 10.0f;   // +하면 위로, -하면 아래로 하기 위해서 (점프)
    float yVelocity = 0; // 점프를 위해서

    // 상태 
    public bool isInvincibility = false;

    // 애니메이터 해시
    static class AnimHash
    {
        public static readonly int Walk = Animator.StringToHash("Walk");
        public static readonly int Run = Animator.StringToHash("Run");
        public static readonly int Jump = Animator.StringToHash("Jump");
        public static readonly int Die = Animator.StringToHash("Die");
        public static readonly int Hit = Animator.StringToHash("hit");
    }


    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
            return;
        }

        cc = GetComponent<CharacterController>();
        anim = GetComponent<Animator>();
    }

    private void Start()
    {
        playerManager = PlayerManager.instance;

        skillController = GetComponent<PlayerSkillController>();

        mainCam = Camera.main;
    }

    private void LateUpdate()
    {
        if (mainCam == null || !mainCam)
            mainCam = Camera.main;

        // 카메라 위치 및 방향 동기화
        if (mainCam && camPos)
        {
            mainCam.transform.position = camPos.position;
            transform.rotation = mainCam.transform.rotation;
        }
    }

    private void Update()
    {
        if (anim.GetBool(AnimHash.Die)) return;

        HandleMovement();
        skillController.HandleSkill();
        CheckDeath();
    }

    void CheckDeath()
    {
        if (playerManager.CurrentHp <= 0 && !anim.GetBool(AnimHash.Die) && !isInvincibility)
        {
            anim.SetBool(AnimHash.Die, true);
            UI_Manager.instance.respawnUI.SetActive(true);
        }
    }

    void HandleMovement()
    {
        HandleJump();

        float x = Input.GetAxisRaw("Horizontal");
        float z = Input.GetAxisRaw("Vertical");

        // 1) 입력 → 로컬 → 월드 (수평 성분만)
        Vector3 inputDir = new Vector3(x, 0f, z);
        Vector3 planarDir = transform.TransformDirection(inputDir).normalized;

        // 2) 속도 선택
        walkSpeed = playerManager.PlayerStatus.MoveSpeed;
        runSpeed = playerManager.PlayerStatus.RunSpeed;
        bool isRunning = Input.GetKey(KeyCode.LeftShift);
        float speed = (x != 0 || z != 0) ? (isRunning ? runSpeed : walkSpeed) : 0f;

        // 3) 애니메이션
        anim.SetBool(AnimHash.Walk, speed > 0f && !isRunning);
        anim.SetBool(AnimHash.Run, speed > 0f && isRunning);

        // 4) 중력 갱신 (아래로 가속)
        yVelocity -= gravity * Time.deltaTime;

        // 5) ***수평과 수직을 분리해서 합산***
        Vector3 velocity = planarDir * speed; // 수평 속도
        velocity.y = yVelocity;               // 수직 속도 (speed로 곱하지 않음)

        // 6) 최종 이동 (프레임당 한 번만)
        cc.Move(velocity * Time.deltaTime);
    }

    void HandleJump()
    {
        if (cc.isGrounded)
        {
            // 접지 시 살짝 붙여주기(경계에서 붕뜸 방지)
            if (yVelocity < 0f) yVelocity = -2f;

            if (Input.GetKey(KeyCode.Space))
            {
                yVelocity = jumpForce;
                anim.SetBool(AnimHash.Jump, true);
            }
            else if (anim.GetBool(AnimHash.Jump))
            {
                anim.SetBool(AnimHash.Jump, false);
            }
        }
    }

    public void Damage()
    {
        anim.SetTrigger(AnimHash.Hit);
    }
}
using UnityEngine;

public class Camera_ : MonoBehaviour
{
    private static Camera_ instance;
    
    SettingManager settingManager;
    MonsterTargeting monsterTargeting; 

    public float rotationSpeed = 50f;   // 카메라 회전 속도
    float rotationX = 0.0f;             // 카메라의 수직 회전 각도
    float rotationY = 0.0f;             // 카메라의 수평 회전 각도

    GameObject targetMonster; // 타겟 몬스터


    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else if (instance != this)
        {
            Destroy(gameObject);
        }
    }

    private void Start()
    {
        settingManager = SettingManager.instance;
        monsterTargeting = GameObject.Find("Player").GetComponent<MonsterTargeting>(); 
    }

    void Update()
    {
        rotationSpeed = settingManager.MouseSensitivity(); // 마우스 감도 설정 가져오기

        CameraRotation(); 
        LookAtTargetMonster(); 
    }

    // 카메라 회전 처리
    void CameraRotation()
    {
        if (Input.GetMouseButton(1))
        {
            float mouseX = Input.GetAxis("Mouse X"); // 수평 움직임
            float mouseY = Input.GetAxis("Mouse Y"); // 수직 움직임

            rotationX += mouseX * rotationSpeed * Time.deltaTime; // 수평 회전량 계산
            rotationY += mouseY * rotationSpeed * Time.deltaTime; // 수직 회전량 계산

            // 수직 회전 각도를 제한하여 너무 위나 아래로 카메라가 회전하지 않도록 함
            if (rotationY < -15) rotationY = -15;
            else if (rotationY > 15) rotationY = 15;

            transform.localEulerAngles = new Vector3(-rotationY, rotationX, 0); // 카메라의 로컬 각도를 설정하여 회전 적용
        }
    }

    // 타겟 몬스터를 바라보게 처리
    void LookAtTargetMonster()
    {
        if (monsterTargeting != null)
        {
            targetMonster = monsterTargeting.TargetMonster; // 타겟 몬스터 가져옴 

            if (targetMonster != null)
            {
                Vector3 targetPosition = targetMonster.transform.position; // 타겟 몬스터의 위치

                targetPosition.y = transform.position.y; // 카메라의 높이에 맞게 y값 설정

                // 타겟 몬스터를 바라보는 방향 계산
                Quaternion targetRotation = Quaternion.LookRotation(targetPosition - transform.position);

                // 부드러운 회전을 위해 Slerp를 사용하여 카메라 회전 적용
                transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
            }
        }
    }
}

전체 흐름도