체온 시스템

JJW·2024년 12월 17일
0

Unity

목록 보기
17/34

오늘은 저번에 소개했었던 낮과밤 시스템의 체온을 추가해보도록 하겠습니다.
간단하게 구현할 예정이며 낮,밤때 온도가 달라지게끔 하였습니다. 계절은 구현하고 싶으나. 에셋이 없네용..


체온 시스템 구성 요소

1. Temperature (플레이어 체온 클래스)

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

public class Temperature : MonoBehaviour
{
    // 체온 상태를 나타내는 열거형
    public enum TemperatureState
    {
        Cold,
        Neutral,
        Hot
    }

    // 기본 체온 (정상적인 인간 체온)
    [SerializeField]
    private float baseTemperature = 36.5f;
    public float BaseTemperature
    {
        get { return baseTemperature; }
        private set { baseTemperature = value; }
    }

    // 현재 맵의 온도
    private float mapTemperature = 0f;

    // 장착된 아이템의 온도 보정 값들
    private List<float> itemTemperatureModifiers = new List<float>();

    // 최소 및 최대 체온
    [SerializeField]
    private float minTemperature = 25.0f;
    public float MinTemperature
    {
        get { return minTemperature; }
        private set { minTemperature = value; }
    }

    [SerializeField]
    private float maxTemperature = 45.0f;
    public float MaxTemperature
    {
        get { return maxTemperature; }
        private set { maxTemperature = value; }
    }

    // 현재 체온
    private float currentTemperature;
    public float CurrentTemperature
    {
        get { return currentTemperature; }
        private set
        {
            currentTemperature = Mathf.Clamp(value, MinTemperature, MaxTemperature);
            UpdateTemperatureState();
            OnTemperatureChanged?.Invoke(currentTemperature, currentState);
        }
    }

    // 체온 변화 이벤트
    public event Action<float, TemperatureState> OnTemperatureChanged;
    // 체온 변화로 인한 HP 변화 이벤트 -> 여기에 플레이어 Hp 깍는 함수 연결시켜주면 됩니다.
    public event Action<float> OnTemperatureHP;

    // 현재 체온 상태
    private TemperatureState currentState = TemperatureState.Neutral;
    public TemperatureState CurrentState
    {
        get { return currentState; }
        private set { currentState = value; }
    }

    // 데미지 입는 시간
    private float dealDelay = 1.0f;
    private float dealValue = 1.0f;
    private float curDelay = 0.0f;

    private void Start()
    {
        // 초기 체온 설정
        CalculateFinalTemperature();
        DayNightCycle.Instance.OnWeatherChange += SetMapTemperature;
    }

    private void Update()
    {
        if(currentState != TemperatureState.Neutral)
        {
            curDelay += Time.deltaTime;

            if(curDelay >= dealDelay)
            {
                curDelay = 0.0f;
                OnTemperatureHP?.Invoke(dealValue);
            }
        }
    }

    // 맵 온도 변경 시 호출되는 함수
    public void SetMapTemperature(float newMapTemperature)
    {
        mapTemperature = newMapTemperature;
        CalculateFinalTemperature();
    }

    // 아이템 장착 시 호출되는 함수
    public void AddItemTemperatureModifier(float modifier)
    {
        itemTemperatureModifiers.Add(modifier);
        CalculateFinalTemperature();
    }

    // 아이템 해제 시 호출되는 함수
    public void RemoveItemTemperatureModifier(float modifier)
    {
        itemTemperatureModifiers.Remove(modifier);
        CalculateFinalTemperature();
    }

    // 최종 체온 계산 함수
    private void CalculateFinalTemperature()
    {
        float itemsTotal = 0f;
        foreach (float mod in itemTemperatureModifiers)
        {
            itemsTotal += mod;
        }

        CurrentTemperature = BaseTemperature + mapTemperature + itemsTotal;
    }

