목차
1. ScriptableObject 문제
이름과 파일명을 맞추지 않으면 자꾸 missing이 발생하 문제
스크립터블 오브젝트로 아이템 오브젝트들을 제작했는데, 자꾸 Missing 상태가 되어 계속 할당을 다시 해 주어야 하는 문제가 발생했다.
원인: Unity에서 ScriptableObject 인스턴스를 만들면, Unity는 그 인스턴스를 .asset 파일로 저장한다.
이 .asset 파일 내부에는 ScriptableObject가 저장하는 데이터뿐 아니라, 그 ScriptableObject가 어떤 클래스를 사용하는지를 나타내는 MonoScript GUID와 FileID가 기록된다. Unity는 이 정보를 바탕으로 ScriptableObject의 타입(클래스)을 찾아 연결한다.
즉, ScriptableObject를 선언한 클래스명과 파일명이 다르면 Unity의 MonoScript 탐색 로직이 실패할 수 있다. 이 경우 에디터나 런타임에서 Missing Object가 되는 현상이 발생할 확률이 높아진다.
해결: ScriptableObject의 클래스명과 파일명을 정확히 맞춰 주어 해결했다.
참고 문헌 :
https://docs.unity3d.com/Manual/class-ScriptableObject.html

2. 네트워크 프레임 문제
멀티플레이 환경에서 모든 소방벨 차단기를 내리면 소방벨이 정지해야 했다.
그래서 “같은 프레임 안에서 모든 차단기의 상태를 검사해, 모두 내려가 있으면 소방벨 소리를 끄는” 로직을 작성했다.
그런데 서버(호스트)에서는 정상 동작했지만, 로컬 클라이언트에서는 소방벨 소리가 계속 울리는 문제가 발생했다.
원인 : 이 문제는 같은 프레임 안에서 검사하려고 한 것이 원인이었다.
Networked 변수든 RPC 요청이든, 네트워크를 통해 값을 변경하면 그 변화가 다른 클라이언트에 전파되기까지 시간이 필요하다.
하지만 이 경우에는 “변수가 변화했다”는 요청을 보낸 직후, 아직 동기화되지 않은 상태에서 같은 프레임 내에 검사를 해버렸기 때문에, 값이 변하지 않은 것으로 간주되어 로직이 올바르게 동작하지 않았다.
해결 : 문제를 해결하기 위해 RPC 콜백 이후 검사 로직을 실행했다.
3. NonAllocRayCast
습득/상호작용 가능한 물체를 판별하거나, 획득/드랍하는 코드는 화면 중앙의 커서 방향으로 Raycast를 사용했다.
처음에는 GC 할당을 방지할 수 있다는 이유로 RaycastNonAlloc()을 사용했지만, 이후 테스트해 보니 문제가 발생했다.
아이템을 습득하거나, 벽 뒤에 있는 물체를 감지하려고 할 때 원하는 대상을 제대로 찾지 못하는 현상이 있었다.
원인 : RaycastNonAlloc()이 충돌 순서를 거리순으로 정렬해 주지 않기 때문이다.
https://docs.unity3d.com/ScriptReference/Physics.RaycastNonAlloc.html

