midas+son의 크리에이티브(creative) 이야기

Assets 폴더 아래에 Resources 폴더를 만들고
그 하위에 있는 파일들에 접근 하기 위해서
Resources.Load<T>("상대경로") 
를 사용한다.

여기서 주의 사항.
본인이 1시간 가까이 시간을 뺴앗긴 이유

Resources폴더 아래에
json폴더를 만들고
mon_info.json을 만들어 
정보를 관리하고자 하였다.

아래의 코드로 불러오려고 했으나
null 이 계속 나왔다.

Resources.Load<TextAsset>("json/mon_info.json");

\\, / , 디렉터리 없애고
별에별 방법들을 다 사용해 봤는데
안되어서 고민하다가


위와 같은 사항을 발견하였다.


왜 확장자가 안붙어 있는지 참....
아래와 같이 확장자를 지우니 잘 되더라.

Resources.Load<TextAsset>("json/mon_info");


Resources.Load 가 null 이면 일단 Project 탭에서

어떻게 나오는지 확인해보도록 하자

한글을 입력 할 때 마지막 글자가 지워지고 덮어씌워지는 문제가 있다.
예전에 포럼이 있어서 글이 따로 있었는데
지금 보니 링크가 403 forbidden 에러가 뜬다.
웹 상의 접근 권한 에러인데
싸이트가 관리가 안되고 있거나
링크가 바뀌어 처리된 것 같다.

그리하여 본인이 새로 작성해본다.
IME 입력에 대한 NGUI 버그라는데
정확한 원인은 모른다. 

임시 해결법만 알려 주겠다.

C#스크립트를 열어서 프로젝트 파일중에 
UIInput.cs파일을 찾아 연다.

다른 사이트 들에서는 
몇줄에 있다 몇줄에 있다 알려주는데
NGUI 버전마다 다르다.
몇번째 줄인지로 찾다가 고생하지 말자.

아래 키워드로 검색을 하자.(Ctrl + f)
string.IsNullOrEmpty(ime)

그러면 한두개 나올 것이다.
아래의 조건문을 찾아라.
if (string.IsNullOrEmpty(ime) && !string.IsNullOrEmpty(Input.inputString))

그리고 앞의 조건을 없애서
if (!string.IsNullOrEmpty(Input.inputString))
처럼 만들고 저장해라.

끝.


바로 전 글에서 유니티에서 싱글톤 할 때 

class 동적 생성을 하면 좀 문제가 있고

오브젝트를 만들어서 FindObjectOfType 로 

찾아 쓰는게 좋다고 했는데

계속 진행을 해보다 보니 

이것 역시 뭔가 아니었다.


정확히 뭐가 아니었냐하면

Scene이동을 할때마다 이 객체가 Destroy 된다.

FindObjectOfType 을 사용한 싱글톤의 사용범위가 

그 해당 Scene에서만 한정 되어 버린다.


DontDestroyOnLoad 으로 남기면 되지 않겠냐? 라고

생각 할 수도 있지만 

오브젝트의 성격에 따라 한계가 있다.


정말 계속 가지고 있을 오브젝트나 

맵만 Scene체인지 하고 

Player 같은 경우에는 괜찮지만

그 오브젝트를 다른 script에서 

public 참조할경우에는 테스트 하는 경우에 문제가 있다.

Release용과 Test용 오브젝트를 별도로 생각 해야 한다.


그래서 다시 생각을 하게 된 것이다.


정보들을 순수 class 동적 할당된 싱글톤을 사용하고

매 Scene에 필요한 정보들을 가져와 Awake나 Start될때 

파싱을 하는 것이다.


지난 글에서 class new를 사용하면 Warning이 뜬다고 했는데

본인이 잘못 생각하고 짠 부분이 있어서 그렇다.


public class SingletonClass<T> : MonoBehaviour  where T : class, new()

가 아니라

