Unity에서 현실감 있는 낮과 밤 시스템 만들기

JJW·2024년 12월 5일
0

Unity

목록 보기
11/34
post-thumbnail

게임 개발에서 시간의 흐름은 플레이어의 몰입감을 높이는 중요한 요소입니다.
오늘은 Unity 3D에서 낮과 밤이 자연스럽게 전환되는 시스템을 구현하는 방법을 알아보겠습니다.


목표

  • 시간이 흐르면서 낮과 밤이 자연스럽게 전환되는 효과
  • 횃불과 같이 어두울 때 주변을 밝힐 수 있도록 하는 물체 구현

필요한 요소

1. Directional Light

  • Directional Light는 Unity에서 태양빛이나 달빛 같은, 장면 전체에 걸쳐 균일하게 빛을 비추는 광원 역할을 하는 Light Component입니다.
    이는 게임 씬에서 현실적인 조명 환경을 구현할 때 가장 기본적이면서도 중요한 요소입니다.
  • 특징
    • 빛의 위치보다 방향이 중요한 조명입니다.
    • 씬 전체에 동일한 세기를 가진 평행광을 제공하며, 특정 거리나 영역에 제한되지 않습니다.
    • 그림자를 생성할 수 있어, 게임 환경에서 빛의 현실감을 더할 수 있습니다.
    • Realtime, Baked, Mixed 모드를 통해 다양한 조명 효과를 구현할 수 있습니다.

2. Skybox

  • Skybox는 Unity에서 게임 씬의 하늘과 먼 배경을 표현하기 위해 사용하는 환경 맵 입니다.
    단순히 하늘을 보여주는 역할만 하는 것이 아니라, 씬의 분위기를 조성하고 몰입감을 높이는 데 중요한 요소입니다.
  • 특징

    • 플레이어가 어느 방향을 보더라도 하늘과 먼 배경이 자연스럽게 보입니다.
    • 스카이박스는 실제로는 아주 멀리 있는 텍스처처럼 보이지만, 실제로는 씬에 별도의 3D 오브젝트를 배치하지 않고도 이러한 효과를 제공하므로 성능에 영향을 덜 미칩니다.
    • Lighting > Environment Lighting에서 스카이박스가 씬의 기본적인 조명과 반사를 설정하는 데 사용됩니다.
  • 종류

    • 6-Sided 스카이박스
      • 하늘을 구성하는 텍스처를 6개의 면으로 나누어 사용하는 방식입니다
    • Procedural 스카이박스
      • 시간대(낮, 밤, 저녁 등)와 날씨에 따라 스카이박스를 변경하는 데 유용합니다
  • 사용 에셋
    FastSky - Procedural Sky and Clouds URP


구현

1. Procedural Sky 설정

  • 저의 경우에는 FastSky - Procedural Sky and Clouds URP 에셋에 존재하는
    Procedural Sky를 가져와서 사용했습니다.
  • 사용하고자 하는 Scene에 해당 파일을 가지고 온 뒤
    Window -> Rendering -> Environment의 부분을 따라하시면 됩니다.
  • 그리고 Fast_Skt_Sun_Color의 클래스 내용은 태양의 방향에 따른 밝기와 색상을 조절하는 클래스입니다.

2. 시간 흐름 구현

public class DayNightCycle : MonoBehaviour
{
    [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;                        // 태양이 완전히 지는 각도

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

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

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

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

        UpdateTimeDisplay();
        UpdateSunPosition();
    }

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

    void UpdateTimeDisplay()
    {
        int hours = Mathf.FloorToInt(timeOfDay * 24f);
        int minutes = Mathf.FloorToInt((timeOfDay * 1440f) % 60);
        Text_Time.text = $"{aliveDay}일 {hours:D2}:{minutes:D2}";
    }
}
  • 하루를 0 ~ 1의 범위로 표현하였습니다.
  • 0 (자정), 0.25 (오전 6시), 0.5 (정오), 0.75 (오후 6시) 1 (자정)
  • Light의 Rotation X의 값을 바꾸면서 태양의 각도를 Lerp하게 움직이게 하여 자연스럽게 해가 지고 뜨는것 처럼 구현하였습니다.

3. 횃불 구현

  • 횃불의 경우에는 자신의 주위로 빛을 비쳐줘야하기 때문에 광원 타입을
    Point 타입으로 설정합니다.
  • 플레이어 하위에 Light -> Point Light를 생성해줍니다.

  • Color 같은 경우에는 횃불이기 때문에 Color값을 주황 - 빨강 사이에 Color로 설정합니다.
  • Range의 경우 빛의 범위 입니다. 적당히 설정해줍시다.
  • Intensity의 경우에는 빛의 밝기 입니다. 이것도 적당히 설정해줍시다.

  • 횃불이라긴 보다는 랜턴의 느낌?이 강하여 횃불 처럼 빛이 흔들리는 효과가 필요해보입니다.
public class TorchFlicker : MonoBehaviour
{
    public Light torchLight;
    public float minIntensity = 1f;
    public float maxIntensity = 5f;
    public float flickerSpeed = 5f;

    private float noiseTime;

    void Update()
    {
        if (torchLight != null)
        {
            noiseTime += flickerSpeed * Time.deltaTime;
            float noiseValue = Mathf.PerlinNoise(noiseTime, 0f);

            // 값에 따라 밝기 변경
            torchLight.intensity = Mathf.Lerp(minIntensity, maxIntensity, noiseValue);
        }
    }
}
  • Mathf.PerlinNoise()를 사용하여 연속적이고 부드러운 랜덤값을 사용하여 반환하도록 합니다.
  • minIntensity의 값과 maxIntensity값의 차이가 크면 흔들리는 느낌이 강화됩니다.
  • flickerSpeed 변화의 속도입니다.

  • 아직 횃불이라는 아이템이 없어 오른손에서만 빛이 나옵니다. 그래도 빛의 범위가 일정한 것 보다는 자연스럽게 나옵니다.

테스트

  • 해의 각도를 움직이면서 자연스럽게 그림자의 위치도 바뀌며
    UI도 시간 흐름을 잘 표현하고 있으며 시간 흐름에 따른 낮,밤도 잘 구현된 것 같습니다.

느낀 점

시간 흐름 클래스에서 옵저버 패턴을 사용하여 UI,캐릭터,몬스터의 시간에 따른 행동을 명령하는 것도 괜찮아 보입니다. 밤이되면 몬스터의 경우 잠을 잔다던지 더 공격적으로 변하는 등..
개인 프로젝트를 구현하면서 유용하게 쓰일 듯 합니다.
스카이박스는 6-Sided 밖에 몰랐는데 Procedural 스카이 박스가 더 좋은 것 같다고 느껴집니다..

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

0개의 댓글