    // 체온 상태 업데이트 함수
    private void UpdateTemperatureState()
    {
        TemperatureState previousState = currentState;

        if (currentTemperature <= MinTemperature)
        {
            currentState = TemperatureState.Cold;
        }
        else if (currentTemperature >= MaxTemperature)
        {
            currentState = TemperatureState.Hot;
        }
        else
        {
            currentState = TemperatureState.Neutral;
        }

        if (previousState != currentState)
        {
            Debug.Log($"Temperature state changed to {currentState}");
        }
    }
}
  • 플레이어의 체온 관리를 담당해 줄 클래스입니다.
  • 적정온도를 가지고 있으며 낮,밤을 구현한 클래스에서 현재 시간에 따른 온도를 받아 상호작용 합니다.
  • 외부요소로 체온을 더하거나 뺄 수 있게끔 함수를 만들어놓았습니다. 아이템 장착이나 횃불을 들었을 때나 불 근처에 있을 때 실행시켜주면 됩니다.

2. TemperatureUI

using UnityEngine;
using UnityEngine.UI;

public class TemperatureUI : MonoBehaviour
{
    [SerializeField] private Temperature Temperature;

    [SerializeField] private Text text_Temperature_State;
    [SerializeField] private Text text_Temperature;


    [SerializeField] private Text text_HP;
    [SerializeField] private float textHpValue = 100.0f;

    private void Awake()
    {
        Temperature.OnTemperatureChanged += UpdateTemperature;
        Temperature.OnTemperatureHP += TakeDamageUI;
    }

    private void UpdateTemperature(float temperature, Temperature.TemperatureState state)
    {
        text_Temperature_State.text = $"{state.ToString()}";
        text_Temperature.text = $"{temperature.ToString("F1")}.C";
    }

    private void TakeDamageUI(float damage)
    {
        textHpValue -= damage;
        text_HP.text = $"HP : {textHpValue.ToString()}";
    }

    public void OnClickEquipped(float value)
    {
        Debug.Log("OnClickEquipped");
        Temperature.AddItemTemperatureModifier(value);
    }

    public void OnClickUnequipped(float value)
    {
        Debug.Log("OnClickUnequipped");
        Temperature.RemoveItemTemperatureModifier(value);
    }
}
  • 테스트를 위해 만든 클래스입니다.
  • 아직 실제 데이터 클래스를 구현하지 않아 임시 변수로 HP가 체온에 따라 조절되도록 하였습니다.

3. DayNightCycle (낮과 밤 관리 클래스)

using System;
using UnityEngine;
using UnityEngine.UI;

public class DayNightCycle : MonoSingleton<DayNightCycle>
{
    [Header("시간 설정")]
    public float dayLengthInSeconds = 120f;                 // 하루 시간
    [Range(0f, 1f)] public float initialTimeOfDay = 0.25f;  // 오전 6시부터 시작 (0 = 자정, 0.25 = 오전 6시)
    private float timeOfDay;                                // 현재 시간 비율 (0 ~ 1)
    public float timeScale = 1f;                            // 시간 배속

    [Header("Directional Light 설정")]
    public Light directionalLight;
    public float minSunAngle = -90f;                        // 자정에서의 각도
    public float maxSunAngle = 270f;                        // 태양이 완전히 지는 각도

    // Weather Settings
    [Header("Weather 설정")]
    public float dayMapTemperature = 10f;                    // 낮의 맵 온도 보정값
    public float nightMapTemperature = -10f;                 // 밤의 맵 온도 보정값
    public float dayStartTime = 0.25f;                       // 낮 시작 시간 (0.25 = 오전 6시)
    public float dayEndTime = 0.75f;                         // 낮 끝 시간 (0.75 = 오후 6시)
    private bool isDay;

    // 테스트용
    public Text Text_Time;
    public int hours;                                       // 현재 시
    public int minutes;                                     // 현재 분
    public int aliveDay;                                    // 살아있는 시간

    // 날씨 변경용 (여기서는 그냥 밤,낮으로 체온 조절을 위해)
    public event Action<float> OnWeatherChange;

    private void Start()
    {
        // 초기 시간 설정 (오전 6시부터 시작)
        timeOfDay = initialTimeOfDay;
    }

    private void Update()
    {
        // 시간 진행
        timeOfDay += (Time.deltaTime / dayLengthInSeconds) * timeScale;

        // 하루가 지나면 다시 0으로 순환
        if (timeOfDay > 1f)
        {
            timeOfDay -= 1f;
            aliveDay++;
        }

        UpdateDayNightState();
        UpdateTimeDisplay();
        UpdateSunPosition();
    }

