LayerMask, 비트 연산, 싱글톤에 대해 강의를 해주셨다. 영상 강의 내용 중에 나왔던 것들인데 명확한 이해를 하지 못하고 이렇게 코드를 작성하면 이렇게 동작하는 구나~ 하고 넘어갔었어서 아주 반가웠다.
유니티에서는 총 32개(0~31)의 레이어를 활용할 수 있으며, 이를 한번에 처리하기 위해 정수형 변수의 각 비트(32비트)를 할당하여 처리합니다. 1개의 변수를 여러 개의 bool값처럼 처리하는 방법!
이렇게 하는 경우 32개의 레이어를 비교연산 하기 때문에 2진법으로 표현하여 효율적으로 처리하는 것이 보통이다. 이진수로 변환하여 비트 연산을 활용하여 사용한다.
LayerMask.GetMask(params string[] layerNames);
LayerName들을 넣어서 비트마스크를 만듭니다.
LayerMask mask = LayerMask.GetMask("Monster") | LayerMask.GetMask("Wall");
// int mask = (1 << 8) | (1 << 9); 와 동일
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100.0f, mask))
{
Debug.Log(hit1.collider.gameObject.name);
}
LayerMask.value
비트마스크값을 도출합니다(실제 이진수를 십진수로 바꿔서 계산) 예) 1025 = 1024 + 1
LayerMask.NameToLayer(string layerName)
레이어의 이름을 통해 레이어의 인덱스(비트마스크값 아님)을 도출합니다. 예) 9
LayerMask.Contains(int layerIndex)
LayerMask가 특정한 레이어 인덱스를 포함하고 있는지 확인합니다
비트 옮기기 (Shift)
비트를 왼쪽으로 옮긴다는 의미에서 비트 시프트(shift)연산은 << 처럼 나타내고, 오른쪽으로 옮기는 시프트연산은 >>처럼 나타냅니다. // 1 << 2
// 8번 레이어 활성화
// 8번 : 보스 6번 : 일반
int layerMask = 1 << 8;
layerMask |= 1 << 6;
// 사용 예시: 8번 레이어의 오브젝트만 감지하는 Raycast
if (Physics.Raycast(ray, out hit, 100, layerMask))
{
// 8번 레이어의 오브젝트와 충돌했을 때의 처리
}
비트를 1로 만들기 (OR) |
// 8번과 10번 레이어 결합
int layerMask = (1 << 8) | (1 << 10);
101000000000
// 사용 예시: 8번 또는 10번 레이어의 오브젝트만 감지하는 Raycast
if (Physics.Raycast(ray, out hit, 100, layerMask))
{
// 8번 또는 10번 레이어의 오브젝트와 충돌했을 때의 처리
}
비트가 1인지 확인하기 (AND) &
bool isLayer8Included = (layerMask & (1 << 8)) != 0;
// 사용 예시: layerMask에 8번 레이어가 포함되어 있는지 확인
if (isLayer8Included) {
// 8번 레이어가 포함된 경우의 처리
}
// 예시
// 1000000000 & 1001010101
// => 1000000000 != 0 => true
// 100000000 & 010101010 => 000000000 != 0 => false
비트 뒤집기 (NOT) ~
// 8번 레이어 제외
int layerMask = ~(1 << 8);
// 사용 예시: 8번 레이어를 제외한 모든 레이어의 오브젝트를 감지하는 Raycast
if (Physics.Raycast(ray, out hit, 100, layerMask))
{
// 8번 레이어를 제외한 오브젝트와 충돌했을 때의 처리
}
[System.Flags] // 중첩 비트 마스크를 부여한다.
public enum StatusEffects
{
None = 0,
Poisoned = 1 << 0, // 0001
Burned = 1 << 1, // 0010
Frozen = 1 << 2, // 0100
Paralyzed = 1 << 3 // 1000
}
public class StatusEffectManager
{
private StatusEffects currentEffects = StatusEffects.None;
public void AddEffect(StatusEffects effect)
{
currentEffects |= effect;
}
public void RemoveEffect(StatusEffects effect)
{
currentEffects &= ~effect;
}
public void ClearEffects()
{
currentEffects = StatusEffects.None;
}
public bool HasEffect(StatusEffects effect)
{
// 0100 & 0100 => 0100 != 0000 -> true
return (currentEffects & effect) != StatusEffects.None;
}
public void PrintEffects()
{
Console.WriteLine("Current Status Effects: " + currentEffects);
}
}
class Program
{
static void Main()
{
StatusEffectManager manager = new StatusEffectManager();
// 상태 이상 추가
manager.AddEffect(StatusEffects.Poisoned);
manager.AddEffect(StatusEffects.Frozen);
// 현재 상태 이상 출력
manager.PrintEffects(); // Poisoned, Frozen
// 상태 이상 제거
manager.RemoveEffect(StatusEffects.Poisoned);
// 상태 이상 확인
if (manager.HasEffect(StatusEffects.Frozen))
{
Console.WriteLine("The character is frozen.");
}
// 모든 상태 이상 제거
manager.ClearEffects();
}
}
싱글톤 패턴은 한 개의 인스턴스만 생성하고, 어디서든 그 인스턴스에 접근할 수 있는 디자인 패턴입니다.
대부분 사람들이 싱글톤을 쓰는 이유 : 객체 간 접근이 편하니까 / 중앙집중식 관리가 머리 안아픔
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
void Awake()
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject); // 중복 인스턴스가 생성될 경우 제거
}
}
}
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
void Awake() // 게임오브젝트가 처음으로 켜진 그 순간
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject); // 씬 전환 시 파괴되지 않도록 설정
}
else
{
Destroy(gameObject); // 중복 인스턴스가 생성될 경우 제거
}
}
public void Init(){
// 초기 세팅
}
}
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T _instance;
public static T Instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType<T>();
if (_instance == null)
{
GameObject obj = new GameObject();
// obj.name = typeof(T).Name;
_instance = obj.AddComponent<T>();
}
}
return _instance;
}
}
}
public class AudioManager : Singleton<AudioManager>
{
public void PlaySound(string soundName)
{
// 사운드 재생 로직
}
}
우선 영상 강의를 보며 간지러웠던 부분들을 긁어주셔서 좋았다. 갑자기 새로운 개념들로 구현하기 시작해서 혼란스러운 와중에 반가웠다. 모두 이해한 것은 아니지만 그래도 코드를 계속 작성해오다 보니 어느 정도의 감은 잡힌다. 역시 이해도가 부족할 땐 계속해서 반복적으로 사용해 보는 것이 최선인 듯 하다.