공부/Game Bootcamp

[멋쟁이사자처럼 부트캠프 TIL] 유니티 게임 개발 3기 : 애니메이션을 더한 캐릭터 점프, 아이템 먹기

Ail_ 2024. 12. 16. 16:04

 

캐릭터 이동 코드

// CharCharacter.cs

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

public class CharController : MonoBehaviour
{
    private const float jumpTestValue = 0.3f;
    private static readonly int Speed1 = Animator.StringToHash("Speed");
    private static readonly int Ground = Animator.StringToHash("Ground");
    [SerializeField] private float Speed = 5.0f;
    [SerializeField] private float JumpSpeed = 15.0f;
    [SerializeField] private Camera _mainCamera;
    [SerializeField] private float CameraSpeed = 4.0f;
    [SerializeField] private float MaxDistence = 4.0f;
    
    private Vector3 cameraOffset;
    
    InputAction Move_Input;
    private Animator _animator;
    private Rigidbody2D _rigidbody;
    private SpriteRenderer _spriteRenderer;
    private InputAction Jump_Input;

    public bool Grounded = true;

    void Start()
    {
        _animator = GetComponent<Animator>();
        _rigidbody = GetComponent<Rigidbody2D>();
        _spriteRenderer = GetComponent<SpriteRenderer>();
        
        UnityEngine.InputSystem.PlayerInput Input = GetComponent<UnityEngine.InputSystem.PlayerInput>();
        Move_Input = Input.actions["Move"];
        Jump_Input = Input.actions["Jump"];

        cameraOffset = _mainCamera.transform.position - transform.position;
    }

    
    void Update()
    {
        Vector2 moveValue = Move_Input.ReadValue<Vector2>();
        
        if (moveValue.x != 0)
            _spriteRenderer.flipX = moveValue.x < 0;
        
        _animator.SetFloat(Speed1, Mathf.Abs(moveValue.x));
        _rigidbody.velocity = new Vector2(moveValue.x * Speed, _rigidbody.velocity.y);
        
        if (Jump_Input.triggered && Grounded)
        {
            _rigidbody.AddForce(Vector2.up * JumpSpeed, ForceMode2D.Impulse);
            _animator.Play("Alchemist_Jump");
        }
    }
    
    private void LateUpdate()
    {
        var CharPosition = transform.position + cameraOffset;
        float speed = CameraSpeed;

        Vector3 newPosition = Vector3.zero;
        
        if (Vector3.Distance(CharPosition, _mainCamera.transform.position) >= MaxDistence)
        {
            Vector3 Gap = ((_mainCamera.transform.position) - CharPosition).normalized * MaxDistence;
            newPosition = CharPosition + Gap;
        }
        else
        {
            newPosition = Vector3.MoveTowards(_mainCamera.transform.position, 
                CharPosition, 
                speed * Time.deltaTime);
        }

        _mainCamera.transform.position = newPosition;
    }
}

 

강사님이 주로 쓰시는 방식인 빈게임 오브젝트 만들어서 Circle Collider 사용하는 방식

// GroundCheck.cs

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

public class GroundChecker : MonoBehaviour
{
    CharController charController;
    
    void Start()
    {
        charController = GetComponentInParent<CharController>();
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        Debug.Log("OnTriggerEnter2D");
        charController.Grounded = true;
        charController.GetComponent<Animator>().Play("Idles");
    }
    
    void OnTriggerExit2D(Collider2D other)
    {
        Debug.Log("OnTriggerExit2D");
        charController.Grounded = false;
    }
}

 

ProjectSetting에서 일괄 물리 설정 가능

2D는 2D 설정이 따로 있음

 

 

최종적으로 이펙트까지 추가함

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

public class ItemGetter : MonoBehaviour
{
    // 아이템이 이동할 목표 위치를 나타내는 UI 요소(RectTransform)
    public RectTransform itemRectTransform;

    // 아이템이 표시되는 캔버스
    public Canvas canvas;

    // 충돌 시 생성될 UI 아이템의 프리팹
    public GameObject itemPrefab;

    // 아이템이 목표 위치로 도착할 때 나타날 이펙트의 프리팹
    public GameObject getEffectPrefab;