public class SingletonClass<T> where T : class, new()

가 되어야 한다.


오브젝트로 사용될 class가 아닌데 monoBehaviour를 상속 받으니 

워닝(Warning)이 뜨는 것이다.


위와 같이 하자.


그리고 Object용 싱글톤도 쓰일곳이 분명히 있다.

순수 Class처럼 사용할지

Object에 싱글톤을 걸지는 

용도에 따라 잘 파악해서 사용하자.


오브젝트용 싱글톤 코드는 이전 글을 참조하자.


싱글톤이란 디자인 패턴 중 하나로

실직적으로 class가 오직 한번만 생성되며 

어디서 호출하든 같은 instance를 가져오게하여

데이터를 공유하거나 관리할 수 있는 방법론이다.


이는 c++ 부터 Direct 3D로 게임을 만들 때에도 사용되어왔으며

각종 manager 처리를 할 때 유용하다.


보통 static으로 처리를 하거나

null이 아닐때에는 new 하지 않고

있는 값을 return하는데


유니티에서는 좀 다른 방법을 써야 하기에

이렇게 글을 남긴다.


아래는 C++에서 하던 데로 

유니티에서 작성한 싱글톤 class 이다.

유니티는 C# 스크립트를 사용했으므로 C#의 문법이 되겠다.


아래의 class를 상속하게 되면 그 class는 싱글톤이 되어 버린다.

(T는 템플릿, where 뒤에 있는 항목은 해당 템플릿이 가지는 속성이라고 생각하면 된다.)


public class Singleton<T> : MonoBehaviour where T : class, new()

{

    protected static T instance;


    public static T GetSingleton()

    {

        if (instance == null)

        {

            instance = new T();


            if (instance == null)

            {

//에러 메세지

                Debug.LogError(" ** Class Create Fail -> " + typeof(T));

            }

        }


        return instance;

    }

}


하지만 위와 같이 해버리면  아래와 같은 Warning이 발생 한다.


/*

 * 

You are trying to create a MonoBehaviour using the 'new' keyword.  

This is not allowed.  

MonoBehaviours can only be added using AddComponent().  

Alternatively, your script can inherit from ScriptableObject or no base class at all

UnityEngine.MonoBehaviour:.ctor()

Singleton`1:.ctor()

UtilManager:.ctor()

System.Activator:CreateInstance()

Singleton`1:GetSingleton() (at Assets/Manager/Singleton.cs:32)

Login:Start() (at Assets/Manager/Login.cs:8)

 * 

 */



대략...
컴포넌트가 없으면 위험 할수 있으니 
new로 동적 할당 하는 것은 권장 할 수 없다는 내용이다.
그리고 아래는 방법 들인데 ctor()은 어디 붙은 함수인지 의문이다.

여튼 유니티에서 new 동적 할당은 권장 되지 않으니
다른 방법을 찾았다.

Hierarchy 에 empty 로 manager용 오브젝트를 하나 만들고
미리 class가 있는 script를 add Component 한다.

그리고 그 Component를 찾아 사용하는 것이다.

위와 같은 방법을 쓰는 코드는 아래와 같다.

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    protected static T instance;

    public static T GetSingleton()
    {
        if (instance == null)
        {
            instance = (T)FindObjectOfType(typeof(T));
            
            if (instance == null)
            {
                Debug.LogError(" ** FindObjectOfType Fail -> " + typeof(T));
            }
        }

        return instance;
    }
}


유니티에는 각각의 GameObject들에 대해

방향을 나타내는 백터가 내장되어있다.


스크립트에서 확인을 해보면


Vector3.up

Vector3.down

Vector3.forward

Vector3.back

Vector3.left

Vector3.right


와 같은 값이 존재하는데

이들은 World 의 방향성을 나타낸다.


즉 하나의 오브젝트의 Rotate를 변경하여도

위의 변수들의 방향은 바뀌지 않는다는 것이다.


