스파르타코딩클럽_Unity개발과정

유니티 숙련 개인 과제 - 3D 게임 캐릭터 이동과 물리 (2)

ybbro 2025. 5. 22. 22:19

1. 체력 표시, 감소, 회복

캠프 파이어 불 안에 트리거를 두고 그 안의 대미지를 가할 수 있는 오브젝트에 대미지를 지속적으로 주게끔 만들었습니다.

using UnityEngine;
using System.Collections.Generic;

namespace Environment
{
    [RequireComponent(typeof(Collider))] // 오브젝트를 부착할 때 콜라이더가 없다면 부여
    public class Campfire: MonoBehaviour
    {
        public int damage;
        public float damageDelay;
        List<IDamagable> damagables = new List<IDamagable>();
        
        private void Awake()
        {
            // 콜라이더를 트리거로 변경
            TryGetComponent(out Collider col);
            col.isTrigger = true;
        }
        
        private void Start()
        {
            // 대미지를 주는 주기마다, 영역 내 대미지를 줄 수 있는 오브젝트들에 대미지
            InvokeRepeating("DealDamage", 0, damageDelay);
        }
        
        void DealDamage()
        {
            for (int i = 0; i < damagables.Count; i++)
            {
                damagables[i].TakeDamage(damage);
            }
        }
        
        // 캠프파이어 불에 출입하는 대미지를 줄 수 있는 오브젝트들에 대해 이를 등록/해제
        private void OnTriggerEnter(Collider other)
        {
            if(other.TryGetComponent(out IDamagable damagable))
                damagables.Add(damagable);
        }
        
        private void OnTriggerExit(Collider other)
        {
            if(other.TryGetComponent(out IDamagable damagable))
                damagables.Remove(damagable);
        }
    }
}

 

 

2. 점프대

플레이어 점프 코드를 재사용할 수 있지 않을까 하여 매개변수로 점프력만 바꾸어 높이 점프할 수 있게 하였습니다.

 

 

3. 상호작용 가능한 물체 탐색 및 데이터 받기

 

탐색 범위를 시각화하였습니다.

캐릭터 앞쪽의 오브젝트 중 상호작용이 가능한 것이 있다면 안내 메세지를 띄우도록 하였습니다.

using UnityEngine;
using TMPro;
using System;
using UnityEngine.InputSystem;

public class Interaction : MonoBehaviour
{
    public float checkRadius = 1; // 체크 영역의 반경
    public float checkDelay = 0.2f; // 체크 딜레이가 너무 짧으면 최소 4프레임 주기로 해야 한다는 경고 주루룩 출현
    [SerializeField] TextMeshProUGUI textInfo; // 정보를 표시할 텍스트 UI
    public LayerMask layerMask; // Interaction 레이어만 포함하는 레이어마스크
    
    private IInteractable curInteractable;
    
    void Start()
    {
        // 반복적으로 상호가능 오브젝트 탐색
        InvokeRepeating("SearchInteractables", 0, checkDelay);
    }

    void SearchInteractables()
    {
        // 해당 스크립트를 부착한 오브젝트가 캐릭터 앞쪽에 위치하여 캐릭터 앞의 상호작용 가능한 물체들을 탐색
        // 구 형태로 레이를 쏘아 맞은 상호작용 가능한 오브젝트들
        Collider[] hits = Physics.OverlapSphere(transform.position,checkRadius,layerMask);
        // 플레이어 위치
        Vector3 playerPos = GameManager.Instance.Player.transform.position;
        
        // 맞은 오브젝트가 없다면: 정보를 표시하지 않음
        if (hits.Length == 0)
        {
            curInteractable = null;
            textInfo.text = "";
        }
        // 맞은 오브젝트가 있다면: 정보 표시
        else
        {
            // 플레이어로부터 가장 가까운 상호작용 가능한 오브젝트가 0번에 오게끔 정렬
            Array.Sort(hits, (a, b) 
                => Vector3.SqrMagnitude(a.transform.position - playerPos).CompareTo(Vector3.SqrMagnitude(b.transform.position - playerPos)));
            
            // 가장 가까운 상호작용 가능 오브젝트의 정보 출력
            if (hits[0].TryGetComponent(out curInteractable))
            {
                textInfo.text = curInteractable.GetInfo();
            }
        }
    }

    // 상호작용 기능 발동
    public void OnInteraction(InputValue inputValue)
    {
        if (inputValue.isPressed)
        {
            curInteractable?.OnInteract();
        }
    }

    // 범위가 어떤지 보기 위한 디버그용 영역 표시
    private void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawSphere(transform.position,checkRadius);
    }
}