오브젝트를 미리 생성하고 필요할때 꺼내서 사용하고 사용이 다되면 풀에 반납하여 비활성화 시켜주는 것을
오브젝트 풀링이라고 배워왔다.
하지만 유니티에서는 이 오브젝트 풀을 사용 할수 있게 패키지로 제공하고 있다.
using UnityEngine.Pool;
해당 패키지를 사용하기 위해서는 해당 코드를 작성하고 사용해야된다.
해당 Pool를 사용하려면 어떡해 해야되는가?
public IObjectPool<GameObject> objectPool;
public ObjectPool<GameObject> pool;
변수로 선언할때는 이 두방식을 이용하여 변수를 초기화 하여 사용하면된다.
초기화시 필요한 생성자 매개변수들
pool = new ObjectPool<GameObject>(CreateObject, GetObject, ReleaseObject, DestroyObject, collectionChecks, minSize, maxSize);
objectPool = new ObjectPool<GameObject>(CreateObject, GetObject, ReleaseObject, DestroyObject, collectionChecks, minSize, maxSize);
이런식으로 선언을 하면된다.
namespace UnityEngine.Pool
{
public interface IObjectPool<T> where T : class
{
int CountInactive { get; }
T Get();
PooledObject<T> Get(out T v);
void Release(T element);
void Clear();
}
}
해당 코드가 IObjectPool의 내부 구조이다.
using System.Collections.Generic;
using System.ComponentModel;
using UnityEditor.Presets;
using UnityEngine;
using UnityEngine.Pool;
//최소 50개의 오브젝트 수 보장, 부족할 경우 누적 300개까지 추가 생성, 300개가 넘어갈 경우 가장 오래전에 생성된 오브젝트를 반환 후 재사용
public class Week2_OjbectPool_Q4 : Singleton<Week2_OjbectPool_Q4>
{
[SerializeField] private GameObject prefab;
public ObjectPool<GameObject> pool;
public bool collectionChecks = true;
private const int minSize = 50;
private const int maxSize = 300;
private GameObject container;
void Awake()
{
container = new GameObject(prefab.name + "_Container");
/*
createFunc: 오브젝트 생성 함수 (Func)
actionOnGet: 풀에서 오브젝트를 가져오는 함수 (Action)
actionOnRelease: 오브젝트를 비활성화할 때 호출하는 함수 (Action)
actionOnDestroy: 오브젝트 파괴 함수 (Action)
collectionCheck: 중복 반환 체크 (bool)
defaultCapacity: ObjectPool의 List<T>에 미리 자리 만드는것(오브젝트 생성x)(int)
maxSize: 저장할 오브젝트의 최대 갯수 (int)
*/
pool = new ObjectPool<GameObject>(CreateObject, GetObject, ReleaseObject, DestroyObject, collectionChecks, minSize, maxSize);
}
private GameObject CreateObject()
{
// [요구스펙 1] Create Object
//Instantiate();
GameObject obj = Instantiate(prefab, container.transform);
return obj;
}
public void GetObject(GameObject obj)
{
// [요구스펙 2] Get Object
obj.gameObject.SetActive(true);
}
public void ReleaseObject(GameObject obj)
{
// [요구스펙 3] Release Object
obj.gameObject.SetActive(false);
}
public void DestroyObject(GameObject obj)
{
//오브젝트 파괴 함수
Destroy(obj.gameObject);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
for (int i = 0; i < 10; i++)
{
pool.Get();
}
}
}
}
해당 코드는 UnityEngine.Pool을 이용하여 오브젝트 풀을 구성한 예제 코드이다.
using UnityEngine;
using TMPro;
public class DebugText : MonoBehaviour)
{
public TMP_Text tmpT;
public TextMeshProUGUI tmpUgui;
tmpT.text = "원하는 텍스트 적어넣기";
tmpUgui.text = "원하는 텍스트 적어넣기2";
}
public int ObstcalseLayer = 5;
public int PeopleLayer = 8;
LayerMask layerMask = 1 << ObstcalseLayer; //장애물(5번레이어)를 비트마스크로 생성후 레이어마스크에 초기화
raycastObj = ~raycastObj; //반전비트를 이용하여 장애물 레이어를 0으로 변경
//레이캐스트에 layerMask를 인자값으로 넣어 장애물 레이어를 투과하여 레이캐스트를 발사
Physics.Raycast (transform.position, transform.TransformDirection (Vector3.forward), hit, Mathf.Infinity, layerMask);
이렇게 작성된다.
3.카메라 렌더링 설정 : 카메라가 특정 레이어의 오브젝트를 렌더링하도록 설정하여, 게임의 시각적 요소를 세밀하게 제어를 가능하게 한다.
Q.레이어마스크를 어떡해 쓰는거야?
A.우선 오브젝트에 있는 레이어를 코드내에서 사용하기위해 비트마스크로 생성해야한다.
1 << n // 1을 n번째 비트 위치로 시프트(이동)한다는 의미
//즉 n은 레이어(0~32 layer정수값을 의미), n번쨰 레이어를 나타내는 비트마스크로 생성한다는 의미
//예시
//충돌된 객체의 게임오브젝트에서 레이어를 초기화
int layer = collision.gameObject.layer;
1 << layer; //호출받아온 레이어를 비트마스크로 생성하여 사용
해당 코드처럼 1 << n 을 사용하여 비트마스크로 생성하는것이다.
그럼 비트마스크로 생성까지 완료했으면 그 이후에는 어떡해 해야되나?
해당 비트마스크를 이용하여 검사를 진행하면된다.
위에서 설명한 논리 연산자들을 활용하는 것이다.
AND (&): 특정 레이어의 존재 여부 확인할떄 사용 OR (|): 새로운 레이어를 추가할 때 사용 XOR (^): 두 레이어의 차이를 찾을떄 사용 ( 비교하는 대상이 다르면 1 ) NOT (~): 특정 레이어를 제외시킬때 사용 ( 모든비트 반전 )
보통 이 논리연산자들 이 특징들을 사용한다.
public LayerMask levelCollisonLayer; //비교할 기준 레이어
private void OnTriggerEnter2D(Collider2D collision)
{
//LayerMask의 value 프로퍼티는 해당레이어의 비트마스크를 십진수를 반환해준다.
if (IsLayerMatched(levelCollisonLayer.value, collision.gameObject.layer))
{
//레이어가 일치하면 true
}
//레이어가 불일치하면 false
}
private bool IsLayerMatched(int value, int layer)
{
//value는 비트마스크이기 때문에 그대로 사용 , layer는 비트마스크로 생성
//(value | 1 << layer) : | 연산자로 기준 레이어(value)와 객체와 충돌한 레이어(layer) OR연산
//OR 연산된 비트마스크 가 기준 레이어와 일치할경우 True
return value == (value | 1 << layer);
}
해당 코드는 OR 연산을 충돌된 객체 레이어가 기준 레이어와 일치할 경우 동작하는 코드이다.
OR 연산은 레이어를 추가할때 사용 하는데 , 이런식으로도 원하지 않는 레이어는 제외할수 있게 구성 할 수도 있다.
Animator Class에 있는 메서드로 애니메이션의 파라미터의 이름(문자열)을 해시값으로 변경하여
최적화 및 성능 개선에 도움을 주는 메서드이다.
Q.Animator의 함수라는건 알겠어, 근데 왜 사용하는거야?
A.위에서 말했듯이 최적화 및 성능개선이라고 했다.
애니메이션의 파라미터를 코드로 사용할때
SetBool("isWalking",true);
이런식으로 문자열을 그대로 넣어서 사용하게 되면 파라미터의 문자열을 서로 비교하는 작업을 하는데
이때 처리비용이 크기 때문에 비효율적이라고 하는것이다.
하지만 StringToHash를 사용하여 문자열을 Hash값으로 변경해주면
int(정수)는 한 자리만 체크하므로 최적화에 매우 효율적이기 때문이다.
또한 문자열 -> 정수( 21억 범위)로 변홤하여 충돌(중복)이 일어날 확률이 매우 낮다!!! 문자열 -> 정수(Hash값)은 항상 동일한 해시값을 반환하기에 다른 파라미터를 건들일 일도 없다.
Q.StringToHash 단점이 있을까?
A.
정수 -> 문자열 반환에는 사용하지 못하는 단점이 있다, StringToHash는 일방향으로 작동하는 메서드이기 때문에
p.s
정수(Hash값) -> 문자열은 항상 동일한 문자열을 반환하지 않기에 충돌이 발생 할 수 있다.
Q.StringToHash는 어떡해 사용해?
public class TopDownAnimationController : AnimationController
{
//해당 객체에서 사용할 Class 위에 문자열을 해시값으로 변환한후 정적변수로 선언해놓기
private static readonly int isWalking = Animator.StringToHash("isWalking");
private static readonly int isHit = Animator.StringToHash("isHit");
private static readonly int attack = Animator.StringToHash("attack");
//필요할때 해당 변수값을 가져와서 사용하기
private void Move(Vector2 vector)
{
animator.SetBool(isWalking);
}
}
이런식으로 미리 문자열을 해시값으로 변환하여 선언한 뒤 (정적으로 생성하여 컴파일하면서 동시에 생성)