
LayerMask.NameToLayer("NAME") // 특정 레이어 선택시
int layerMask = 1 << LayerMask.NameToLayer("Emeny");
// 다수의 레이어 선택시
LayerMask mask = LayerMask.GetMask("Name1", "Name2");
// example
if (Physics.Raycast(ray, out hit, distance, layerMask)) { ... }
LayerMask mask = LayerMask.GetMask("Name1", "Name2");
// Name1 이 8번 레이어면?
// 1 << 8 (256)
int layermask = LayerMask.NameToLayer("Name1");
string name = LayerMask.LayerToName(8);
int bitMask = enemyLayer.value | obstacleLayer.value;
int layer = 7; // 0, 1, 2 를 말한다.
int layer = 1 << 7; // 이렇게 해야 7번만 선택한다.
public static class Extentions
{
public static bool Contains(this LayerMask mask, int layer)
{
return (mask.value & 1 << layer) != 0;
}
public static bool Contains(this LayerMask mask, GameObject obj)
{
return (mask.value & 1 << obj.layer) != 0;
}
}
//
if (layerMask.Contains(mask, 8)) { ... }
if (mask.Contains('Enemy', 몬스터)) { ... }
if (target == "" || target == "" || ...) // 너무 많은 비교 연산
if (mask.Contains(target, layer)) // 간단하고 빠른 bit 연산
메인 카메라 외 다른 카메라 하나로 동시에 보는 것.
즉, 랜더링 두배가 될 수도 있어 굉장히 무겁고 비쌀 수 있다.
[Sub Camera] -> [Target Texture] -> [Render] -> [Meterial or UI -> RawImage]
미니맵

CCTV

Scope