Unity의 RaycastNonAlloc()은 충돌 정보를 배열에 저장하지만, 저장 순서가 거리순으로 보장되지 않는다.
그래서 hits[0]을 기준으로 오브젝트를 조작하거나 습득하려 할 때, 올바른 대상이 아닌 다른 오브젝트가 선택되는 문제가 발생했다.
* 참고로 Physics.Raycast에서 GC가 발생하는 이유는, 주로 RaycastAll이 호출될 때 매번 새로운 배열을 생성하고 할당하기 때문이다.반면 RaycastNonAlloc은 미리 준비한 배열에 결과를 덮어쓰므로 GC가 발생하지 않는다.
해결 : Physics.Raycast로 변경
4. ItemData의 Instance화 (ScriptableObject)
아이템들이 공통적으로 갖고 있는 속성을 효율적으로 관리하기 위해 ScriptableObject를 사용했다.
다만 이렇게 하면, 같은 종류의 아이템이더라도 서로 다른 속성을 가져야 할 때 처리하기 어렵다.
예를 들어, 이번 게임에서는 길가에 떨어진 종이를 주우면 읽을 수 있었는데, 같은 종이 아이템이라도 그 내용이 모두 달랐다.
이를 해결하기 위해 PaperIndex라는 수치를 만들어 종이 아이템마다 다르게 설정하고, 해당 index에 따라 서로 다른 JSON 데이터를 가져오도록 했다. 하지만 이처럼 ScriptableObject만으로는 모든 변동 정보를 담기 어려운 점이 있었다.
원인 : ScriptableObject의 본래 목적은 값의 사본이 중복 생성되지 않도록 하여, 메모리 사용을 줄이는 데 있다.
해결 : 그래서 고정되며 변하지 않는 데이터는 ScriptableObject에, 같은 아이템 내에서도 변동 가능한 데이터나 개별 오브젝트만이 가지는 데이터는 Instance로 분리해 저장하여 문제를 해결했다.
public class ItemObject : NetworkBehaviour
{
public ItemData itemData;
[Networked] public int paperIndex {get; set; }
[Networked] public PlayerRef playerRef { get; set; }
public bool IsEmpty()
{
return itemData.itemType == ItemType.None || itemData == null;
}
public ItemInstance CreateInstance()
{
var instance = new ItemInstance(itemData);
if (itemData.itemType == ItemType.Paper)
{
instance.paperIndex = paperIndex;
}
else if (itemData.itemType == ItemType.ReviveTicket)
{
instance.playerRef = playerRef;
}
return instance;
}
public void Clear()
{
itemData = null;
}
}
public class ItemInstance
{
public ItemData data;
// 종이인 경우에만 사용
public int? paperIndex;
public PlayerRef playerRef;
public ItemInstance(ItemData data)
{
this.data = data;
}
}
* ItemData가 SO이다.
5. OutLineShader
using UnityEngine;
public class OutlineObject : MonoBehaviour
{
private Renderer rend;
public Material[] originalMaterials;
public Material outlineMaterial;
public bool outlineVisible = false;
private void Start()
{
rend = GetComponentInChildren<Renderer>();
originalMaterials = rend.materials;
}
public void ShowOutline()
{
if (outlineVisible) return;
Material[] newMaterials = new Material[originalMaterials.Length + 1];
for (int i = 0; i < originalMaterials.Length; i++)
{
newMaterials[i] = originalMaterials[i];
}
newMaterials[originalMaterials.Length] = outlineMaterial;
rend.materials = newMaterials;
outlineVisible = true;
}
public void HideOutline()
{
if (!outlineVisible) return;
rend.materials = originalMaterials;
outlineVisible = false;
}
}
다음과 같은 코드를 통해 Outline Shader 기능을 만들었다. 목표물에 커서를 올려 놓으면 목표물이 밝게 빛나면서 상호작용/습득이 가능한 오브젝트임을 알려주기 위한 기능이었다.
그러나 일부 오브젝트에서 정상적으로 동작하지 않는 문제가 있었다.
원인 : 이는 LOD(Level of Detail) 설정 때문이었다. Unity에서는 LOD를 사용해 성능을 최적화한다.
내 코드에서는 Renderer에 있는 materials 배열을 가져와, 그 위에 흰색 선을 덧입히는 방식으로 Outline을 적용했다.
대부분의 오브젝트는 LOD0가 Hierarchy 상에서 가장 위에 있었지만, 일부 오브젝트에서는 LOD2나 LOD3이 가장 먼저 탐색되는 자식으로 존재했다.
GetComponentInChildren은 Hierarchy를 순서대로 순회하며 가장 먼저 발견한 Renderer 컴포넌트를 반환하기 때문에, 가까이에서 보이게 하려면 LOD0의 Material을 바꿔야 했으나, 다른 LOD의 Renderer를 가져와서 원하는 결과를 얻지 못했다.
해결 : LOD의 순서를 바꿔서 해결했다.
6. 유니티에서 null 비교 문제
길어서 따로 포스팅을 작성했다.
null 비교를 하다가 원하는 결과가 안나와서 찾아봤다.
https://febelo0524.tistory.com/243
유니티에서의 null ( feat. fake null )
목차 유니티에서의 null 실험 유니티에서는 일반적인 null 개념과는 다른 동작이 적용된다. 이를 흔히 fake null이라고 부른다.* 비공식 용어이다. using UnityEngine;public class FakeNullTest : MonoBehaviour{ void St
febelo0524.tistory.com
7. 유니티에서의 Find 함수 문제
유니티에는 여러 Find 종류의 함수가 존재한다.
- GameObject.Find(String name)
- GameObject.FindWithTag(String tag)
- GameObject.FindObjectsWithTag(string tag)
- Object.FindObjectsOfType<T>()
원인 : 위 함수들은 모두 Hierarchy 상에서 “활성화된 오브젝트”만 찾는다.
(FindObjectsOfType<T>(true);로 하면 비활성화도 가능)
https://docs.unity3d.com/ScriptReference/GameObject.Find.html

첫 시작 지점에서, 비활성화된 오브젝트를 찾아서 할당하려 했을때 위와 같은 이유로 문제가 발생했었다.
해결 : 검사를 하는 프레임에 할당해야 하는 물체(Find로 찾는)가 active 상태를 유지하게 강제하거나, 직접 에디터로 할당해 주었다.
8. 멀티 콜라이더 문제
모두가 입장하면 특정 이벤트가 발생하도록 하기 위해, 콜라이더를 설정하고 충돌로 조건을 감지하도록 동작시켰다.
그런데 서버에서는 정상적으로 작동했지만, 클라이언트에서는 이벤트가 발생하지 않는 문제가 있었다.
원인 : 이는 콜라이더의 상태 변화를 네트워크를 통해 동기화하지 않았기 때문에 발생한 문제였다.
NetworkObject는 기본적으로 서버에서만 충돌을 검사하며, 클라이언트에서는 로컬 콜라이더끼리의 충돌이 감지되지 않는다.
해결 : 따라서 로컬 클라이언트에서 오브젝트가 이동해도, 충돌 이벤트가 발생하지 않아 이벤트 조건을 충족하지 못했다.
9. LayerMask, Layer, 비트 연산
중간중간 아이템 습득/버리기/상호작용을 위해 Raycast를 사용했는데
벽 너머로 감지가 되는 경우가 있어 이를 해결하기 위해 공부했다.
https://febelo0524.tistory.com/244
10. 코루틴과 멀티 문제

원인 : 코루틴은 일정 시간이 지난 후에 실행되는 것처럼 보이지만, 실제로는 해당 시간 동안 Unity에 실행 제어를 위임하고, 지정된 시간이 지난 뒤 다시 코루틴의 다음 부분을 실행하는 방식으로 동작한다.
따라서 위의 코드처럼 일정 시간이 지난 후에 동작하게 만들어 놓아도, Shutdown이나 중단 로직이 호출되면 코루틴이 중간에 멈춰 의도한 대로 실행되지 않을 수 있다.
해결 : 씬이 넘어가도 존재하는 함수에서 실행할 수 있게 하거나, 혹은 씬이 넘어갔을 때 초기화 해야 하는 것들을 초기화했다.
'시행착오' 카테고리의 다른 글
| 네트워크 오브젝트 동기화 관련 ( 애니메이션 ) (0) | 2025.07.07 |
|---|---|
| 네트워크 관련 ( 포톤 퓨전 , HasAuthority ) (0) | 2025.05.04 |
| Tunneling 문제를 해결하기 위한 Raycast 활용 (0) | 2025.04.14 |
| 구면 좌표계로 구현하는 카메라 / Skin Width 개념 (0) | 2025.04.05 |
| Unity < - > Node.js Socket.IO 데이터 교환 (0) | 2025.03.18 |