Unity 숙련 주차 - Survival 게임 낮과 밤 구현하기

Amberjack·2024년 2월 2일
0

Unity

목록 보기
26/44

👨‍🏫 AnimationCurve

AnimationCurve는 Unity에서 애니메이션의 키프레임(Keyframe)을 사용하여 값을 보간(interpolate)하는데 사용되는 클래스이다. 이 클래스를 사용하여 시간에 따라 값을 부드럽게 변화시키는 커브를 정의하고, 이를 기반으로 애니메이션을 만들 수 있다.

Unity Documentation : https://docs.unity3d.com/kr/2021.3/Manual/animeditor-AnimationCurves.html

보간(interpolate)

두 점을 연결하는 방법을 보간이라 한다. 두 데이터의 사이를 잇는 방법이라고 생각하면 편하다.

AnimationCurve 클래스의 기본적인 구성 요소는 다음과 같다.

  • 키프레임(Keyframe): 시간에 따른 값을 정의하는 점을 의미. 키프레임은 시간(t)과 해당 시간에 대응하는 값(value)으로 이루어짐.

  • 보간 방식(Interpolation Mode): 인접한 키프레임 사이의 값을 보간하는 방법을 지정한다. 기본적으로는 Cubic Bezier 보간이 사용되며, 선형, 스텝, 등 다양한 보간 방식을 선택할 수 있다.

AnimationCurve 사용법

주요한 AnimationCurve 메서드 및 사용법은 다음과 같다.

  1. AddKey : 새로운 키 프레임을 추가한다.
using UnityEngine;

public class ExampleScript : MonoBehaviour
{
    private AnimationCurve curve;

    private void Start()
    {
        // 새로운 AnimationCurve 생성
        curve = new AnimationCurve();

        // 키프레임 추가 (시간, 값)
        curve.AddKey(0f, 0f);
        curve.AddKey(1f, 1f);
    }

    private void Update()
    {
        // 시간에 따라 값을 보간하여 출력
        float time = Time.time;
        float value = curve.Evaluate(time);
        Debug.Log("Time: " + time + ", Value: " + value);
    }
}
  1. Evaluate : 특정 시간에 해당하는 값을 보간하여 반환한다.
  2. keys : 키프레임의 배열을 가져온다. 이를 통해 키프레임들을 추가, 수정, 삭제할 수 있다.

AnimationCurve 는 주로 애니메이션 시간에 따른 값을 정의하는데 사용되며, 특히 더 복잡한 애니메이션을 제어하기 위해 사용되는 경우가 많다. 예를 들어, 오브젝트의 움직임, 크기 조정, 회전 등에 대한 애니메이션을 정의하거나, 재미있는 게임 요소들에 활용할 수 있다.

🔅 낮과 밤 구현하기

빈 오브젝트를 생성하고 _DayNightCycle라고 이름을 변경한다.
이후, _DayNightCycle 밑에 DirectionalLight를 Sun으로 이름을 변경한 뒤 넣어준다.

이후 Sun을 복제하여 Moon으로 이름을 변경해주고 SetActive를 False로 변경해둔다.

이후 Moon을 다음과 같이 수정해준다.

Intensity : 빛의 강도. 0.2로 변경해준다.

Realtime Shadows → Strength : 그림자가 지는 정도. 우리는 저녁이기 때문에 그림자가 지는 정도를 약하게 할 것이다. 0.5로 변경한다.

색상은 푸르스름한 계열로 변경해준다.

확인해보기


Sun과는 다르게 많이 어두워진 것을 확인할 수 있다.

DayNightCycle.cs 작성하기

Environments 밑에 DayNightCycle.cs를 생성한다.

public class DayNightCycle : MonoBehaviour
{
    // Slider로 값을 조절하기 위해 Range를 정해준다.
    [Range(0.0f, 1.0f)]
    public float time;
    public float fullDayLength;     // 하루의 길이
    public float startTime = 0.4f;
    private float timeRate;
    public Vector3 noon;    // 자정의 각도

    [Header("Sun")]
    public Light sun;
    public Gradient sunColor;   // 그라데이션
    public AnimationCurve sunIntensity;  // AnimationCurve를 통해 그래프를 생성할 수 있다. 해당 그래프에서 원하는 값들을 Time 값을 통해 꺼내올 수 있다.

    [Header("Moon")]
    public Light moon;
    public Gradient moonColor;   // 그라데이션
    public AnimationCurve moonIntensity;  // AnimationCurve를 통해 그래프를 생성할 수 있다. 해당 그래프에서 원하는 값들을 Time 값을 통해 꺼내올 수 있다.

    [Header("Other Lighting")]
    public AnimationCurve lightingIntensityMultiplier;      // 풍경광
    public AnimationCurve reflectionIntensityMultiplier;    // 반사광

    private void Start()
    {
        timeRate = 1.0f / fullDayLength;
        time = startTime;
    }

