[ 스크립터블 오브젝트 ]
스크립터블 오브젝트(ScriptableObject)는 유니티에서 데이터를 저장하고 관리하는 일종의 컨테이너이다. 오브젝트에 대한 데이터 및 설정을 스크립터블 오브젝트에 저장하고, 이를 다른 오브젝트에 적용해서 재사용할 수 있다. 인스펙터 창에서 직접 수정하고 관리할 수 있다.
using UnityEngine;
[CreateAssetMenu(fileName = "Stat", menuName = "Monster/Defalut")]
public class StatSO : ScriptableObject
{
public string name;
public int level;
public float attackDemage;
}
예를 들어 몬스터에 대한 이름, 레벨, 공격력에 대한 데이터가 있다. 공격력이 10인 5레벨 고블린이 100마리가 있는데, 동일한 데이터가 100마리 모두에게 존재하면 메모리 낭비가 심해진다. 하지만 공통된 정보를 스크립터블 오브젝트에 저장하면 메모리도 절약할 수 있고, 코드가 깔끔해지며 관리가 쉬워진다.
CreateAssetMenu 속성을 통해 유니티 메뉴에서 새로운 에셋을 생성할 수 있다. 게임 오브젝트에 스크립터블 오브젝트를 추가하고, 세부적인 데이터 값은 인스펙터 창에서 설정할 수 있다.
[ 레이어 마스크 ]
유니티에서 설정 가능한 레이어는 32개이다. int가 32비트이기 때문에 각 비트가 하나의 레이어를 표현한다. 레이어 마스크는 특정 레이어의 정보를 비트로 표현한 것이다. 그래서 비트 마스크라고도 부른다.
레이어 간의 비교를 통해 레이어가 레이어 마스크에 포함되는지 확인해서 물리 연산, 레이 캐스트와 같은 연산을 작동되게 한다. 각 비트의 값이 1이라면 해당 레이어를 포함하고, 0이라면 포함하지 않는다.
[ 비트 시프트 연산 ]
데이터를 비트 수준에서 이동시키는 것이다. 1 << n은 1을 n번 이동시켜 n+1번째 레이어를 나타내는 비트 마스크를 생성한다. 이를 통해 특정 레이어의 연산을 쉽게 수행할 수 있다. 예를 들어 1 << 4는 1을 4번째 위치로 이동시킨 것이고, 0001에서 10000이 돼서 5번째 레이어를 의미하게 된다.
아래 글의 '시프트 연산' 목차에서 추가적인 내용을 확인할 수 있다.
2024.04.24 - [Coding/C#] - [내일배움캠프 8일차 TIL] 비트 연산자, 시프트 연산, 단축 평가, 비트 플래그, 2진법
유니티에서 비트 시프트 연산을 활용해 다음과 같은 것들을 할 수 있다.
- 충돌 검사: 특정 레이어에 속한 오브젝트만을 대상으로 충돌 검사를 진행할 수 있다
- 레이캐스팅 제어: 레이캐스트가 특정 레이어에 속한 오브젝트에게만 반응하도록 할 수 있다
- 카메라 렌더링 설정: 카메라가 특정 레이어에 속한 오브젝트만 렌더링하게 하여 게임의 시각적 요소를 세밀하게 제어할 수 있다
[ 오브젝트 풀링 ]
using System.Collections.Generic;
using UnityEngine;
public class BulletPool : MonoBehaviour
{
public static BulletPool Instance;
[SerializeField]
private GameObject bulletPrefab; // 총알 프리팹
[SerializeField]
private int poolSize = 20; // 풀 크기
private Queue<GameObject> bullets; // 총알을 보관할 큐
private void Awake()
{
Instance = this;
InitializePool();
}
// 풀 초기화
private void InitializePool()
{
bullets = new Queue<GameObject>();
for (int i = 0; i < poolSize; i++)
{
GameObject bullet = Instantiate(bulletPrefab);
bullet.SetActive(false); // 비활성화 상태로 시작
bullets.Enqueue(bullet);
}
}
// 총알 가져오기
public GameObject GetBullet()
{
if (bullets.Count > 0)
{
GameObject bullet = bullets.Dequeue(); // 큐에서 총알 가져오기
bullet.SetActive(true); // 활성화
return bullet;
}
else
{
// 모든 총알이 사용 중일 때 추가 생성 (선택)
GameObject bullet = Instantiate(bulletPrefab);
bullet.SetActive(true);
return bullet;
}
}
// 총알 반환
public void ReturnBullet(GameObject bullet)
{
bullet.SetActive(false); // 총알 비활성화
bullets.Enqueue(bullet); // 큐에 다시 추가
}
}
오브젝트 풀링의 핵심은 재사용이다. 즉 빈번하게 생성되고 파괴되는 총알과 같은 오브젝트들을 미리 생성해두고 필요할 때 가져가서 사용한 뒤, 사용이 끝나면 다시 풀에 되돌려 놓는 것을 말한다. 이는 생성 및 파괴의 비용이 큰 반복적인 작업을 줄여서 메모리 할당과 가비지 컬렉션의 성능 저하를 방지할 수 있다. 하지만 너무 적게 생성하면 오브젝트 풀링의 의미가 없고, 너무 많이 생성하면 불필요한 메모리를 사용하게 되므로 오브젝트 풀의 크기를 적절히 조절해야 한다.
[ FindGameobjectWithTag ]
using UnityEngine;
public class PlayerManager : MonoBehaviour
{
private GameObject player; // Player 오브젝트를 참조할 변수
void Awake()
{
// "Player" 태그를 가진 첫 번째 활성 오브젝트를 찾아서 저장
player = GameObject.FindGameObjectWithTag("Player");
}
void Update()
{
// Update 함수에서 player 참조를 사용
if (player != null)
{
// 플레이어의 위치를 기반으로 특정 작업을 수행
Debug.Log("Player's position: " + player.transform.position);
}
}
}
지정된 태그를 가지고 있는 첫 번째 활성 오브젝트를 반환하는 메서드이다. 이를 이용해 특정 태그를 가진 오브젝트를 빠르게 찾을 수 있다. 하지만 CPU를 많이 사용하기 때문에 Start 또는 Awake에서 한 번만 호출하고, 오브젝트를 찾고 나면 참조를 저장한 뒤 재사용한다. 만약 게임 오브젝트가 많다면 태그보다는 레이어나 다른 메서드를 사용하는 것이 효율적일 수 있다.
[ 회고 ]
강의를 듣고 개념을 정리하는 것까지는 이해가 되는데, 막상 이거를 코드로 작성해 보려고 하면 다시 또 이해가 되지 않는다. 단순히 이론을 정리하고 이해하는 것과 이를 실제로 사용하는 것은 큰 차이가 있는 것 같다. 이론으로 기본 개념과 작동 원리 등을 알아 놓고, 코딩은 손으로 직접 해보는 것만큼 실력이 빠르게 느는 방법이 없는 것 같아서 역시 계속해서 사용해 봐야 할 것 같다.
오늘은 목표로 했던 유니티 개발 입문 강의 완강을 하지 못했다. 생각보다 이론 부분이 어려워서 이것들을 이해하고 따라서 코드를 작성해 보고 나니 시간이 꽤 흘러있었다. 팀 프로젝트에서 적과 관련된 움직임, 공격과 같은 모든 기능을 구현하는 역할을 맡아서, 시간을 내서 강의를 들어봐야겠다.
'Coding > Unity' 카테고리의 다른 글
[내일배움캠프 32일차 TIL] 아이템 사용 트러블 슈팅 (0) | 2024.05.30 |
---|---|
[내일배움캠프 30일차 TIL] 인벤토리 창 트러블 슈팅 (0) | 2024.05.28 |
[내일배움캠프 21일차 TIL] 애니메이션 적용, 이름 입력, 씬 전환 시 데이터 유지, DontDestroyOnLoad (0) | 2024.05.14 |
[내일배움캠프 20일차 TIL] 카메라 추적, 보간 적용 (0) | 2024.05.13 |
[내일배움캠프 19일차 TIL] New Input System, 이벤트 처리 (0) | 2024.05.10 |