    private void UpdateDayNightState()
    {
        bool currentlyDay = (timeOfDay >= dayStartTime && timeOfDay < dayEndTime);

        if (currentlyDay != isDay)
        {
            isDay = currentlyDay;

            if (isDay)
            {
                Debug.Log("It is now Daytime.");
                OnWeatherChange?.Invoke(dayMapTemperature);
            }
            else
            {
                Debug.Log("It is now Nighttime.");
                OnWeatherChange?.Invoke(nightMapTemperature);
            }
        }
    }

    private void UpdateSunPosition()
    {
        if (directionalLight != null)
        {
            // 시간에 따른 태양 각도 계산
            float sunRotationX = Mathf.Lerp(minSunAngle, maxSunAngle, timeOfDay);
            directionalLight.transform.rotation = Quaternion.Euler(new Vector3(sunRotationX, 90f, 0f));
        }
    }

    private void UpdateTimeDisplay()
    {
        int hours = Mathf.FloorToInt(timeOfDay * 24f);
        int minutes = Mathf.FloorToInt((timeOfDay * 1440f) % 60);
        Text_Time.text = $"{aliveDay}일 {hours:D2}:{minutes:D2}";
    }
}
  • 저번에 구현하였던 DayNightCycle를 수정하였습니다. Singleton으로 수정하였으며 낮과 밤의 온도와 시간을 구해 Temperature 클래스에 맵 온도를 변경해줍니다.

테스트 영상

  • 낮과 밤에 따라 온도가 플레이어의 온도가 변하며 HP가 변동되는 모습을 볼 수 있습니다.

문제 수정

UI 상호작용 안되는 문제

횃불 및 체온 조절 아이템

using System.Collections.Generic;
using UnityEngine;

public class TemperatureStruct : MonoBehaviour
{
    [SerializeField] private float temperatureValue;

    private Dictionary<Temperature, float> dic_Chars = new Dictionary<Temperature, float>();

    public void OnTriggerEnter(Collider other)
    {
        if(other.TryGetComponent(out Temperature temp) == true)
        {
            dic_Chars.Add(temp, temperatureValue);
            temp.AddItemTemperatureModifier(temperatureValue);
        }
    }

    public void OnTriggerExit(Collider other)
    {
        if (other.TryGetComponent(out Temperature temp) == true)
        {
            dic_Chars.Remove(temp);
            temp.RemoveItemTemperatureModifier(temperatureValue);
        }
    }

    private void OnDisable()
    {
        foreach(var pair in dic_Chars)
        {
            pair.Key.RemoveItemTemperatureModifier(pair.Value);
        }
        dic_Chars.Clear();
    }
}
  • 횃불이나 모닥불 같은 맵 오브젝트용 클래스입니다.
  • Trigger를 사용하였으며 해당 오브젝트 비활성화 될 때 Dictionary에 존재하는 User들의 체온을 돌려주고 Clear시킵니다.

    // 아이템 장착 시 호출되는 함수
    public void AddItemTemperatureModifier(float modifier)
    {
        itemTemperatureModifiers.Add(modifier);
        CalculateFinalTemperature();
    }

    // 아이템 해제 시 호출되는 함수
    public void RemoveItemTemperatureModifier(float modifier)
    {
        itemTemperatureModifiers.Remove(modifier);
        CalculateFinalTemperature();
    }
  • 아이템의 경우에는 아직 아이템이 만들어지지 않아 테스트용 함수로 추가하였습니다.

테스트 영상

  • 장착,해제, 횃불 ON/OFF 시 체온의 변동이 있으며 HP도 줄어들지 않습니다.

느낀 점

체온 시스템을 간단하게 구현한 것 같아 마음이 쓰이지만 계절은 에셋이 부족하여..구현하지 못해 아쉽습니다.. 그리고 UI가 클릭이 되지 않아 아이템 장착,해제 시에 온도 변동 및 횃불 근처에 있는 경우 온도 변동을 못 보여드려서 아쉽습니다.. UI 클릭 안되는 오류 수정한 뒤 내일 올리려고 합니다.. 전체적으로 좀 아쉬운 시스템 구현이였습니다..

수정하였습니다.

  • 제가 조사한 내용이 맞지 않거나 잘못 된 경우에 댓글로 잘못된 점 지적해주시면 감사합니다 ! (´._.`)
profile
Unity 게임 개발자를 준비하는 취업준비생입니다..

0개의 댓글