뉴비겜톤에 참가하며 진행하였던 프로젝트의 문제점을 코드적으로 확인하고 개선을 수행, 고려해보는 작업을 진행해보려한다.
https://nunsori.tistory.com/16
[뉴비겜톤] 멘토링 내용, 진행내용 정리
경일게임일게임IT아카데미에서 주최한 뉴비겜톤에 참가하고,프로젝트를 진행하며, 개발 부분에서 느끼게되었던 점을 정리하려한다. 팀개요이름 : 풀리퀘스트인원 : 개발2, 기획1, 아트2프로젝트
nunsori.tistory.com
- Tag, Layer정리, DetectorClass

같은 이름의 tag가 형식이 다르게 삽이이 되어있고, 명칭도 이 태그나 레이어가 어떤것인지 정확히 알아볼 수 없는 것들이 존재한다.
tag와 layer에 둘 다 enemey 라는 네이밍이 존재하며 혼동이 일어날 수 있다.
보통 레이어는 layer collision matrix를 수정하여, 각 레이어간 불필요한 물리연산을 진행하지 않도록 진행 해줄 수 있고, 그룹별로 관리의 진행이라면
태그는 단일 게임오브젝트를 관리를 진행한다.
이를 위해 각 tag, layer개선이 필요하다.
https://docs.unity3d.com/kr/2021.3/Manual/Tags.html
태그 - Unity 매뉴얼
태그(Tag) 는 한 개 이상의 게임 오브젝트 에 할당할 수 있는 레퍼런스 단어입니다. 예를 들어, 플레이어가 조작하는 캐릭터에 “Player”를, 플레이어가 조작하지 않는 캐릭터에 “Enemy” 태그를
docs.unity3d.com
https://docs.unity3d.com/kr/2021.3/Manual/Layers.html
레이어 - Unity 매뉴얼
레이어는 씬에서 게임 오브젝트를 분리할 수 있는 툴입니다. UI 및 스크립트를 통해 레이어를 사용하여 씬 내의 게임 오브젝트가 서로 상호작용하는 방식을 편집할 수 있습니다.
docs.unity3d.com
using System;
using UnityEngine;
using System.Collections.Generic;
using System.ComponentModel;
public class Detector : MonoBehaviour
{
[Obsolete("String is not good at performance."),HideInInspector,SerializeField] private string tagName = "";
public Collider2D selfCollider;
public GameObject detected; // 가장 가까운 감지 오브젝트
private readonly List<GameObject> detectedObjects = new(); // 감지 중인 오브젝트들
public event Action<Collider2D> DetectAction;
public event Action<Collider2D> DetectOutAction;
void OnEnable()
{
selfCollider = gameObject.GetComponent<Collider2D>();
}
void OnTriggerEnter2D(Collider2D collision)
{
string[] names = tagName.Split(',');
for (int i = 0; i < names.Length; i++)
{
if (string.IsNullOrEmpty(collision.tag)) continue;
if (collision.CompareTag(names[i]))
{
if (!detectedObjects.Contains(collision.gameObject))
detectedObjects.Add(collision.gameObject);
detected = GetClosestObject(detectedObjects);
DetectAction?.Invoke(collision);
}
}
}
void OnTriggerExit2D(Collider2D collision)
{
string[] names = tagName.Split(',');
for (int i = 0; i < names.Length; i++)
{
if (string.IsNullOrEmpty(collision.tag)) continue;
if (collision.CompareTag(names[i]))
{
DetectOutAction?.Invoke(collision);
detectedObjects.Remove(collision.gameObject);
if (detected == collision.gameObject)
detected = null;
DetectUpdate();
}
}
}
void DetectUpdate()
{
if (detectedObjects.Count > 0)
{
detected = GetClosestObject(detectedObjects);
}
else
{
detected = null;
}
}
private GameObject GetClosestObject(List<GameObject> objects)
{
GameObject closest = null;
float closestSqrDist = float.MaxValue;
Vector3 myPos = transform.position;
for (int i = 0; i < objects.Count; i++)
{
if (objects[i] == null) continue; // Destroy 등으로 null된 경우 안전 처리
float sqrDist = (objects[i].transform.position - myPos).sqrMagnitude;
if (sqrDist < closestSqrDist)
{
closestSqrDist = sqrDist;
closest = objects[i];
}
}
return closest;
}
}
다음 detector class는
해당 컴포넌트가 등록되어있는 collider를 바탕으로,
tagName변수에 있는 태그를 감지하여, 저장해놓는 스크립트이다.
다만 여기서 ','를 이용하여 여러 태그를 감지할 수 있도록 설정해주었는데,
string연산을 진행하며 성능저하가 일어날 수 있다.
또한, ','로 여러 태그를 확인한다는 것이 처음 코드를 접하는 입장에서, inspector내에서만 해당 컴포넌트의 기능을 파악하기에는 어렵기 때문에 이를 좀더 inpector상에서 알기 쉽게,
그리고 tag뿐만 아니라 layer도 감지할 수 있도록 기능개선을 진행해볼 예정이다.
개선이후코드는 다음과 같다.
using System;
using UnityEngine;
using System.Collections.Generic;
[Serializable]
public class TagListWrapper { public List<string> value; }
[Serializable]
public class LayerMaskListWrapper { public List<LayerMask> value; }
public class Detector : MonoBehaviour
{
public enum DetectType { TagOnly, LayerOnly, TagAndLayer }
public enum DetectCondition { AND, OR }
//구 버전 detect변수, 교체필요
[Obsolete("String is not good at performance."), HideInInspector, SerializeField] private string tagName = "";
[Header("Detector Basic Setting")]
[SerializeField]
private DetectType detectType = DetectType.TagAndLayer;
[ShowIfEnum("detectType", DetectType.TagOnly, DetectType.TagAndLayer)]
//detectType이 TagOnly 또는 TagAndLayer일 때만 노출
[SerializeField, Tooltip("감지할 tag 이름 목록")]
private TagListWrapper tagList;
[ShowIfEnum("detectType", DetectType.LayerOnly, DetectType.TagAndLayer)]
[SerializeField, Tooltip("감지할 layermask 목록")]
private LayerMaskListWrapper layerMasks;
[ShowIfEnum("detectType", DetectType.TagAndLayer)]
[Header("조건 설정")]
[SerializeField, Tooltip("tag와 layer 하나만 맞아도 감지 /n tag와 layer 모두 다 맞아야 감지")]
private DetectCondition detectCondition = DetectCondition.OR;
[HideInInspector]
public Collider2D selfCollider;
[HideInInspector]
public GameObject detected; // 가장 가까운 감지 오브젝트
private readonly HashSet<GameObject> detectedObjects = new(); // 감지 중인 오브젝트들
public event Action<Collider2D> DetectAction;
public event Action<Collider2D> DetectOutAction;
void OnDisable()
{
DetectAction = null;
DetectOutAction = null;
}
void OnEnable()
{
selfCollider = gameObject.GetComponent<Collider2D>();
}
void OnTriggerEnter2D(Collider2D collision)
{
//Event 호출여부
if (DetectCheck(collision))
{
if (!detectedObjects.Contains(collision.gameObject))
detectedObjects.Add(collision.gameObject);
detected = GetClosestObject(detectedObjects);
DetectAction?.Invoke(collision);
}
}
void OnTriggerExit2D(Collider2D collision)
{
if (DetectCheck(collision))
{
DetectOutAction?.Invoke(collision);
detectedObjects.Remove(collision.gameObject);
if (detected == collision.gameObject)
detected = null;
DetectUpdate();
}
}
private bool DetectCheck(Collider2D collision)
{
//TODO : 마이그레이션 이후 삭제 필요
if (!string.IsNullOrEmpty(tagName))
{
string[] names = tagName.Split(',');
for (int i = 0; i < names.Length; i++)
{
if (string.IsNullOrEmpty(collision.tag)) continue;
if (collision.CompareTag(names[i]))
{
return true;
}
}
}
bool tagDetect = false;
bool LayerDetect = false;
//tag감지
if (detectType == DetectType.TagOnly || detectType == DetectType.TagAndLayer)
{
for (int i = 0; i < tagList.value.Count; i++)
{
if (string.IsNullOrEmpty(collision.tag)) continue;
if (collision.tag == tagList.value[i]) tagDetect = true;
}
}
//layermask감지
if (detectType == DetectType.LayerOnly || detectType == DetectType.TagAndLayer)
{
for (int i = 0; i < layerMasks.value.Count; i++)
{
if (collision.gameObject.layer < 0)
{
Debug.LogError("Detector class error detected layer idx is minus\nDetector Name : " + gameObject.name + " / Detected name : " + collision.gameObject.name);
continue;
}
if (collision.gameObject.layer == layerMasks.value[i])
{
LayerDetect = true;
}
}
}
//감지여부 리턴하기
switch (detectType)
{
case DetectType.TagOnly: return tagDetect; break;
case DetectType.LayerOnly: return LayerDetect; break;
case DetectType.TagAndLayer:
if (detectCondition == DetectCondition.AND)
{
return tagDetect && LayerDetect;
}
else if (detectCondition == DetectCondition.OR)
{
return tagDetect || LayerDetect;
}
break;
}
Debug.LogError("Detector Class DetectCheck Function Error");
return false;
}
private void DetectUpdate()
{
if (detectedObjects.Count > 0)
{
detected = GetClosestObject(detectedObjects);
}
else
{
detected = null;
}
}
private GameObject GetClosestObject(HashSet<GameObject> objects)
{
GameObject closest = null;
float closestSqrDist = float.MaxValue;
Vector3 myPos = transform.position;
var e = objects.GetEnumerator();
while (e.MoveNext())
{
var t = e.Current;
if (t == null) continue;
float sqrDist = (t.transform.position - myPos).sqrMagnitude;
if (sqrDist < closestSqrDist)
{
closestSqrDist = sqrDist;
closest = t;
}
}
// List 일때
// for (int i = 0; i < objects.Count; i++)
// {
// if (objects[i] == null) continue; // Destroy 등으로 null된 경우 안전 처리
// float sqrDist = (objects[i].transform.position - myPos).sqrMagnitude;
// if (sqrDist < closestSqrDist)
// {
// closestSqrDist = sqrDist;
// closest = objects[i];
// }
// }
return closest;
}
}
주요 변경점은 다음과 같다.
- inspector내에서 사용하지 않는 public변수에 HideInInspector attribute추가
- detect 기능을 tag, layer 모두 감지 가능하도록 확장
- DetectCheck함수를 통해, 원하는 물체감지여부 함수를 따로 분리 (기존에 OnTriggerEnter, Exit함수에 각각 존재하였지만 공통 기능을 함수로 따로 분리하여 코드길이가 길어지는 것을 방지, 함수명에 알맞은 기능으로 분리하였다.)
- 이벤트 구독해제 추가
-커스텀 attribute추가를 통해, detecttype에 따라 필요한 변수만 inspector에서 보이게끔 조정



