본문 바로가기
카테고리 없음

'GameSaveData' is missing the class attribute 'ExtensionOfNativeClass'! 해결

by mazayong 2025. 10. 26.

프로젝트를 리팩토링하는데 그 전에 위와 같은 에러 로그가 떠서 해당 에러를 해결하기로 하였다.

 

1. 문제 발생 상황

2. 로그의 원인

3. 해결하지 않을 경우

4. 고민한 방법

4.1. SceneManagerEx의 Clear() 내부 GC.Collect()

4.2. GameSaveData 설정에서 이어하기 부분 관련 코드 누락

4.3. 데이터 형식 수정

4.4. [System.Serializable] 추가

4.5. 씬 내부 오브젝트들에서 사용되지 않는 데이터 객체 삭제

4.6. 컴파일러 캐시 삭제

4.7. 메타데이터 삭제

5. 후기

 

 

 

1. 문제 발생 상황

게임 시작 후 씬 이동이 이루어진다.

StartScene -> LoadingScene -> Ch0Scene으로 이동한다.

StartScene에서 게임 시작 버튼을 누를 경우, SaveDataManager과 PlayerManager에서 GameSaveData를 설정 및 초기화하고, SceneManagerEx에서 Ch0Scene을 로드한다. (해당 매니저에서 큐와 asyncOperation을 이용해 LoadingScene을 거쳐 Ch0Scene으로 이동시키고, 이 때 GameSaveData도 같이 이동시킨다.) 

LoadingScene으로 이동했을 때, LoadingScene에서 로딩이 끝났을 때, Ch0Scene으로 이동했을 때 총 3번 에러 로그가 떴다. 

 

 

2. 로그의 원인

해당 로그 메세지는 보통 MonoBehaviour나 ScriptableObject를 상속받지 않은 일반 C# 클래스를 Unity 직렬화 시스템이 처리하려고 할 때의 경고이다. 

 

 

3. 해결하지 않을 경우

게임엔진 내부 시스템이 해당 클래스를 잘못 해석하고 있다는 의미이므로 해당 클래스를 런타임에 제대로 읽어오지 못해 게임 데이터에 관한 기능들을 수행할 수 없을 것이다.

 

 

 

4. 고민한 방법

 

4.1. SceneManagerEx의 Clear() 내부 GC.Collect()

Clear()함수는 아래와 같이 이루어져 있다.

public void Clear()
{
    Resources.UnloadUnusedAssets();
    System.GC.Collect();
}

해당 함수는 씬 전환 완료 직후에 이루어지도록 설정되어 있다.

System.GC.Collect()는 C#의 GC를 실행하여 메모리 내에서 참조하지 않는 객체를 정리한다. 

(해당 함수를 쓴 이유는 미리 메모리 정리를 할 수 있지 않을까라는 생각에서 쓴 것 같다..프로파일러를 보니 CPU 같은 자원도 갑자기 많이 잡아먹고, GC가 해당 시점에 실행된다는 보장이 없으며, 메모리 누수로 인해 GC 대상이 아닌 객체를 null로 만들 수 있어 굉장한 안티패턴이라는 생각이 든다.)

 

Resources.UnloadUnusedAsstes()는 사용하지 않는 에셋의 메모리 버전을 해제하는 함수이다.

이 경우는 데이터가 ScriptableObject 기반이면 내려버릴 수 있겠지만, .sav데이터에 json 형식으로 사용되도록 설정했기 때문에 해당 함수는 두고 GC 호출 제거만 제거했다.

 

++ 그리고 해당 장르는 비주얼노벨인데, GC가 일어날 때 프리징이 일어날 수 있다고 해서 프리징 방지를 위해서라도 없앴다. (그리고 2d 비주얼 노벨이어서 상대적으로 자원도 덜 들어가기도 하니 말이다.)

(프리징 : GC가 작동할 때 게임의 메인 스레드가 일시적으로 멈추는데, 데이터 교체가 많은 시점에 GC를 강제하면 큰 규모의 GC 작업이 발생하여 눈에 띄는 프레임 끊김이 발생하는 현상)

 

 

 

4.2. GameSaveData 설정에서 이어하기 부분 관련 코드 누락

급하게 마감하느라 세이브데이터 부분 테스트하기가 어려웠었다... 그래서 확인해보니 이어하기 상황일 때 로드 여부와 세이브데이터의 현재 인덱스를 가져오는 건 설정되었지만 현재 데이터 객체를 가져오는 부분이 설정되지 않아서 해당 부분을 추가했다.

 

 

 

4.3. 데이터 형식 수정

클래스 형식으로 데이터를 저장할 때, List<GameObject>와 같은 방식으로 게임오브젝트를 저장했었다. 그런데 이 때 유니티의 계층 구조에 종속되어 있어 직렬화할 수 없기 때문에 string으로 수정했다.

 

 

4.4. [System.Serializable] 추가

GameSaveData 위에 해당 속성을 추가해 해당 클래스를 직렬화 대상으로 간주하도록 처리했다.

(직렬화 : 데이터 구조, 객체 상태가 에디터에 저장되고 나중에 재구성할 수 있는 포맷으로 자동으로 변환되는 프로세스)

 

그리고 컴파일러 오류를 갱신하기 위해 Reimport All도 같이 시도했다.

 

 

4.5. 씬 내부 오브젝트들에서 사용되지 않는 데이터 객체 삭제

Player 객체 내부에 GameSaveData의 잘못된 참조가 포함되어 있어서 해당 객체를 삭제했다.

 

 

4.6. 컴파일러 캐시 삭제

이전에 잘못된 상태에서 클래스를 읽은 캐시를 기억하고 있어서 컴파일러가 다시 재설정된 클래스 정의를 완전히 다시 읽지 못했다고 생각했다.

unity 에디터를 종료하고, 프로젝트 폴더 안 library를 삭제하고 재실행했다.

 

그리고 해당 클래스를 사용한 코드들을 다시 확인했다. (단독 클래스로 설정되어 있는지, MonoBehaviour 상속받는 클래스처럼 다뤘는지)

 

 

4.7. 메타데이터 삭제

결국 메타데이터 파일을 강제로 초기화했다.

unity를 종료하고, GameSaveData.cs, GameSaveData.cs.meta와 library를 같이 삭제한 후 다시 unity를 실행했다.

그리고 GameSaveData.cs 파일을 다시 만들어서 처리했다.

그랬더니 드디어 오류가 해결되었다.

 

 

5. 후기

결국 게임엔진 내 c#, cpp로 처리하는 부분 사이에서 꼬여버린 게 문제였다. 처리하는데 내가 몰랐고 잘못 알았던 것을 다시 알게 되었고, 문제를 해결해서 다행이다. 그래도 원인을 알았으니 다음에 이런 문제가 생긴다면 더 빠르게 해결하길 기대한다.