전체 글

유니티에서 작업을 하던 중, 스크립트에서 실행 순서의 문제 때문에 진행이 안되는 일이 생겼다.

 

씬 내에서 게임모드를 만들었는데, 해당 게임 모드에서 특정 변수를 받아와야지만, 스크립트를 정상적으로

 

실행할 수 있는 상황이었다. 그림으로 표현하자면 다음과 같다.

스크립트의 실행 순서가 꼬였다

해서 방법을 찾던 중, 프로젝트 자체 세팅에서 Execution Order라는 것을 설정할 수 있다는 것을 찾게되었다.

Edit > ProjectSetting > Script Execution Order 탭에서 수정할 수 있다.

Script Execution Order

재밌는건 여기서 수정한 Order는 각 cs 파일들의 meta파일에 값이 지정되는 것을 볼 수 있었다.

meta 파일의 executionOrder

기본적으로 대부분의 스크립트들의 order는 0으로 설정되어있는 것도 확인할 수 있었다.

List<T> someList = new List<T>(new T[4]);
foreach(var data in someList){
    if(data == null){
        someList.Remove(data);
    }
}

위와 같이 작성을 하게 되면, List의 Remove메서드의 특징으로 인해서

InvalidOperationException: Collection was modified; enumeration operation may not execute.

위와 같은 에러를 만나게된다.

foreach(var data in someList){
    if(data == null){
        someList.Remove(data);
        break;
    }
}

단순히 한 가지 아이템을 찾기 위한 것이라면, 위와 같이 break를 활용하여, 간단히 에러 없이 넘어갈 수 있겠지만,

위에서 작성한 코드처럼 List에 존재하는 목록을 모두 확인해야하는 경우가 있을 수 있다.

이런 경우라면 foreach는 사용하는 것이 어렵다.

그래서, for문을 활용하여 작성을 하는 것이 좋다.

여기서, 반복문 작성 시의 유의점이 있다. List의 Remove나 RemoveAt 메서드는 해당 item을 지우고 난 뒤에 자동으로

List의 크기를 줄여주기 때문에 주의해야하는 점이 있는데,

for(int index = 0; i < someList.Count; index++){
    if(someList[i] == null){
        someList.RemoveAt(index);
    }
}

일반적으로 위와 같이 for문을 작성하게되는데, 위와 같이 작성을 하게되면, RemoveAt을 통해서 줄어버린 List의 index에 접근을

못하고 넘어가버리는 경우가 발생하기때문에 List의 모든 항목을 점검하지 못하게되는 문제가 생긴다.

for(int index = someList.Count - 1; index >= 0; index--){
    if(someList[i] == null){
        someList.RemoveAt(index);
    }
}

따라서, 위와 같이 작성하는 것이 안전하게 List의 모든 목록을 확인할 수 있는 형태이다.

[유니티 플랫폼 별 경로들 모음]

더보기

[윈도우 에디터]
Application.persistentDataPath : 사용자디렉토리/AppData/LocalLow/회사이름/프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 프로젝트디렉토리/Assets
Application.streamingAssetsPath : 프로젝트디렉토리/Assets/StreamingAssets
파일 읽기 쓰기 가능


[윈도우 응용프로그램]
Application.persistentDataPath : 사용자디렉토리/AppData/LocalLow/회사이름/프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 실행파일/실행파일_Data
Application.streamingAssetsPath : 실행파일/실행파일_Data/StreamingAssets
파일 읽기 쓰기 가능

[맥 에디터]
Application.persistentDataPath : 사용자디렉토리/Library/Caches/unity.회사이름.프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 프로젝트디렉토리/Assets
Application.streamingAssetsPath : 프로젝트디렉토리/Assets/StreamingAssets
파일 읽기 쓰기 가능

[맥 응용프로그램]
Application.persistentDataPath : 사용자디렉토리/Library/Caches/unity.회사이름.프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 실행파일.app/Contents
Application.streamingAssetsPath : 실행파일.app/Contents/Data/StreamingAssets
파일 읽기 쓰기 가능

[웹 플랫폼]
웹에서는 명시적인 파일 쓰기 불가능. 애셋번들로 해야함
Application.persistentDataPath : /
Application.dataPath : unity3d파일이 있는 폴더
Application.streamingAssetsPath : 값 없음.