그렇다면 각각의 오브텍트가 가지는 방향은

어떻게 찾을까?(Local 방향)


그것은 각각의 GameObject들의

transform에 내장되어있다.


transform.up

transform.forward

transform.right


이렇게만 있는데

그럼 나머지 downbackleft는 어떻게 해야 하는냐?

각각 위의 값을 -(음수, 마이나스)해주면 된다.

유니티로 게임을 빌드 시키고 테스트를 할때

다른 프로그램을 실행하거나

마우스로 바탕화면을 클릭하거나 하여

포커싱이 유니티 밖으로 나가게 되면

게임이 동작이 멈춰보이고

다시 유니티를 클릭하면

여러 동작들이 한꺼번에 되는 경우가 있다.


그 이유가 유니티는 기본값으로

포커싱이 안되어 있으면 화면이 갱신되지 않기 때문이다.


포커싱이 안되어 있어도 

동작을 시키기 위해서는

아래와 같은 코드를 스크립트에 적용 시키면 된다.


    public void Awake()

    {

        Application.runInBackground = true;

    }

유니티 자체는 싱글 쓰레드로 되어있다.

하지만 스크립트에서

thread를 동적 할당 하여 사용할 수 있다.


여기서 문제가 발생 할 수 있는 것이다.


유니티의 play를 종료해도

해당 Thread가 계속 동작을 하고 있다는 것이다.


네트워크 게임을 만들다가 

유의 했던 사항들 몇가지 적어보겠다.


1. OnDestroy() 이벤트 함수에 종료 처리 할 수 있는 것들은 해두자.


2. Thread 종료는 Abort() 로 하지 말자. 메인 Thread에서 놓아줄 뿐이지 동작 하고 있다. 


3. Thread 내에서 While(true) 는 가급적 사용하지 말고 bool 변수를 두어 break 외에도 빠져 나갈 수 있게 하자.


4. 좀 위험 하다 생각하면 try, catch 를 하고 catch 부분에 Debug를 남기자. (뭐라도 남겨라)


5-1. TCP 소켓 통신 에서는 Client 접속이 끊어지면 Read Failure가 계속 발생 함에 유의 하자.

5-2. 연속 발생 횟수를 체크해서 Thread 의 loop를 끝내고 나가도록 하자. 

5-3. Thread 나가기 전에 close() 할 수 있는건 하자.


요즘 TcpListner로 동기식 소켓 네트워킹을 배워서

유니티 게임에 접목 시키기 위해 테스트 중이다.


유니티 프로젝트를 

서버용으로 하나

클라이언트용 하나

각각 만들어 사용하는데


서버랑 클라이언트랑 각각 실행 시키다가

재 실행을 하거나

아니면 하나가 오류 동작을 하면

문제가 없는 프로젝트도 "응답없음"으로

아무 동작 할 수가 없다.


작업관리자에서 

유니티 2개를 끄거나 

아니면 하나만 끄거나(남은 하나도 결국 동작이 이상해진다)

해야 하는데

테스트 시간이 너무 오래 걸린다.


지금 몇시간 째 후회 중이다.


유니티에서 지원하는 

네트워크 컴포넌트를 사용한건 아니라고 하지만

이건 참 너무 한거 같다.



유니티에서 Scene을 여러개 생성한뒤

Scene 간의 이동이 가능 하다.


이전에는 


Application.LoadLevel(int index);    //빌드 셋팅할 때의 index번호 


Application.LoadLevel(string name);    //Scene 이름


을 사용 하였지만


현재 최신 버전에서는 위의 코드는 사용하지 않기를 권장한다.


새로 권장 하는 코드는


using UnityEngine.SceneManagement; 을 추가하여


아래 코드 중에서 사용 하는 것이다.


SceneManager.LoadScene(int index);    //빌드 셋팅할 때의 index번호 


SceneManager.LoadScene(string name);    //Scene 이름


