ByproductCreator
최적화
TemperatureManager
작성
현재 바위
오브젝트는 돌멩이
부산물을 100퍼센트 확률로 생성하도록 되어있다.
그리고 맵에는 대략 500개 정도의 바위 오브젝트가 있다.
부산물의 생성 시도는 DayCycle
클래스가 관리하는 1시간마다 시도되는데, 돌멩이의 생성확률은 100퍼센트이기 때문에 게임이 실행되고 첫 1시간째에는 500개가 넘는 프리팹이 인스턴시에이트된다.
이 과정에서 병목이 발생하였기 때문에, ByproductCreator
의 구조를 살짝 바꾸기로 했다.
먼저, 활성화된 청크에 위치해있는 부산물 생성기만 동작하도록 ManagementedObject
컴포넌트를 추가하여 관리되도록 했다.
이로 인해 한 번에 맵에 있는 모든 바위가 부산물을 생성하지 않게 되면서, 병목은 발생하지 않게 되었다.
하지만 다른 문제가 생겼다. 현재 부산물 생성기는 비활성화 이후 다시 활성화 됐을 때, 생성해놨던 부산물의 개수를 초기화하기 때문에, 플레이어가 1시간마다 청크를 왔다갔다하면서 부산물을 복사해낼 수 있었다.
이를 해결하기 위해, 부산물 생성기가 생성한 부산물을 알고 있도록 하고, 생성된 부산물은 삭제될 때 부산물 생성기에게 자신이 삭제됨을 알리도록 했다.
부산물 생성기는 자신이 생성한 부산물을 HashSet
으로 관리하게 했다.
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;
}
}
using UnityEngine;
public class Byproduct : MonoBehaviour
{
private ByproductCreator _parent;
public void SetInfo(ByproductCreator parent)
{
_parent = parent;
}
private void OnDestroy()
{
_parent.Remove(this);
}
}
온도를 관리하기 위한 TemperatureManager
클래스를 작성했다.
가운데에는 일반 섬이 있고, 양측에는 각각 더운 섬, 추운 섬이 있다.
더운 섬과 추운 섬의 영향력은 플레이하면서 바뀔 수 있고, 이에 따라 맵의 각 위치에는 다른 온도가 적용돼야한다.
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;
}
처음에는 단순히 더운 섬과 추운 섬의 거리를 계산하고, 그 거리를 이용해 두 섬의 온도를 선형보간하여 중간 지점들의 온도를 계산했다.
이렇게 계산했을 때의 문제점은 두 가지 정도가 있었다.
이를 해결하기 위해서 다음과 같이 변경했다.
Ease In Out
그래프를 적용한다. 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;
}