레이블이 NGUI인 게시물을 표시합니다. 모든 게시물 표시
레이블이 NGUI인 게시물을 표시합니다. 모든 게시물 표시

2015년 3월 10일 화요일

[NGUI] UISprite에 대한 Custom Shader 적용

개요

NGUI에서 UISprite에 대한 Custom Shader의 적용 방법을 정리하고자 한다.
여기서 쉐이더는 일단 desaturate시키는 쉐이더를 예로 들어보도록 하자.

참조

http://www.tasharen.com/forum/index.php?topic=1256.0

설명
우선, NGUI의 Sprite는 1개의 Atlas단위로 쉐이더가 지정되어있다.
참고 링크에서도 이야기가 되고 있지만. 사실 기본적인 의도대로라면 1번의 drawcall에서 shader의 변경이란게 불가능하기 때문에
(정말로? 음... 사실 Sprite에서 임의의 쉐이더를 정의해놓고 drawcall을 쪼개는 한이 있더라도 선택 운용하게 해주면 좋겟다.)
수순대로라면 drawcall을 분리해서 사용해야하는 수순이 된다. 그래서 NGUI에서는 Atlas단위에 있어서 근본적인 설계딴에서 별도의 쉐이더를 분류하고 있지 않다.

자, 우선 여기서 생각해야할 제약조건은

1. Atlas단위로 shader는 고정되어있다.
2. shader를 분리해서 별개의 atlas를 작성해서 이용한다면 일단 렌더링시에 별도의 drawcall로 운용이 된다. 유색 - 무색 - 유색으로 끼게되면 하나의 텍스쳐라도 3번의 드로우콜이 필요하게 될것이다.

그리고 우리가 여기서 생각해야할 부분은

1. 그렇다면 Atlas는 우선적으로 제각각 작성해야할 것이다.
2. 그런데 완전 같은 이미지 소스에 대한 desaturation임에도 불구하고 별도의 texture를 작성할 필요가 있는가?
 -> 흑백 이미지와 컬러 이미지를 따로 작성한다면 유지보수에 있어서도 귀찮아지는 문제점이 발생할것이고, 행여나 실수를 하게되면 리소스가 결여되는 문제가 생길 수 있다.
 -> 심지어 메모리가 2배로 사용된다. 어찌되었든 바람직하진 않다.

여기서 간단히 생각해볼 수 있는것은. NGUI를 확장한다거나 하는 방법이 아닌
1개의 texture를 각각 참조하는 UIAtlas를 2개 작성하는 방법이 그 해결책의 하나가 될 수 있다고 생각한다.

우선 Desaturate를 실시하기 위한 shader에 관해선
아래 링크에 괜찮은 샘플들이 있으니 이 부분은 가져다 쓰도록 하자.

http://forum.unity3d.com/threads/desaturation-grayscale-shader-for-ios.82105/

그리고 문제의 UIAtlas인데 수순은 다음과 같다.

예를들어 UIItem이라는 atlas가 있다고 하자.

그럼 이 atlas의 구성은 다음과 같을 것이다.

UIItem.prefab
UIItem.mat
UIItem.png

우리는 이중에서 UIITem.prefab, UIItem.mat의 2개 파일을 복사할 필요가 있다.
Unity상에서 이 두개의 파일을 선택하여 Ctrl+D / OSX: Command+D 를 눌러서 복제를 하자

아마 UIItem 1과 같은 느낌으로 파일명이 바뀌었을건데, 여기서 양쪽파일명을 Desaturate 아니면 Gray와 같이 1의 부분을 대치하여 파일명을 정리해주자.

이후에 UIItem.prefab의 UIAtlas Component의 Material을 복제된 mat파일을 보도록 다시 링크를 이어주자 (복제했더니 원본을 보고있더라)
그리고 mat쪽에서 상기 링크에 있는 흑백화 쉐이더를 붙여준다.

이후에 UIItem을 이용하고 있던 Sprite에 대해서 이번에 복제 작성한 UIItem???를 연결시켜주면
에디터에서는 여전히 컬러가 들어간 이미지가 표시되지만 게임화면에서는 흑백으로 표시되는것을 알 수 있다.

UIAtlas의 instance자체에서 텍스쳐에 대한 매핑정보를 보존하고 있기 때문에, 한번 갱신을 했다면 이 복제 atlas도 다시 만들어줄 필요가 있다.
이 방법도 다소 귀찮긴 하지만. 적어도 Sprite의 렌더링에 있어서 가장 큰 크기를 차지하는 텍스처가 중복되지 않아도 된다는 장점이 있다.

NGUI에서 공식적으로 서포트 되어주었으면 하지만, 적어도 그때까지는 이런 방법을 강구할 필요가 있을것 같다.
필요에 따라서는 복제 갱신하는 툴이나 배치를 작성해도 되지 않을까