    // 화면 좌표 변환에 사용할 카메라
    public Camera camera;

    // EaseInOut 함수: 부드럽게 가속과 감속을 적용하는 보간(Interpolation) 함수입니다.
    // t 값(0~1 사이)을 받아 움직임을 부드럽게 만드는 데 사용됩니다.
    private float EaseInOut(float t)
    {
        // t가 0.5보다 작으면 빠르게 가속, 크면 천천히 감속합니다.
        return t < 0.5f ? 2f * t * t : -1f + (4f - 2f * t) * t;
    }

    // GoingToBox 코루틴: 아이템을 박스(UI)로 이동시키고 도착 후 이펙트를 생성합니다.
    IEnumerator GoingToBox(RectTransform itemTransform, RectTransform boxTransform)
    {
        // 이동에 걸리는 총 시간(초)
        float duration = 2.0f;

        // 경과 시간 초기화
        float t = 0.0f;

        // 아이템의 초기 위치 저장
        Vector3 itemBeginPOS = itemTransform.position;

        // 경과 시간 비율이 1.0 이하일 때까지 반복
        while (1.0f >= t / duration)
        {
            // EaseInOut을 사용해 새 위치 계산
            Vector3 newPosition = Vector3.Lerp(itemBeginPOS, boxTransform.position, EaseInOut(t / duration));

            // 아이템의 위치를 새로 계산된 위치로 설정
            itemTransform.position = newPosition;

            // 경과 시간 증가
            t += Time.deltaTime;

            // 다음 프레임까지 대기
            yield return null;
        }

        // 이동 종료 후 정확히 목표 박스 위치로 아이템 설정
        itemTransform.position = boxTransform.position;

        // 아이템 UI 오브젝트 제거
        Destroy(itemTransform.gameObject);

        // 목표 위치에 이펙트 생성
        var particle = Instantiate(getEffectPrefab, boxTransform.position, getEffectPrefab.transform.rotation);

        // 디버그 로그로 이펙트 객체 정보를 출력
        Debug.Log(particle);

        // 생성된 이펙트의 크기를 변경
        particle.transform.localScale = new Vector3(4.0f, 4.0f, 4.0f);

        // Z축 위치를 0으로 설정 (2D 화면에서 정확히 보이도록 설정)
        var vector3 = particle.transform.position;
        vector3.z = 0.0f;
        particle.transform.position = vector3;

        // 3초 후 이펙트를 삭제
        Destroy(particle, 3.0f);
    }

    // OnTriggerEnter2D: 2D 물리 충돌이 발생했을 때 호출되는 Unity 이벤트 메서드
    private void OnTriggerEnter2D(Collider2D other)
    {
        // 충돌한 오브젝트 정보를 디버그 로그로 출력
        Debug.Log(other);

        // 충돌 위치에서 새로운 UI 아이템 생성
        var newObject = Instantiate(itemPrefab, other.transform.position, Quaternion.identity, canvas.transform);

        // 생성된 아이템의 이미지를 충돌한 오브젝트의 스프라이트로 설정
        newObject.GetComponent<Image>().sprite = other.GetComponent<SpriteRenderer>().sprite;

        // 새로 생성된 아이템의 위치를 충돌한 오브젝트와 동일하게 설정
        newObject.transform.position = other.transform.position;

        // 월드 좌표를 화면 좌표로 변환
        var newScreenPosition = Camera.main.WorldToScreenPoint(newObject.transform.position);

        // 화면 좌표를 캔버스의 로컬 좌표로 변환
        Vector2 localPoint;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            canvas.GetComponent<RectTransform>(), newScreenPosition, camera, out localPoint);

        // 생성된 아이템의 로컬 위치를 캔버스 내 좌표로 설정
        newObject.transform.localPosition = localPoint;

        // 아이템을 목표 박스 위치로 이동시키는 코루틴 실행
        StartCoroutine(GoingToBox(newObject.GetComponent<RectTransform>(), itemRectTransform));

        // 충돌한 오브젝트를 제거
        Destroy(other.gameObject);
    }
}