24.03.06 TIL - Unity : 2D 게임에서 Universal RP로 낮밤 & 조명 표현

JJwoo·2024년 3월 6일
post-thumbnail

2D 환경에서 조명을 표현하고 싶어서 Universal RP를 설치하여 구현해보기로 함

  1. 확장 프로그램 설치 ( 프로젝트 시작 시 URP를 포함하지 않았었다. )


  1. Assets에 URP Asset 생성, Renderer를 Assets 인스펙터에 적절하게 부여

  1. Project Setting - Graphics에 URP 관련 탭이 생긴 것을 확인 할 수 있다.


  1. 하이어라키 창에서 Ligth - Global Light 2D 오브젝트를 생성해주었다. ( Light Global라는 이름의 오브젝트 )

전역 조명이라고 생각하면 좋을 듯 하다.

그 외에 집 오브젝트나 상점 오브젝트에도 부여해줄 각각의 조명용 오브젝트를 생성해주었다.
( 플레이어, 일부 몬스터 등에게도 하위 오브젝트로 조명을 부여하였다 )

사진을 보면 조명 아이콘이 보인다.

Freeform Light 2D는 자유롭게 조명의 범위를 커스텀 조절 할 수 있다.

Spot은 말 그대로 점이고

Sprite는 원하는 모양의 Sprite를 기준으로 조명을 표현 할 수 있는데, 여기서는 Freeform을 사용하였다.

노란 사각형 안에 흰 사각형이 Freeform으로 지정한 조명의 범위이다.

본인은 FalloffFalloff Strength, Intensity를 통해 빛의 강도(밝기)나, 빛의 강도가 거리에 따라 감소하는 것을 조절했다.


낮과 밤의 구현은 다른 작업자 분이 GameManager 스크립트 안에서 시간으로 정의 하였기에, 그 부분에 내 코드를 넣어보기로 했다.

전체 코드

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance;
    public Light2D globalLight; // Light2D 컴포넌트 참조
    public Color dayColor = Color.white; // 낮 색
    public Color nightColor = new Color(30 / 255f, 130 / 255f, 255 / 255f); // 밤 색
    public float transitionDuration = 10.0f; // 낮,밤 전환 시간

    public static int PlayerGold = 500;

    [Header("Time")]
    public TMP_Text TimeText;
    //public GameObject DarkImage;

    [Header("Player")]
    public TMP_Text PlayerGoldText;

    private float realTime = 1;//실제시간몇초당 10분
    private float time;
    private int gameTime = 360;

    public static bool isNight = false; //자는버튼

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else if (Instance != null)
        {
            Destroy(this.gameObject);
        }
    }

    private void Update()
    {
        if (isNight == true)
        {
            time = 0;
            gameTime = 360;
            isNight = false;
            //AlphaReset();
            TimeCheck();
        }
        else
            TimeCheck();

        // 모든 MobSpawnManager에 대해 몬스터를 리스폰
        if (IsMorning())
        {
            foreach (var spawnManager in FindObjectsOfType<MobSpawnManager>())
            // MobSpawnManager 스크립트가 달려있는 모든 오브젝트를 찾아서 각각 개체에 반복문 수행  
            {
                spawnManager.SpawnMonsters();
            }
        }
        UpdateLightingTransition();
    }

    public void TimeCheck()
    {
        time += Time.deltaTime;

        if (time >= realTime)
        {
            gameTime += 10;
            time = 0;

            //AlphaChange();
            TimeTextChange();
        }
    }

    public void TimeTextChange(bool isDay = false)
    {
        string text;

        if (gameTime >= 1440)
            text = "오전  ";
        else if (gameTime >= 720)
            text = "오후  ";
        else
            text = "오전  ";

        string hour;
        hour = (gameTime / 60).ToString("D2");

        if (gameTime / 60 >= 13)
            hour = ((gameTime / 60) % 12).ToString("D2");

        string minute;
        minute = (gameTime % 60).ToString("D2");

        TimeText.text = text + hour + " : " + minute;

        if (isDay == true)
            TimeText.text = "오전  06 : 00";
    }



    private void UpdateLightingTransition()
    {
        float currentHour = gameTime / 60.0f; // 현재 시간을 시간 단위로 변환
        float lerpFactor;

        // 오전 6시부터 낮 색상으로 전환 시작
        if (currentHour >= 6f && currentHour < 7f) // 오전 6시부터 1시간 동안
        {
            lerpFactor = (currentHour - 6f) / 1f; // 오전 6시부터 1시간 동안 보간 계산
            globalLight.color = Color.Lerp(nightColor, dayColor, lerpFactor);
        }
        else if (currentHour >= 17f && currentHour < 18f) // 오후 5시부터 1시간 동안 밤으로 전환 시작
        {
            lerpFactor = (currentHour - 17f) / 1f; // 오후 5시부터 1시간 동안 보간
            globalLight.color = Color.Lerp(dayColor, nightColor, lerpFactor);
        }
        else if (currentHour >= 7f && currentHour < 17f) // 오전 7시부터 오후 5시까지는 낮 색상 유지
        {
            globalLight.color = dayColor;
        }
        else // 그 외 시간에는 밤 색상 유지
        {
            globalLight.color = nightColor;
        }
    }



    public bool IsMorning() // 오전 7시부터 아침, 아침 되면 몬스터 리스폰
    {
        return gameTime >= 420 && gameTime < 430;
    }

    public bool IsNight() // 오후 6시부터 다음날 오전 6시까지 밤으로 판단
    {
        return gameTime >= 1080 || gameTime < 360; // 1080은 오후 6시, 360은 오전 6시를 의미
    }