SceneManager.LoadScene(int index, LoadSceneMode mode);


SceneManager.LoadScene(string name, LoadSceneMode mode);



LoadSceneMode 에는 

LoadSceneMode.Single 과 LoadSceneMode.Additive 가 있는데


LoadSceneMode.Single은 예전과 같이 

현재 Scene 의 Object들을 날리고

이동하는 Scene의 Object만을 새로 로드라는 것이고

LoadSceneMode.Additive은 현재 Scene을 그대로 남긴체

이동할 Scene을 로드하여 추가하는 것이다.


이전에는 현재 Scene의 객체중 남기고 싶은 것만 

스크립트에서 DontDestroyOnLoad(gameObject); 해주었는데

LoadSceneMode.Additive를 하면서 

DontDestroyOnLoad(gameObject);를 해버리면

DontDestroyOnLoad 한 객체들만 별도로 Scene의 구분이 되어 버린다.


Scene의 관리를 위해 적절히 잘 사용 하여야 할 것이다.

AudioSource 다중 사용이 무슨 말인가 하면

보통 AudioSource의 clip(음원 이라고 생각하자)을 

하나 등록 하여 사용하게 된다.


스크립트에서 도중에 해당 clip을 바꾸어 사용할 수 있다.

하지만 그러면 이전의 음원은 날라가거나 짤리게 된다.


배경음이 있고 몬스터가 있고

스킬이 있고 이펙트가 있다.


하나의 AudioSource를 사용하면

게임을 하기 싫어질 만큼 귀가 괴로워진다.


보통은 하나의 오브젝트에 하나의 AudioSource를 가지고 있다.

몬스터는 몬스터의 음악이 있고

배경음도 지역에 따라 clip을 바꾸어 틀어 주게 된다.


하지만 하나의 오브젝트에서

동시에 들려주어야 하는 것이 있다면 어떻게 할 것인가?


쉽게 예를 들어 설명하면

캐릭터가 있다. 

점프를 할 떄 점프 음을 튼다.

펀치를 날리면 타격음이 나와야 한다.

도중에 맞으면 잡음정도 하나 생겨야 한다.


이렇게 하나의 FBX로 만들어진 오브젝트의 경우

상태 값이 많아 사용될 AudioSource의 양도 많아 질텐데

하나의 AudioSource로는 감당이 안되며

동시에 처리가 힘들다.


그럼 어떻게 해야 되겠는가?


쉽게 AudioSource를 여러개 만드는 것이다.


어디에 만드느냐?


오브젝트에 Create Empty를 하여 자식으로 더미를 만든다.

FBX로 불러온 것이라면 본의 위치를 잡는다.

점프음은 다리 부분에 더미를 만들어 AudioSource를 넣는다.

펀치는 주먹에 더미를 만들고 AudioSource를 추가한다.

이렇게 자식으로 더미를 만들어 AudioSource를 각각 추가한다.

그리고 캐릭터 컨트롤 스크립트에서 

public으로 전역변수 AudioSource를 

각각 변수에 할당 하여 컨트롤 하거나

child를 찾아 컨트롤 할 수 있다. 

그러면 여러가지의 음원이 씹히지 않고

깔끔하게 따로따로 나올 수 있다.


AudioSource 를 설계 할 때에는

하나의 AudioSource에서 clip을 바꾸어 가며 컨트롤 할지

더미로 분할하여 다중으로 AudioSource를 생성해 컨트롤 할지 

고려해봐야 함을 기억하자.

'공부 > Unity' 카테고리의 다른 글

[잡담]유니티로 서버 달지 말자!!!  (0) 2016.06.11
Unity Scene Change(씬 바꾸기)  (0) 2016.06.04
GetComponent<T>() 사용에 대해  (0) 2016.06.02
다중 트리거(Trigger) 조작 문제  (0) 2016.06.01
splat map  (0) 2016.05.30