2015년 2월 6일 금요일

[uFrame 1.5] Customize Loading Scene

개요
현 시점에서 Loading Scene에 관한 문서화가 이루어져 있지 않은 관계로 사용방법을 정리하고자 한다.

참고
uFrame에 기본적으로 첨부되어있는 Loading scene을 참고

기본적으로 LevelLoaderView가 uFrameComplete에 구현되어 포함되어 있다.
OnGUI기반으로 작성된 UnityGUILevelLoaderView는 이 LevelLoaderView를 inherit하고있으며, 동일한 구조로 작성하면 된다.

Progress는 각 SceneManager의 *Scene.cs의 Load에서 progress에 대해 작업중인 메세지와 progress factor를 0.0-1.0 사이의 값으로 feedback해주면 된다.

수순

1. NGUI

이번에는 NGUI를 활용하여 Loading Scene을 구성해보고자 한다.

NGUI 3.x의 버전(이번에는 3.7.7)에서 하나의 UIRoot이하 UIWidget은, 각각 하나의 Layer를 가질 필요가 있다. Loading Scene은 로딩이 완료될때까지는 해당 Object가 파기되지 않으므로, 두개의 UIRoot가 공존하게 된다. 따라서 Loading용 UI는 Layer를 다르게 설정해 기존 UI와 구분되게할 필요가 있다.

이를 수행하지 않은 경우, 기존에 UIRoot에 대해 Anchor설정을 실시한 Object들은 제각각 알맞은 위치를 참조하지 않는 버그가 존재한다.

http://www.tasharen.com/forum/index.php?topic=11126.0

따라서, Loading용 UI레이어는 LoadingUI라던가 하는식으로 기존의 레이어와는 분리토록 하자.

그 이외에는 NGUI Slider나, 기타 NGUI용 위젯들을 이용하여 화면을 구성하고, 관련된 조작을 LevelLoaderView Inheritance에서 설정하면 되겠다.

2. LoadingSceneObject : MonoBehaviour

Loading scene용 Object는 DontDestroyOnLoad옵션이 걸려 이전 화면과 다음 화면의 transition이 종료될 때 까지화면에 존재하다, 특정 타이밍에 모두 파기되게 된다.

이를 위해서 Loading용 Scene의 GameObject에는 파기를 위한 마커와 같은것이 존재하는데, uFrame에서는 LoadingSceneObject : MonoBehaviour 와 같은 형태로 준비되어있다.
기본적으로 샘플로 제공된 Loading Scene의 LoadingSceneView라는 GameObject와 같은 구성에서 그 하위에 오브젝트들을 구성하면 LoadingSceneObject가 이미 첨부되어있기에 상관없으나, 그 밖에 화면을 구성하고자 한다면 LoadingSceneObject를 각 GameObject에 붙여두도록 하자. 이를 실시하지 않은 경우 Loading이 끝나더라도 오브젝트가 파기되지 않는다.

3. LevelLoaderView inheritances

Loading Scene의 LoadingSceneView(GameObject)에 LevelLoaderView의 inheritnace를 붙여줄 필요가 있다. UnityGUILevelLoaderView.cs를 참고하면 알겠지만, 아래에 간단하게 NGUI를 활용한 경우의 구성을 첨부한다.

 using UnityEngine;  
 public class CustomLevelLoaderView : LevelLoaderView  
 {  
      public UISlider m_slider;  
      public UILabel m_label;  
      public void Update() {  

           if(this.m_slider != null)
           {
                this.m_slider.value = Model.Progress;  
           }
           
           if (m_label != null) 
           {  
                this.m_label.text = Model.Status;  
           }  

      }  
 }  

4. Progress Feedback

예를들어 ItemScene.cs라는 SceneManager가 있다고 가정해보자, 그렇다면 해당 SceneManager의 경로는

{UnityProject}/Assets/{uFrameProject}/SceneManagers/ItemScene.cs

와 같은 경로가 될것이고, 정상적으로 작성된 SceneManager라면 아래와 같은 맴버 함수가 있을거라 생각된다.

public override System.Collections.IEnumerator Load(UpdateProgressDelegate progress)

여기서 progress에 대해 피드백을 실시하면 앞서 작성한 loading scene에서 LoadingSceneViewModel에서 보고된 데이터값을 subscribe하여 화면에 출력해주게 된다.

무의미하지만 적당하게 coroutine으로 작성된 예시를 보이자면

for (int i=0i<5i++) {
   progress("dummy "+i,i/5f);
   yield return new UnityEngine.WaitForSeconds(0.25f);        
}


와 같이 사용해볼 수 있겠다.