[안드로이드 External]
Application.persistentDataPath : /mnt/sdcard/Android/data/번들이름/files
파일 읽기 쓰기 가능
Application.dataPath : /data/app/번들이름-번호.apk
Application.streamingAssetsPath : jar:file:///data/app/번들이름.apk!/assets 
파일이 아닌 WWW로 읽기 가능/쓰기 불가능

[안드로이드 Internal] 
Application.persistentDataPath : /data/data/번들이름/files/
파일 읽기 쓰기 가능
Application.dataPath : /data/app/번들이름-번호.apk
Application.streamingAssetsPath : jar:file:///data/app/번들이름.apk!/assets
파일이 아닌 WWW로 읽기 가능/쓰기 불가능

[iOS]
Application.persistentDataPath : /var/mobile/Applications/프로그램ID/Documents 
파일 읽기 쓰기 가능
Application.dataPath : /var/mobile/Applications/프로그램ID/앱이름.app/Data
Application.streamingAssetsPath : /var/mobile/Applications/프로그램ID/앱이름.app/Data/Raw 
파일 읽기 가능/쓰기 불가능

출처: https://qits.tistory.com/entry/유니티-플랫폼별-경로-정리 [Quiet, In The Storm...]

 

위의 자료에서 보듯이 플랫폼 별로 파일의 경로가 다른 것을 볼 수 있는데, 이러한

 

경로의 다양성으로 인해서 각각의 플랫폼별에 맞게 코드 상에서 작업을 해줘야한다.

 

물론, 유니티에서는 기본적으로 Application.~path로 어느정도 정돈이 된 class를 받아올 수 있다.

 

쓰고나서 읽는 형태로 빌드를 해서 올린 상태에서 쓰고 읽는 형태라면 읽기 쓰기 가능한 경로인

 

Application.persistantDataPath를 활용하면 별 어려움 없이 사용이 가능하다. 하지만, 내가 필요했던 내용은

 

에디터 상에서 맵 관련 파일들을 생성한 뒤, 그 파일들을 이용하고자 했기에 이미 저장되어있는 파일들을

 

사용할 방법이 필요했다.

 

현재는 안드로이드와 PC에서 에디터 상에서만 작업을 하고 있지만, 추후에는 iOS쪽도 추가할 예정이므로

 

나중에 추가를 한다고 생각하고 코드를 작성했다.

T LoadJsonFile<T>(string loadPath, string fileName, RuntimePlatform platform)
{
        string jsonData = "";
        if (platform == RuntimePlatform.WindowsEditor)
        {
            FileStream fileStream = new FileStream(string.Format("{0}/{1}.json", loadPath, fileName), FileMode.Open);
            byte[] data = new byte[fileStream.Length];
            fileStream.Read(data, 0, data.Length);
            fileStream.Close();
            jsonData = Encoding.UTF8.GetString(data);
        }
        else
        {
#if ANDROID_GOOGLEGAMES
            var filePath = loadPath + fileName + ".json";
            UnityWebRequest www = UnityWebRequest.Get(filePath);
            www.SendWebRequest();
            while (!www.isDone) { } //완료될 때까지 대기

            byte[] resultBytes = www.downloadHandler.data;
            jsonData = Encoding.UTF8.GetString(resultBytes);
#endif
        }
        return JsonUtility.FromJson<T>(jsonData);
}

현재 작성 중인 코드에서 json 파일을 이용하는데, json 파일은 범용적으로 사용되는 형태의 파일이므로 path를 잡을 때,

 

윈도우 에디터 즉, 유니티 상에서 사용하는 경우도 있기에 현재 실행 중인 플랫폼이 무엇인지 받아오는 매개변수를 받아

 

처리를 함으로써, 모바일 환경과 PC에서 에디터에서 실제 작업하는 환경에서도 동작하도록 구현하였다.

 

그리고 UnityWebRequest 클래스를 사용하였는데, 이를 사용하기 위해선 UnityEngine.Networking을 넣어줘야한다.

 

기본적으로 WWW클래스를 사용하여서도 작업은 가능하지만, 유니티에서 추천을 하지 않는 방식이라고 하므로,

 

UnityWebRequest로 변경하였다. 동작의 형태는 WWW와 UnityWebRequest 둘이 같다고 봐도 무방하다. 

 

UnityWebRequest가 좀 더 세분화되고 잘 구현이 되어있다고 보는게 맞을 것 같다.