- 위와같이 에디터상에서도 DetectType에따라 List나 조건관련 변수가 보이고, 보이지 않게끔조정이 되어, 해당 타입에 필요한 변수들만 띄워주는 것을 볼 수 있다.
- 복잡한 방 입장 판별, 방 구조


위와같이 방의 입장판별, 이동을 막는 부분에 대한 콜라이더가 매우 지저분하며, 하나로 통합이 되지 않았다.
또한 다른오브젝트에 카메라 연출, fade같은 스크립트가 나뉘어져 있고, roominfo class에 등록되어있지 않아 roominfo가 초기화되어 맵이 다시 쓰여지더라도, 아래 오브젝트들은 초기화되지 않는 이슈가 있다.
이를 해결하기 위해 roominfo를 간결화 하고, 각 콜라이더를 단순화, 나뉘어져 있는 기능들을 하나로 통합할 필요성이 있다.
--이후 작성 진행중--
'개발 > Unity' 카테고리의 다른 글
| [Unity] Merge-Is-Mine 핵심기능 리팩토링, 비주얼 개선 (0) | 2025.09.02 |
|---|---|
| [Unity] 프로젝트 소스코드 관련 간단 검토자료 (0) | 2025.08.13 |
| [Unity] Merge-Is-Mine 맵 생성 시스템 (0) | 2025.08.03 |
| [Unity] ios 빌드시 xcworkspace가 생성안될때 (0) | 2024.04.18 |
| [Unity] ios 빌드 오류 , Undefined symbol: _OBJC_CLASS_$_WKWebView 관련 (0) | 2024.03.25 |