내일배움캠프 Unity 75일차 TIL - 팀 9와 4분의 3 - 개발일지

Wooooo·2024년 2월 13일
0

내일배움캠프Unity

목록 보기
77/94

[오늘의 키워드]

ByproductCreator 최적화
TemperatureManager 작성


[ByproductCreator 최적화]

현재 바위 오브젝트는 돌멩이 부산물을 100퍼센트 확률로 생성하도록 되어있다.
그리고 맵에는 대략 500개 정도의 바위 오브젝트가 있다.

부산물의 생성 시도는 DayCycle 클래스가 관리하는 1시간마다 시도되는데, 돌멩이의 생성확률은 100퍼센트이기 때문에 게임이 실행되고 첫 1시간째에는 500개가 넘는 프리팹이 인스턴시에이트된다.

Profiler

이 과정에서 병목이 발생하였기 때문에, ByproductCreator의 구조를 살짝 바꾸기로 했다.

먼저, 활성화된 청크에 위치해있는 부산물 생성기만 동작하도록 ManagementedObject 컴포넌트를 추가하여 관리되도록 했다.
이로 인해 한 번에 맵에 있는 모든 바위가 부산물을 생성하지 않게 되면서, 병목은 발생하지 않게 되었다.

하지만 다른 문제가 생겼다. 현재 부산물 생성기는 비활성화 이후 다시 활성화 됐을 때, 생성해놨던 부산물의 개수를 초기화하기 때문에, 플레이어가 1시간마다 청크를 왔다갔다하면서 부산물을 복사해낼 수 있었다.

이를 해결하기 위해, 부산물 생성기가 생성한 부산물을 알고 있도록 하고, 생성된 부산물은 삭제될 때 부산물 생성기에게 자신이 삭제됨을 알리도록 했다.

부산물 생성기는 자신이 생성한 부산물을 HashSet으로 관리하게 했다.

ByproductCreator.cs

using System.Collections.Generic;
using UnityEngine;

public class ByproductCreator : MonoBehaviour
{
    [Header("Data")]
    [SerializeField] private GameObject _prefab;
    [Range(0f, 1f)] [SerializeField] private float _distribution;
    [SerializeField] private float _maxRange;
    [SerializeField] private float _minRange;
    [SerializeField] private int _maxCreateCount;

    private HashSet<Byproduct> _byproducts = new();
    private DayCycle _manager;

    private void OnEnable()
    {
        if (_manager == null)
            _manager = Managers.Game.DayCycle;

        _manager.OnTimeUpdated += TryCreate;
    }

    private void OnDisable()
    {
        if (_manager != null)
            _manager.OnTimeUpdated -= TryCreate;
    }

    private void Start()
    {
        var managed = gameObject.GetOrAddComponent<ManagementedObject>();
        managed.Add(this, typeof(Behaviour));
    }

    public void TryCreate()
    {
        if (_distribution <= Random.value || _byproducts.Count >= _maxCreateCount)
            return;

        Vector3 spawnPosition = Random.onUnitSphere;
        spawnPosition.Set(spawnPosition.x, 0, spawnPosition.z);
        spawnPosition.Normalize();
        spawnPosition *= Random.Range(_minRange, _maxRange);
        spawnPosition += transform.position;

        Create(spawnPosition);
    }

    public void Create(Vector3 spawnPosition)
    {
        if (IsValidPosition(ref spawnPosition))
        {
            var obj = Managers.Game.ResourceObjectSpawner.SpawnObject(_prefab, spawnPosition).GetOrAddComponent<Byproduct>();
            obj.SetInfo(this);
            _byproducts.Add(obj);
        }
    }

    public void Remove(Byproduct byproduct)
    {
        _byproducts.Remove(byproduct);
    }

    public bool IsValidPosition(ref Vector3 pos)
    {
        pos += Vector3.up * 50f;
        if (Physics.Raycast(pos, Vector3.down, out var hit, 100f, int.MaxValue, QueryTriggerInteraction.Collide))
        {
            pos = hit.point;
            return hit.collider.gameObject.layer == 12;
        }
        else
            return false;
    }
}

Byproduct.cs

using UnityEngine;

public class Byproduct : MonoBehaviour
{
    private ByproductCreator _parent;

    public void SetInfo(ByproductCreator parent)
    {
        _parent = parent;
    }

    private void OnDestroy()
    {
        _parent.Remove(this);
    }
}

[TemperatureManager 작성]

온도를 관리하기 위한 TemperatureManager 클래스를 작성했다.

가운데에는 일반 섬이 있고, 양측에는 각각 더운 섬, 추운 섬이 있다.

더운 섬과 추운 섬의 영향력은 플레이하면서 바뀔 수 있고, 이에 따라 맵의 각 위치에는 다른 온도가 적용돼야한다.

GetTemperature()

    public float GetTemporature(Vector3 position)
    {
        float t = Vector3.Distance(_fireIsland.Position, position);
        t = Mathf.InverseLerp(0, _islandDistance, t);
        t = Mathf.Clamp01(t);
        Debug.Log(Mathf.Lerp(_fireIslandTemperature, _iceIslandTemperature, t) + _environmentTemperature);
        return Mathf.Lerp(_fireIslandTemperature, _iceIslandTemperature, t) + _environmentTemperature;
    }

처음에는 단순히 더운 섬과 추운 섬의 거리를 계산하고, 그 거리를 이용해 두 섬의 온도를 선형보간하여 중간 지점들의 온도를 계산했다.
이렇게 계산했을 때의 문제점은 두 가지 정도가 있었다.

  1. 섬마다 적용될 영향력의 계산이 난해하다.
  2. 선형보간이기 때문에 거리에 따른 온도 변화가 급격하다.

이를 해결하기 위해서 다음과 같이 변경했다.

  1. 온도의 계산은 양쪽 섬마다 영향력을 포함하여 계산한 후, 각각의 온도를 더한다.
  2. 거리에 따른 온도 변화를 Animation Curve를 이용해 Ease In Out 그래프를 적용한다.

GetTemperature()

    public float GetTemperature(Vector3 position)
    {
        float iceTemperature = GetTemperature(position, _iceIsland.Property);
        float fireTemperature = GetTemperature(position, _fireIsland.Property);
        Debug.Log(new Vector3(iceTemperature, fireTemperature, _environmentTemperature) + " " + (iceTemperature + fireTemperature + _environmentTemperature));
        return iceTemperature + fireTemperature + _environmentTemperature;
    }

    private float GetTemperature(Vector3 position, IslandProperty island)
    {
        float temperature = Vector3.Distance(position, island.center);
        temperature = Mathf.InverseLerp(0f, GetInfluenceDistance(island), temperature);
        temperature = _influenceCurve.Evaluate(temperature) * island.Temperature;
        return temperature;
    }
profile
game developer

0개의 댓글