string GetPathByPlatform(RuntimePlatform platform)
{
        string filePath = "";
        if (platform == RuntimePlatform.WindowsEditor)
        {
            filePath = Path.Combine(Application.dataPath + "/StreamingAssets/추가경로/");
            return filePath;
        }
        else
        {
#if ANDROID_GOOGLEGAMES
            filePath = Application.streamingAssetsPath + "/추가경로/";
#endif 
        }
        return filePath;
}

위의 코드는 Json에서 사용했다고 작성한 코드에서 플랫폼별로 다른 경로를 사용해야하기에 #if/#endif를

 

사용하여 구분을 하였다. 추후에 iOS도 작업을 해야한다. 추가경로의 부분에 경로를 넣게되면,

 

Android의 경우 StreamingAssets라는 폴더가 통째로 복사되어서 넘어가는 형태이기에 폴더를 구분지어

 

사용할 수 있다. 그냥 StreamingAssets라는 폴더에 바로 넣고 사용한다면, 추가경로 부분에 "/"를 추가하여서,

 

작성을 하면 될 것이다.


WebRequest가 무엇인지 공부를 해봐야할 것 같다.

 

WebRequest는 인터넷에서 데이터에 액세스하기 위한 NET Framework의 요청/응답 모델에 대한

추상(Visual Basic의 MustInherit) 기본 클래스입니다. 

 

라고 간단한 정의를 찾아볼 수는 있었는데 아직 웹 쪽으로는 모르는 부분이 많아서 무슨소린지 정확히는 모르겠다.

 

www.isDone이라는 bool값이 있는 것을 보면 비동기로 동작을 하기에 데이터를 받아오기까지 기다린 뒤

 

받은 데이터를 가지고 처리를 하는 것 같기는 하다.

 

작성한 글의 오타나 코드 상의 버그나 좀 더 개선될 수 있는 부분이 있다면 지적해주신다면 감사히받겠습니다.

버즈 플러스가 잘 쓰다가 어느 순간부터 갑자기 

 

한 쪽만 들려서 뭐지 하면서 한 동안 골치가 아팠다

 

인터넷을 찾아보니 과충전 등 여러가지 이유가 보이던데 

 

전부 나한테는 해당이 안되는 내용이었다.

 

LG폰이라 호환이 안되는건가 했지만, 실제로 사용도 잘 하다가 갑자기 이러니 환장할 노릇이었다.

 

웨어러블 앱도 다 지우고 깔고 플러그인도 지우고 깔고 하는데도, 처음 초기화 후 연결하면 잘 되다가,

 

한 번이라도 케이스에 넣었다가 꺼내는 순간, 자동 연결도 제대로 동작을 안하고, 연결이 된다 하더라도

 

오른쪽만 나오고 한 쪽은 안 나오는 현상이 반복되었다.

 

이게 뭔가 싶어서 삼성 전자 서비스 센터도 가봤지만, 거기서도 버즈 플러스에는 문제가 없다고 했다.

 

핸드폰에 문제가 있는거라고, 아마 초기화를 해야될 것 같다고 했는데, 이거 하나 때문에 초기화 하기는 너무 귀찮았다.

 

그러다가 혹시나 하는 마음에 핸드폰 재부팅을 했더니

 

잘 동작을 한다 ㅡㅡ... 아무래도 사용하다가 중간에 뭔가 프로그램이 꼬인건지... 아무튼 지금은 제대로 동작을 한다. 후

 

골치 아팠던 문제 하나 해결해서 다행이다.

유니티 상의 렌더링의 순서

 

파이프라인?

랜더 큐

Particle 계산 -> 카메라 -> Particle -> UI

 

소트 레이어/ 오더링 레이어

랜더로만 처리를 하게 되면, 원하는 대로 작업을 하기가 힘드니

 

랜더의 각각 위치( 카메라, UI 등 )에서 

 

또 한 번, Z Order 순서를 줘서 그려지는 순서를 조정할 수 있게 해주는 기능

Order in Layer로 그려지는 우선순위를 지정해줄 수 있다.

 

UI를 그려주는 순서를 미리 정해주는게 UI 간의 관계 구조도만 잘 되어있다면, 훨씬 유용할 것 같다.

 

나중에 그림 좀 더 그려서 이해하기 쉽게 포스팅에 더 추가해봐야겠다.

+ Recent posts