게임매니저 스크립트에서는 여러가지 기능을 담당하는데,

일단 시간이 흘러가게하는 기능

시간에 따라 다른 스크립트에서 몬스터를 리스폰 하는 기능

그리고 Light2D 컴포넌트 참조 하여 낮과 밤의 색상을 정하고, 이를 특정 시간에 특성 초 동안 Global Light의 색상이 변하게 하는 기능이다.

이 조명 구현과 관련 된 코드만 따로 작성하면

using UnityEngine;
using UnityEngine.Rendering.Universal; // Light2D를 사용하기 위한 네임스페이스

public class GameManager : MonoBehaviour
{

    public Light2D globalLight; // 전역 조명을 위한 Light2D 컴포넌트
    public Color dayColor = Color.white; // 낮 시간의 조명 색상
    public Color nightColor = new Color(30 / 255f, 130 / 255f, 255 / 255f); // 밤 시간의 조명 색상
    
    private int gameTime = 360; // 게임 내 시간, 360은 오전 6시를 나타냄


    private void Update()
    {
        TimeCheck(); // 시간 업데이트 체크
        UpdateLightingTransition(); // 조명 전환 업데이트
    }

    public void TimeCheck()
    {
        //게임 시간 체크, 업데이트
    }

    private void UpdateLightingTransition()
    {
        float currentHour = gameTime / 60.0f; // 게임 시간을 시간 단위로 변환
        float lerpFactor; // 보간을 위한 인자

        // 밤에서 낮으로의 색상 전환
        if (currentHour >= 6f && currentHour < 7f) // 오전 6시부터 7시 사이
        {
            lerpFactor = (currentHour - 6f) / 1f; // 1시간 동안 보간 계산
            globalLight.color = Color.Lerp(nightColor, dayColor, lerpFactor);
        }
        else if (currentHour >= 17f && currentHour < 18f) // 오후 5시부터 6시 사이
        {
            lerpFactor = (currentHour - 17f) / 1f; // 1시간 동안 보간 계산
            globalLight.color = Color.Lerp(dayColor, nightColor, lerpFactor);
        }
        else if (currentHour >= 7f && currentHour < 17f) // 오전 7시부터 오후 5시까지는 낮 색상 유지
        {
            globalLight.color = dayColor;
        }
        else // 그 외의 시간에는 밤 색상 유지
        {
            globalLight.color = nightColor;
        }
    }
}

    public bool IsMorning() // 오전 7시부터 아침, 아침 되면 몬스터 리스폰
    {
        return gameTime >= 420 && gameTime < 430;
    }

    public bool IsNight() // 오후 6시부터 다음날 오전 6시까지 밤으로 판단
    {
        return gameTime >= 1080 || gameTime < 360; // 1080은 오후 6시, 360은 오전 6시를 의미
    }

UpdateLightingTransition() 에서
게임 시간을 좀 더 알아보기 쉽게 시간 단위로 바꾼 현재 시간 변수와, 보간 인자 등의 변수들을 설정하여

인게임 시간인 1시간 (실제시간 6초) 동안 보간을 통해 원하는 색상의 전환이 이루어지도록 하였다.

다만 게임 내에서 Global 조명을 그대로 두면, 낮 시간에도 각각의 오브젝트 (플레이어, 건물 등)에게 조명이 보이는 효과가 있어서, 낮 시간에만 이를 숨겨주는 새로운 스크립트를 만들었다.

Light Handler 스크립트 : 낮 시간에만 조명을 숨기기 위함

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class LightHandler : MonoBehaviour
{
    void Update()
    {
        if (GameManager.Instance.IsNight()) // 밤일 때
        {
            foreach (var light in FindObjectsOfType<Light2D>())
            {
                if (light.CompareTag("Light"))
                {
                    light.enabled = true;
                }
            }
        }
        else // 낮일 때
        {
            foreach (var light in FindObjectsOfType<Light2D>())
            {
                if (light.CompareTag("Light"))
                {
                    light.enabled = false;
                }
            }
        }
    }
}


평상시 else 일 때는 플레이어, 건물 등 조명들을 모두 숨겨져있지만

If에서 GameManager.Instance.IsNight() 를 참조하여, 즉 밤일 때는 Light Tag가 있는 모든 오브젝트를 찾아서 다시 활성화 해주었다.

FindObjectsOfType은 빛 오브젝트 숫자가 적기에 사용해보았는데, 다음에는 성능적으로 더 나은 방법을 생각해보아야겠다.


실행해보기

게임 플레이 전 화면, 미세하게 조명이 보인다.

게임 시작시 아침 화면, 조명이나 기타 UI들이 다 숨겨지는 모습.

오후 5~6시 동안 화면이 점점 어두워지면서

밤으로 전환되고 조명이 켜진 모습.

원하는 대로 잘 구현되었다.

profile
개발 모코코

0개의 댓글