    private void Update()
    {
        // time을 계속해서 증가시켜주는 데, time을 퍼센테이지로 사용하기 위해 1.0f의 나머지 연산을 해준다.
        time = (time + timeRate * Time.deltaTime) % 1.0f;

        UpdateLighting(sun, sunColor, sunIntensity);
        UpdateLighting(moon, moonColor, moonIntensity);

        // Sun과 Moon의 빛의 차이가 얼마 없는 것을 수정하는 코드
        // ambientIntensity : 풍경광.
        // reflectionIntensity : 반사광.
        // 시간에 따라 풍경광과 반사광을 변화시켜 낮과 밤의 밝기 차이를 최대화
        RenderSettings.ambientIntensity = lightingIntensityMultiplier.Evaluate(time);
        RenderSettings.reflectionIntensity = reflectionIntensityMultiplier.Evaluate(time);
    }

    private void UpdateLighting(Light lightSource, Gradient colorGradient, AnimationCurve intensityCurve)
    {
        lightSource.transform.eulerAngles = (time - (lightSource == sun ? 0.25f : 0.75f)) * noon * 4.0f;
        lightSource.color = colorGradient.Evaluate(time);
        lightSource.intensity = intensity;

        GameObject go = lightSource.gameObject;
        if (lightSource.intensity == 0 && go.activeInHierarchy) go.SetActive(false);
        else if (lightSource.intensity > 0 && !go.activeInHierarchy) go.SetActive(true);
    }
}

UpdateLighting에 대한 추가 설명

UpdateLighting 메서드에
lightSource.transform.eulerAngles = (time - (lightSource == sun ? 0.25f : 0.75f)) * noon * 4.0f; 라는 코드가 있다.
이 코드는 현재 lightSource의 transform의 eulerAngles를 변경하는 코드이다.
전체 게임을 360도인 원으로 생각을 해보자. 그러면 정오와 자정을 기준으로 4분할을 할 수 있다.

이 때, noon은 정오이기 때문에, 결국 noon의 각도는 90도가 된다.
따라서 noon * 4.0f는 전체 360도를 의미하게 된다.

다음으로 (time - (lightSource == sun ? 0.25f : 0.75f)) 에 대해 생각해보자.
lightSource가 sun인지 판별을 해서 sun일 경우 0.25f라는 값을 넘겨준다. 이 0.25f는 전체 시간 1.0f의 1 / 4 지점이다. 결국 게임 상에서 정오일 때가 시간으로 0.25f임을 알려주는 것이다. 반대로 0.75f일 경우는 자정이라는 것을 알 수 있다.

결국 lightSource.transform.eulerAngles = (time - (lightSource == sun ? 0.25f : 0.75f)) * noon * 4.0f; 라는 코드는
(time - 낮 시간 (혹은 밤 시간)) * noon * 4.0f; 로 정리할 수 있다.

결국 해당 코드는 시간에 따른 lightSource의 현재 위치를 계산하기 위한 코드이다.

예를 들어 현재 시간이 태양이 뜨고 정오가 되기 전 시간이라면 태양의 위치는 위의 그림에서 4 사분면에 위치한다는 것을 생각할 수 있다.

따라서 time - (lightSource == sun ? 0.25f : 0.75f)) * noon * 4.0f 를 실행하면 Unity에서도 태양이 떠서 정오가 되기 전 어딘가의 위치가 나오게 될 것이다.

🌃 낮과 밤 세팅하기

유니티로 돌아와서 _DayNightCycle을 수정하자.

Sun

Sun의 색상을 세팅하는데, 노란색으로 색을 변경해준 뒤, 가운데 지점에 더블 클릭을 통해 포인트를 하나 더 생성하고 색을 흰색으로 변경해준다. → 정오(가운데) 때 태양이 가장 밝도록 색을 변화시켜주는 것.

이후 Sun Intensity를 설정하는데, 아래 모양과 같이 세팅한다.

다만, 해당 모양이 없을 경우, 우상향 그래프를 선택한 뒤, Add Key를 통해 반대편 그래프를 생성한다.

이후, Edit Key를 통해 Key 값들을 수정하는데 왼쪽의 time을 0.2, 오른쪽은 0.8로 변경한다.

이후, 왼쪽 Key의 Right Tangent를 Linear로, 오른쪽 Key의 Left Tangent를 Linear로 변경해준다.

Moon

moon의 색상은 Sun과 비슷하게 세팅해준다.

Moon Intensity은 아래와 같이 설정해준다.



Moon은 Sun이 없을 때 떴다가, Sun이 있을 때 없어야하기 때문에 Sun과 반대 모양임을 알 수 있다. 또한, Moon은 Sun보다 더 어둡기 때문에, value 값 또한 0.2로 적게 세팅해준다.

Other Lighting

Lighting Intenstiy Multiplier도 세팅해준다.

time이 0.4, 0.6일 때 value가 1이 되도록 세팅해준다.
그러면 그래프가 아래와 같이 생기게 되는데, 0.4의 Right Tangent를 Linear로, 0.6의 Left Tangent를 Linear로 설정한다.

그리고 0.6 지점의 Right Tangent를 free로 변경하여 경사가 완만해지도록 조정해준다.

이후 시작 지점과 끝 지점을 각각 0, 1로 변경해준다.

해당 AnimationCurve를 Preset으로 저장하여 Reflection Intenstiy Multiplier에도 적용해준다.

0개의 댓글