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

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}");
}
}
}
- 플레이어의 체온 관리를 담당해 줄 클래스입니다.
- 적정온도를 가지고 있으며 낮,밤을 구현한 클래스에서 현재 시간에 따른 온도를 받아 상호작용 합니다.
- 외부요소로 체온을 더하거나 뺄 수 있게끔 함수를 만들어놓았습니다. 아이템 장착이나 횃불을 들었을 때나 불 근처에 있을 때 실행시켜주면 됩니다.
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가 체온에 따라 조절되도록 하였습니다.
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가 변동되는 모습을 볼 수 있습니다.
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 클릭 안되는 오류 수정한 뒤 내일 올리려고 합니다.. 전체적으로 좀 아쉬운 시스템 구현이였습니다..
수정하였습니다.