Screen Space-Ovelay
Screen Space-Camera⭐⭐
World Space⭐⭐
Constant Pixel Size
Scale With Screen Size ⭐⭐⭐
Constant Physical Size
Profile 내 UI 파트 확인.Layer: UI의 위치와 크기를 계산Render: 실제 Mesh 생성 시간Canvas.SendWillRenderCanvases
- Mission1
- Object Cube, Sphere 생성
- LayerMask 선언 (서로 다르게)
- Capsule (Player) 가 Ray 를 forward 방향으로 쐈을 때, Cube 와 Sphere 의 맞는 대상을 1개만 하기.
using UnityEngine;
public class Missions : MonoBehaviour
{
private Ray _ray;
private Vector3 _rayPosOffset;
private void Start()
{
_rayPosOffset = new Vector3(0, 0.5f, 0);
_ray = new Ray(transform.position + _rayPosOffset, transform.forward);
}
private void Update()
{
DetectCube();
}
private void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawRay(_ray.origin, _ray.direction * 20f);
}
private void DetectCube()
{
_ray.origin = transform.position + _rayPosOffset;
_ray.direction = transform.forward;
LayerMask layerMask = LayerMask.GetMask("Cube");
RaycastHit hit;
if (Physics.Raycast(_ray, out hit, 20f, layerMask))
{
Debug.Log(hit.collider.gameObject.name);
}
}
}
- Mission2
- Project Setting 에 Layer 간 충돌 설정에 의한 동작 확인 하기
- Mission3
- Extentions 로 제작한 코드 활용 예제 해보기.
-> 심화와 엮어 진행.
- Mission4
- 원 2개 (Sphere Layer).
- 큐브 2개 (Cube Layer).
- 키보드 1번을 누르면 Cube/Sphere 에게 독데미지 부여 (코루틴 활용. 디버그 로그 활용)
- 키보드 2번을 누르면 Cube 만 독이 풀린다.
// mission.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class Missions : MonoBehaviour
{
private List<(Coroutine cor, Collider col)> _poisionCorouts;
private void Awake()
{
_poisionCorouts = new List<(Coroutine, Collider)>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Alpha1)) AllTargetsForPoision();
if (Input.GetKeyDown(KeyCode.Alpha2)) CurePoisionOnlyCube();
}
private void Start()
{
_rayPosOffset = new Vector3(0, 0.5f, 0);
_ray = new Ray(transform.position + _rayPosOffset, transform.forward);
}
private void AllTargetsForPoision()
{
LayerMask cubeSphereLayer = LayerMask.GetMask("Cube", "Sphere");
LayerMask layerMask;
Collider[] colliders = Physics.OverlapSphere(transform.position, 20f);
foreach (Collider col in colliders)
{
layerMask = col.gameObject.layer;
if (layerMask.Contains(cubeSphereLayer))
{
Status status = col.GetComponent<Status>();
if (!status.IsPoision)
{
_poisionCorouts.Add((StartCoroutine(Poision(col.gameObject)), col));
status.IsPoision = !status.IsPoision;
}
}
}
}
private void CurePoisionOnlyCube()
{
LayerMask layerMask;
int cubeLayer = LayerMask.NameToLayer("Cube");
foreach ((Coroutine cor, Collider col) in _poisionCorouts)
{
layerMask = col.gameObject.layer;
if (layerMask == cubeLayer)
{
StopCoroutine(cor);
}
}
}
private IEnumerator Poision(GameObject obj)
{
while (true)
{
Debug.Log($"{obj.name}은(는) 중독되었다.");
yield return new WaitForSeconds(0.5f);
}
}
}
// Status.cs
using UnityEngine;
public class Status : MonoBehaviour
{
public bool IsPoision;
}
이왕에 독 데미지를 전역으로 주는거 데미지를 띄워보자 라는 생각으로 AI 함께 진행해 본 것
// DamageController.cs -> 싱글톤
using System.Collections.Generic;
using UnityEngine;
public class DamageController : MonoBehaviour
{
public static DamageController Instance; // 어디서든 접근 가능하게 싱글톤
public GameObject textPrefab;
public Transform canvasParent;
private List<OnOffDamage> pool = new List<OnOffDamage>();
void Awake()
{
Instance = this;
}
public void ShowDamage(int amount, Vector3 worldPos)
{
OnOffDamage targetText = null;
foreach (var t in pool)
{
if (!t.gameObject.activeSelf)
{
targetText = t;
break;
}
}
if (targetText == null)
{
GameObject obj = Instantiate(textPrefab, canvasParent);
targetText = obj.GetComponent<OnOffDamage>();
pool.Add(targetText);
}
targetText.Setup(amount, worldPos);
}
}
// OnOffDamage.cs -> 실제 Text 띄우는 것
using TMPro;
using UnityEngine;
public class OnOffDamage : MonoBehaviour
{
private TextMeshProUGUI textMesh;
void Start()
{
textMesh = GetComponent<TextMeshProUGUI>();
}
public void Setup(int amount, Vector3 worldPos)
{
if (textMesh == null)
{
Debug.LogWarning("TextMeshProUGUI was not assigned!");
textMesh = GetComponent<TextMeshProUGUI>();
}
textMesh.text = $"- {amount}";
transform.position = Camera.main.WorldToScreenPoint(worldPos + Vector3.up * 0.5f);
gameObject.SetActive(true);
Invoke("DisableText", 1f);
}
void DisableText()
{
gameObject.SetActive(false);
}
}
// status.cs
using UnityEngine;
public class Status : MonoBehaviour
{
public bool IsPoision;
public void TakeDamage() // 간단히 추가
{
DamageController.Instance.ShowDamage(10, transform.position);
}
}
Object Polling과 싱글톤을 활용하였음. 데미지로 보여져야하기 때문에 Font 등이 모두 같은 크기로 보여지는 것이 좋을 것이라 생각하여 Canvas 의 RenderMode 는 Screen Space - Overlay 를 채택.
진행하며 의문 사항에 대해 몇가지 AI 에게 질문하였고, 그 중 유의미한 질문과 정리는 아래와 같음
Q. Object Polling 에 의해 캐쉬로 생성된 Object 들은 결국
CleanUp이 되어야 할 텐데, 어떤 기준으로 어느 타이밍에 하는 것이 적절 한가?
A. "시간"에 따른 CleanUp 보다는 "사건"에 따른 CleanUp 이 더 유효하다.Q. 자료 구조는 보통 어떻게 가져가는게 좋나?
A.Queue가 가장 많이 선호되며,LinkedList가 쓰이는 경우도 있다. 동일한 Object Type 당 하나의Queue로 관리하는 것이 보통이다.Dictionary<Object, Queue>로