DINO NET

[슈팅 게임] player 움직임 구현 본문

GMI()/짭타폭스

[슈팅 게임] player 움직임 구현

2023. 10. 12. 14:54

CharacterMovement.cs

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

public class CharacterMovement : MonoBehaviour
{
    private CharacterController _controller;
    private Camera _followCamera;

    [SerializeField] private float _playerSpeed = 5f;
    [SerializeField] private float _rotationSpeed = 10f;
    [SerializeField] private float _boostSpeedMultiplier = 1.5f;
    [SerializeField] private float _boostGaugeMax = 100f;
    [SerializeField] private float _boostGaugeConsumptionRate = 20f;
    [SerializeField] private float _boostGaugeRechargeRate = 10f;

    private bool _groundedPlayer;
    private bool _inputReceived;
    private float _lastInputTime;

    private float _boostGauge;
    private bool _isSprinting = false;
    public Text gaugeText;

    private void Start()
    {
        _controller = GetComponent<CharacterController>();
        _followCamera = Camera.main;
        _boostGauge = _boostGaugeMax;
        StartCoroutine(RechargeGauge());
        StartCoroutine(DisplayChargedAmount());
        StartCoroutine(DisplayGaugeReading());
    }

    private void Update()
    {
        HandleSprintInput();
        Movement();
        LevelPlaneAfterDelay();
    }

    void HandleSprintInput()
    {
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        // Check if there's any input for movement
        bool isMoving = (Mathf.Abs(horizontalInput) > 0.1f || Mathf.Abs(verticalInput) > 0.1f);

        if (Input.GetKey(KeyCode.Space) && isMoving && _boostGauge > 0)
        {
            _isSprinting = true;
            float gaugeConsumption = _boostGaugeConsumptionRate * Time.deltaTime;
            _boostGauge -= gaugeConsumption;

            // Check if remaining gauge is 0.5 or less and stop sprinting immediately
            if (_boostGauge <= 0.5f)
            {
                _isSprinting = false;
                _boostGauge = 0f; // Ensure the gauge is exactly 0 when stopping
            }
        }
        else
        {
            // If not moving or the gauge is empty, stop sprinting immediately
            _isSprinting = false;
        }

        // Clamp the gauge value to ensure it doesn't go below zero
        _boostGauge = Mathf.Clamp(_boostGauge, 0, _boostGaugeMax);
    }

    IEnumerator RechargeGauge()
    {
        while (true)
        {
            if (_boostGauge < _boostGaugeMax)
            {
                _boostGauge += _boostGaugeRechargeRate * Time.deltaTime;
            }

            _boostGauge = Mathf.Clamp(_boostGauge, 0, _boostGaugeMax);
            yield return null;
        }
    }

    IEnumerator DisplayChargedAmount()
    {
        while (true)
        {
            Debug.Log("Charged Amount: " + _boostGauge.ToString("F2"));
            yield return new WaitForSeconds(1f); // Display every second
        }
    }

    IEnumerator DisplayGaugeConsumption()
    {
        float gaugeConsumption = 0f;
        while (_isSprinting)
        {
            gaugeConsumption += _boostGaugeConsumptionRate * Time.deltaTime;
            yield return null; // Wait for the next frame
        }

        // Display gauge consumption and remaining gauge when sprinting stops
        Debug.Log("Gauge Consumption: " + gaugeConsumption.ToString("F2") + " per second");
        Debug.Log("Remaining Gauge: " + _boostGauge.ToString("F2"));
    }

    private IEnumerator DisplayGaugeReading()
    {
        while (true)
        {
            if (_isSprinting || _boostGauge < _boostGaugeMax)
            {
                // Display the gauge reading in real-time on the Text UI element
                if (gaugeText != null)
                {
                    gaugeText.text = "Booster Gauge: " + _boostGauge.ToString("F1");
                }
            }

            yield return null; // Update UI in real-time
        }
    }

    void Movement()
    {
        _groundedPlayer = _controller.isGrounded;
        if (_groundedPlayer)
        {
            _controller.Move(Vector3.down * _controller.stepOffset);
        }

        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        _inputReceived = (Mathf.Abs(horizontalInput) > 0.1f || Mathf.Abs(verticalInput) > 0.1f);

        Vector3 movementInput = (_followCamera.transform.forward * verticalInput) + (_followCamera.transform.right * horizontalInput);
        Vector3 movementDirection = movementInput.normalized;

        float speedMultiplier = _isSprinting ? _boostSpeedMultiplier : 1.0f;
        _controller.Move(movementDirection * _playerSpeed * speedMultiplier * Time.deltaTime);

        if (movementDirection != Vector3.zero)
        {
            Quaternion desiredRotation = Quaternion.LookRotation(movementDirection, Vector3.up);
            transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, _rotationSpeed * Time.deltaTime);
            _lastInputTime = Time.time;
        }
    }

    void LevelPlaneAfterDelay()
    {
        if (!_inputReceived && Time.time - _lastInputTime >= 0.02f)
        {
            Quaternion targetRotation = Quaternion.Euler(0f, transform.eulerAngles.y, transform.eulerAngles.z);
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, _rotationSpeed * Time.deltaTime);
        }
    }
}

기본적으로 키보드에서 입력 값을 받아서 상, 하, 좌, 우로 움직이는 프로그램.

 

엔진에서 바로 값을 바꿀 수 있도록 serialiizefield화 함

 

움직일 비행체의 charactercontroller 가져오기.

3인칭 시점에서 진행될 예정이므로 camera가 비행체 뒤에서 따라올 수 있도록 함.

 

* player (비행체)는 키보드로 움직이고 카메라는 마우스로 움직인다. player와 카메라 따로 보기!!

 

입력값을 받아오고(GetAxis) player의 움직임 구현

Camera가 뒤에서 따라오는 형태!

캐릭터의 움직임을 프레임당 계산 (movementDirection)

카메라 및 player의 회전은 quaternion으로 처리

 

 player movement 구현