블로그 이미지
Every unexpected event is a path to learning for you.

카테고리

분류 전체보기 (2737)
Unity3D (817)
Programming (474)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (228)
협업 (58)
3DS Max (3)
Game (12)
Utility (136)
Etc (96)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (53)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday
04-20 00:00

게임 출시를 앞두고, Android플랫폼의 심각한 발열과 낮은 프레임에 대한 최적화를 수행했다.

여러개의 프로젝트를 런칭하면서 쌓인 노하우 및 새로 공부한 지식으로 유의미한 최적화 성과를 거두었기에 최적화 방법을 공유한다.

 

먼저, 최적화 방법 공유에 앞서 최적화 대상 프로젝트의 스펙은 다음과 같다

-Unity 2021.3.8f로 개발

-URP 프로젝트

-NGUI사용

 

최적화 성과 (갤럭시 s8기준)

FPS :  20~30 -> 50~60

용량 : 400mb -> 300mb

발열 : S22에서 심각한 발열로 인해 30~40fps가 나옴 -> S22와 같은 심각한 발열 기기에서 거의 발열 없이 수십분 이상 구동 가능. 안정적인 60fps유지.

 

이와같은 성능을 유지하기 위해선 여러가지 복합적인 이유들이 있겠지만,

untiy editor의 stats기준으로 batch는 많아도 100~200수준을 잡아주는 것이 좋다

(스크립트 병목과 높은 vertex로 인한 성능 하락은 고려하지 않는다)

 

먼저 최적화에 앞서 본인 프로젝트에 대한 프로파일링을 우선 진행하길 바란다.

필자는 최적화에 Unity Profiler와 Memory Profiler(실험적) 을 사용했다

 

profiler를 통해 스크립트 병목의 원인을 추적하고, 메모리 스냅샷을 사용해 메모리 사용량을 최적화하라

 

그리고 최적화의 대상이 CPU처리인지 GPU처리인지도 파악해야 한다.

 

 

 

NGUI관련 최적화

1. UI Atlas의 depth최적화, panel을 최적화한다. 

ngui에서 가장 기본적인 것으로 동일한 panel을 사용하는 widget들의 depth를 정리하여 최적화 해주어야 한다.

무분별한 panel의 사용으로 동일한 atlas를 사용하는 widget이 쪼개져 중복적인 draw call을 야기하지 않도록 해야한다.

 

ngui에서 A와 B아틀라스 2개를 통해 UI를 구성한다면 흔이 2의 draw call을 예상할 것 이다.

하지만 만약 depth가 A A B A BB이런 식으로 ui를 배치한다면 draw call은 4가 된다

2의 draw call을 얻기 위해선 A A A B B B B와 같이 atlas에 따른 ui depth를 정리해주어야 한다.

 

하나의 atlas로 draw되다가 다른 atals를 만나면 중간에 batching이 끊기게 된다.

 

2. 동일한 atlas를 사용하는 widget은 단일 panel에서 최대한 순서를 정렬하여 처리해준다.

필자의 경우 ui atlas를 back / icon / overay / font 이렇게 4가지로 나누어, 정리했으며 

ui가 아무리 많아도 batch는 최대 4만 유지하도록 해주었다.

draw call의 병합 규칙은 두가지가 있는데, 하나는 material, texture, shader가 모두 같다는 것이고 다른 하나는 widget의 레이어가 같거나 인접해야 한다는 것이다.

 

3. NGUI에서 Transform의 변화, 생성, 삭제가 일어나는 객체를 조심해라

NGUI에서는 Panel에 속한 widget의 Transform이 변화(생성/삭제 포함) 하면 해당 panel에 있는 모든 widget을 다시 rendering한다. 

즉, Panel에 연출을 위한 widget이 tween position, scale등을 한다면 매 lateUpdate에 Panel은 drawcallfill을 다시 호출하는 것이다.

NGUI Widget의 Transform이 정적이지 않거나, 생성/삭제가 자주 일어나는 객체라면 완전히 다른 panel의 하위 객체로 두어 격리해두어야 한다.

즉, NGUI에서는 한 panel에 속한 widget의 상태에 변화가 생기면 해당 panel의 모든 atlas를 다시 렌더링해주게끔 되어있다.

 

3-1. 특히 NGUI의 UI Label을 조심해라

UI Label로 데미지 등을 처리할 경우 데미지 연출을 위해 UI Label이 매프레임 tweening하거나 animation을 재생할 수 있다. 이 경우 UI Label은 엄청난 성능 병목을 일으킨다.

매 프레임 움직이는 UILabel이 속한 panel의 draw fill/fill shadow/draw call fill을 야기시킨다.

특히 UI Label에 쉐도우나, 아웃라인 등이 들어가있다면 매프레임 Transform의 변화에 따라 Label을 다시 렌더링 해주게 된다.

이를 방지하기 위해 데미지 폰트 등은 이미지 폰트를 사용하는 것을 추천한다.

 

4. panel의 static상태 활성화

panel로 구성한 ui에 있는 모든 객체가 정적인 것이 보장된다면, panel의 static옵션을 활성화해주면 좋다.

ngui객체도 기본적으로 mono객체이며, static객체가 아니면 모든 ui객체에서 update/lateUpdate가 호출되기에 성능 병목이 발생한다.

정적인 객체의 경우 3번 방법을 참고하여 동적인 객체만 관리하는 panel등으로 격리해주자

 

 

그 외 Unity공통 최적화

1. GC를 줄이자

BinaryFormatter의 Serialize는 엄청난 양의 GC를 야기하며, 디바이스를 freeze시킨다. 이러한 동작은 매프레임 해주지 말고, 스케쥴러를 만들어 처리해주자

 

BigInteger.Parse(string)과 같은 value copy를 야기하는 처리를 피해주자

 

특정 타이밍에 GC.Collect를 수동으로 호출해주자

 

2. URP Setting의 Post Processing의 fast sRGB/Linear Conversions로 gamma공간을 사용하고 있다면 gpu의 부담을 덜어주자

 

3.  SustainedPerformanceMode를 활성화해 지속가능한 성능 옵션을 켜주어, 발열을 방지하자 (이로 인해 기본적인 fps의 하락이 있을 수 있음)

 

4. GPU의 병목/부하가 생기는 Project Setting의 옵션은 비활성화 하자 (Compute Skinning, Graphics Jobs...등)

 

5. Static Batching과 Dynamic batcing을 적극 활용하자

 

6. Texture compression format : ASTC, normal map encoding : DXT5nm-style

 

7. Screen.SetResoultion을 통해 화면 해상도를 지정해주자 (HD~FHD). 이를 해주지 않을 경우 기기의 해상도에 따라 렌더링되기 때문에 발열과 배터리소모의 원인이된다.

 

8. Target Framerate를 설정해주자 (60fps). 만약 프레임상한이 없다면 상위 기기에선 비정상적으로 높은 fps가 발생할 수 있으며 이는 발열/배터리소모의 원인이 될 수 있다.

 

9. realtime shadow는 최대한 피하고, 눈속임을 통해 그림자를 표시할 수 있는 방법을 찾자

quad를 통해 그림자를 표현하고, 이 quad를 dynamic batcing하는 등이 방법이다.

 

10. 가능한 많은 object pooling을 해주자. 

또한 NGUI객체의 경우 객체를 pool에서 회수/반환할 때 gameObject.SetActive보단 화면에 rendering되지 않는 먼 영역으로 좌표를 이동시켜 주는것이 좋은 선택지일 수 있다.

NGUI객체는 오브젝트가 enable/disable될 때 많은 병목을 일으키는 처리를 수행한다

 

11. 거리에 따른 최적화 방법을 숙지하라

Generate mipmap은 기본적을 texture의 용량을 1.3배 정도 부풀린다.

단 한 화면에 render되는 객체가 많을 경우 이는 한 화면의 메모리 부하를 줄여주므로 적극 활용해야한다.

 

또한 오브젝트에 LOD level을 세팅하면 거리에 따라 3D오브젝트의 렌더링 부하를 줄일 수 있지만 용량이 추가적으로 들어가는 것을 유의해야 한다.

 

Aniso Level을 주의해라. Aniso Level은 오브젝트를 그릴 때 단일 텍스쳐라도 멀어지면 흐리게 해주는 효과이다.

Aniso Level이 높을수록 선명해지겠지만 ,처리비용이 많이 든다.

프로젝트 세팅에서 확인하라 (기본값은 Forced On이다)

 

12. Asset의 Read/Write옵션을 주의하라

이 옵션은 기본적으로 비활성화지만 활성화 시 GPU메모리 뿐만 아니라 메인 메모리에도 복사되므로 소비량이 2배로 증가한다.

꼭 필요한 오브젝트의 경우만 식별하여 사용할 것

 

13. 파티클의 shader/material에 따라 render order를 조정해주어라.

NGUI와 같은 개념으로, 파티클에서 사용되는 shader/material도 render order를 조정해주어 batching되도록 해주자

 

14. Resources.Load를 매니징하자

디바이스로 부터 불러온 자원의 경우 사용 빈도에 따라 GC가 발생하지 않도록 상시 메모리에서 들고있는 등의 방법으로 최적화 해줄 수 있다.

 

15. Debug.Log를 주의해라

런타임 빌드 시 프로젝트 세팅에서 로깅 옵션을 완전히 비활성화 하거나, 스크립트에서 define으로 처리를 격리해주어라.

 

Unity Crash관련 팁

이 항목은 필자가 서비스중인 게임의 크래시 문제를 해결한 경험으로 작성되었기에, 지극히 주관적입니다.

 

1. Android환경에서 Vulkan API는 아직 불안정하다. Vulkan내부에서 native crash를 야기할 수 있다. (필자는 opengles 3을 권장)

 

2. Multithread rendering과 compute skinning도 native crash를 야기할 수 있다. 

 

3. 빈번한 Scene Load는 잦은 크래시를 야기할 수 있다.

맵의 전환 등으로 Scene이 load될 때 전 scene의 unload와 새로운 scene의 load중 스크립트가 unload된 scene의 객체 혹은 아직 preload상태의 scene에 접근할 경우 크래시가 발생한다.

 

추가적으로, unity의 scene 로드 구조는 순간적으로 폭발적인 메모리 사용량을 불러올 수 있다.

A장면에서 B장면으로 넘어갈 때 A장면과 B장면이 동시에 메모리에 올라가있는 순간이 존재하며 이 때 메모리 부족으로 크래시가 발생할 수 있다.

 

A장면에서 B장면으로 넘길 때 빈 장면(C)을 넣어 A장면의 메모리가 모두 해제되고, GC가 Collect되게 한 후 B장면을 로드해주는 방식은 아주 유용하다

 

4. Strip Engine Code옵션은 무조건 끄자

압축된 스크립트 dll을 참조하는 순간 native crash가 발생한다

 

 

[출처] https://gall.dcinside.com/mgallery/board/view/?id=game_dev&no=124306

 

최적화 노하우 공유 - 인디 게임 개발 마이너 갤러리

개인적으로 개발관련해서 메모용으로 사용하는 티스토리 블로그에 작성했다가,꽤 많은 분들에게 도움이 될 것 같아 내용만 똑같이 복붙하여 왔습니다.다들 고생하세요.-----게임 출시를 앞두고,

gall.dcinside.com

 

반응형
Posted by blueasa
, |

Unity 2021.3.33f1

NGUI 2023.08.01

----

 

NGUI-UILabel의 Effect에서 Shadow와 Outline을 같이 적용하고 싶어서 찾아보고 올려둠.

UILabel에 세 곳에 소스 추가

public class UILabel : UIWidget
{
    ....
    
    [DoNotObfuscateNGUI] public enum Effect
    {
        None,
        Shadow,
        Outline,
        Outline8,
        ShadowAndOutline,	// Add
    }   
    
    ....
    
    /// <summary>
    /// How many quads there are per printed character.
    /// </summary>

    public int quadsPerCharacter
    {
        get
        {
            if (mEffectStyle == Effect.Shadow) return 2;
            else if (mEffectStyle == Effect.Outline) return 5;
            else if (mEffectStyle == Effect.Outline8) return 9;
            else if (mEffectStyle == Effect.ShadowAndOutline) return 9;	// Add
            return 1;
        }
    }

	....
    
    public void Fill (List<Vector3> verts, List<Vector2> uvs, List<Color> cols, List<Vector3> symbolVerts, List<Vector2> symbolUVs, List<Color> symbolCols)
	{
        ...
        // Apply an effect if one was requested
		if (effectStyle != Effect.None)
		{
			int end = verts.Count;
			var symEnd = (symbolVerts != null) ? symbolVerts.Count : 0;

			pos.x = mEffectDistance.x;
			pos.y = mEffectDistance.y;

			ApplyShadow(verts, uvs, cols, offset, end, pos.x, -pos.y);
			if (symbolVerts != null) ApplyShadow(symbolVerts, symbolUVs, symbolCols, symOffset, symEnd, pos.x, -pos.y);

            #region Add ShadowAndOutline
            if (effectStyle == Effect.ShadowAndOutline)
            {
                pos.y /= 2;
                pos.x = pos.y;

                offset = end;
                end = verts.Count;

                ApplyShadow(verts, uvs, cols, offset, end, -pos.x, pos.y);

                offset = end;
                end = verts.Count;

                ApplyShadow(verts, uvs, cols, offset, end, pos.x, pos.y);

                offset = end;
                end = verts.Count;

                ApplyShadow(verts, uvs, cols, offset, end, -pos.x, -pos.y);

                offset = end;
                end = verts.Count;

                ApplyShadow(verts, uvs, cols, offset, end, -pos.x, 0);

                offset = end;
                end = verts.Count;

                ApplyShadow(verts, uvs, cols, offset, end, pos.x, 0);

                offset = end;
                end = verts.Count;

                ApplyShadow(verts, uvs, cols, offset, end, 0, pos.y);

                offset = end;
                end = verts.Count;

                ApplyShadow(verts, uvs, cols, offset, end, 0, -pos.y);
            }
			#endregion

            if ((effectStyle == Effect.Outline) || (effectStyle == Effect.Outline8))
			{        
        ...
    }
	
}

 

 

[출처] https://gamedev.stackexchange.com/questions/151329/all-sides-shadow-outline-in-unity-ngui

 

All sides shadow outline in Unity NGUI

How can I make such exactly the same shadow using NGUI?

gamedev.stackexchange.com

 

반응형
Posted by blueasa
, |

[링크] https://cho22.tistory.com/55

 

[UnityNGUI] ScrollView에 Particle Clipping하기

NGUI 스크롤뷰 내부에 파티클이 붙어있는 아이템을 추가했더니 스크롤시 스크롤뷰 밖에서 파티클이 보이는 현상이 발생하였다. 이 현상 수정을 위해 NGUI Particle Clipping, 유니티 파티클 스크롤뷰,

cho22.tistory.com

 

 

 

 

반응형
Posted by blueasa
, |

Unity 2022.3.10f1

NGUI 2022.08.01

----

[추가2] 2023-10-25

NGUI v2023.07.26에서 아래와 같은 업데이트가 올라왔다.

- FIX: NGUI will now change all imported textures to be uncompressed when creating an atlas, matching how it used to work in older versions of Unity.

이 수정 사항 때문에 NGUI Atlas 압축을 유지해주려던 부분이 작동하지 않고 무조건 Uncompressed로 변경되고 있어서 해당 부분인 아래 소스 부분(NGUIEditorTools.cs : line 538)을 주석 처리 했다.

//-------------------------------------------------
//			  NGUI: Next-Gen UI kit
// Copyright © 2011-2023 Tasharen Entertainment Inc
//-------------------------------------------------

using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;

/// <summary>
/// Tools for the editor
/// </summary>

static public class NGUIEditorTools
{
    ...
    static public bool MakeTextureReadable (string path, bool force)
    {
        if (string.IsNullOrEmpty(path)) return false;
        var ti = AssetImporter.GetAtPath(path) as TextureImporter;
        if (ti == null) return false;

        var settings = new TextureImporterSettings();
        ti.ReadTextureSettings(settings);

        if (force || !settings.readable || settings.npotScale != TextureImporterNPOTScale.None || ti.textureCompression != TextureImporterCompression.Uncompressed)
        {
            settings.readable = true;

            if (NGUISettings.trueColorAtlas)
            {
                var platform = ti.GetDefaultPlatformTextureSettings();
                platform.format = TextureImporterFormat.RGBA32;
            }

            settings.npotScale = TextureImporterNPOTScale.None;
            
            #region NGUI Atlas 압축 관련 처리 [blueasa / 2023-10-25]
            /// NGUI v2023.07.26 에서 강제로 Uncompressed 하는 소스가 추가됨.
            /// 이 부분 때문에 아틀라스 압축이 유지되지 않고 풀리는 문제가 있어서 주석 처리 함
            /// trueColorAtlas 셋팅 될 때만 Uncompressed 강제 하도록 함
            if (NGUISettings.trueColorAtlas)
            {
                ti.textureCompression = TextureImporterCompression.Uncompressed;
            }
            #endregion
            
            ti.SetTextureSettings(settings);

#if UNITY_5_6
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
#else
            ti.SaveAndReimport();
#endif
        }
        return true;
    }
    ...
}

 

----

[추가]

아틀라스 압축을 사용하려면 Atlas Maker의 Truecolor를 끄자.

(켜놓으면 압축상태를 무시하고 무조건 Truecolor로 아틀라스를 묶어낸다.)

Truecolor 체크 해제

----

 

[수정] New Atlas 관련 예외처리 추가(2023-04-12)

 

----

게임 최적화를 위해 Andrio/iOS 둘다 설정에서 Texture compression format을 ASTC로 설정하고 사용하고 있다.

NGUI Atlas도 최적화를 위해 Compression을 High Quality로 사용중인데

여기서 문제가 NGUI의 Atlas를 묶을 때는 Compression이 None(비압축)이 아니면 에러가 발생한다.

 

그래서 Atlas 묶을 때는 Compression을 None으로 풀어서 묶은 다음 원래 Compression인 High Quality로 변경하고 있었는데

불편하기도하고 사용하다 실수가 나오기도 해서 이참에 NGUI Atlas Maker를 수정해서 자동으로 되도록 했다.

 

작동 원리는 아래와 같이 간단하다.

(처음 해보는거라 적용 지점이 어딘지 찾는다고 좀 헤멤)

 

  [작동 원리]

----------------------------------------------------

- Atlas가 압축 된 상태면

  1. Compression 별도로 저장

  2. Compression : None으로 변경

  3. Atlas Make(NGUI 원래 소스)

  4. 별도로 저장했던 Compression을 Atlas에 적용해서 원래 압축 상태로 되돌려 줌

- Atlas가 압축 안된 상태(None)면

  1. 원래 소스만 실행(위에서 3.만 실행)

----------------------------------------------------

 

추가된 소스는 NGUI의 UIAtlasMaker.cs - UpdateTexture() 함수에 두 곳 추가 되었다.

소스 위치를 알기 위해 UpdateTexutre() 함수 전체를 올렸고, blueasa를 찾으면 region 정리해 둔 두 곳이 있다.

 

//-------------------------------------------------
//            NGUI: Next-Gen UI kit
// Copyright © 2011-2020 Tasharen Entertainment Inc
//-------------------------------------------------

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

/// <summary>
/// Atlas maker lets you create atlases from a bunch of small textures. It`s an alternative to using the external Texture Packer.
/// </summary>
public class UIAtlasMaker : EditorWindow
{
	....
    
    /// <summary>
    /// Combine all sprites into a single texture and save it to disk.
    /// </summary>
    
    static public bool UpdateTexture (INGUIAtlas atlas, List<SpriteEntry> sprites)
    {
        // Get the texture for the atlas
        var tex = atlasTexture;
        var oldPath = (tex != null) ? AssetDatabase.GetAssetPath(tex.GetInstanceID()) : "";
        var newPath = NGUIEditorTools.GetSaveableTexturePath(atlas as UnityEngine.Object, atlasTexture);
    
        // Clear the read-only flag in texture file attributes
        if (System.IO.File.Exists(newPath))
        {
            System.IO.FileAttributes newPathAttrs = System.IO.File.GetAttributes(newPath);
            newPathAttrs &= ~System.IO.FileAttributes.ReadOnly;
            System.IO.File.SetAttributes(newPath, newPathAttrs);
        }
    
        bool newTexture = (tex == null || oldPath != newPath);
    
        if (newTexture)
        {
            // Create a new texture for the atlas
            tex = new Texture2D(1, 1, TextureFormat.ARGB32, false);
        }
        else
        {
            // Make the atlas readable so we can save it
            tex = NGUIEditorTools.ImportTexture(oldPath, true, false, false);
        }
    
        #region NGUI Atlas 압축 관련 처리 [blueasa / 2023-04-07]
        /// NGUI Atlas가 압축돼 있으면,
        /// 압축 해제한 다음 Atlas 만든 후 원래 압축으로 돌리도록 함
    
        // TextureImporter 가져오기
        string strPath = AssetDatabase.GetAssetPath(tex);
        TextureImporter tiAtlas = AssetImporter.GetAtPath(strPath) as TextureImporter;
        TextureImporterCompression ticTextureCompression_Original = TextureImporterCompression.Uncompressed;
        bool bTextureCompressed = false;
    
        // New Atlas 관련 예외처리(New Atlas는 로직 타지 않도록 수정)
        if (false == newTexture)
        {
            Debug.LogWarningFormat("[Atlas TextureCompression] {0}", tiAtlas.textureCompression);
    
            if (tiAtlas.textureCompression != TextureImporterCompression.Uncompressed)
            {
                // Atlas Texutre 압축 여부
                bTextureCompressed = true;
                // 압축 설정 백업
                ticTextureCompression_Original = tiAtlas.textureCompression;
                // 압축 설정 변경
                tiAtlas.textureCompression = TextureImporterCompression.Uncompressed;
                // 변경 내용 적용
                AssetDatabase.ImportAsset(strPath);
            }
        }
        #endregion
    
        // Pack the sprites into this texture
        if (PackTextures(tex, sprites))
        {
            var bytes = tex.EncodeToPNG();
            System.IO.File.WriteAllBytes(newPath, bytes);
            bytes = null;
        
            // Load the texture we just saved as a Texture2D
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
            tex = NGUIEditorTools.ImportTexture(newPath, false, true, !premultipliedAlpha);
        
            // Update the atlas texture
            if (newTexture)
            {
                if (tex == null)
                {
                    Debug.LogError("Failed to load the created atlas saved as " + newPath);
                    EditorUtility.ClearProgressBar();
                }
                else
                {
                    var mat = spriteMaterial;
        
                    if (mat == null)
                    {
                        var matPath = newPath.Replace(".png", ".mat");
                        var shader = Shader.Find(NGUISettings.atlasPMA ? "Unlit/Premultiplied Colored" : "Unlit/Transparent Colored");
                        mat = new Material(shader);
	    
                        // Save the material
                        AssetDatabase.CreateAsset(mat, matPath);
                        AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
        
                        // Load the material so it`s usable
                        mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
                        spriteMaterial = mat;
                    }
        
                    mat.mainTexture = tex;
                }
        
                ReleaseSprites(sprites);
        
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
            }
        
            #region NGUI Atlas 압축 관련 처리 [blueasa / 2023-04-07]
            if (true == bTextureCompressed)
            {
                // 압축 설정 복원
                tiAtlas.textureCompression = ticTextureCompression_Original;
                // 변경 내용 적용
                AssetDatabase.ImportAsset(strPath);
            }
            #endregion
        
            return true;
        }
        else
        {
            if (!newTexture) NGUIEditorTools.ImportTexture(oldPath, false, true, !premultipliedAlpha);
    
            //Debug.LogError("Operation canceled: The selected sprites can`t fit into the atlas.\n" +
            //	"Keep large sprites outside the atlas (use UITexture), and/or use multiple atlases instead.");
    
            EditorUtility.DisplayDialog("Operation Canceled", "The selected sprites can`t fit into the atlas.\n" +
                    "Keep large sprites outside the atlas (use UITexture), and/or use multiple atlases instead", "OK");
            return false;
        }
    }
    
    ....
}

 

 

반응형
Posted by blueasa
, |

[링크] https://specialmylife.tistory.com/entry/NGUI-%EC%9C%A0%EC%9A%A9%ED%95%9C-%EB%A7%81%ED%81%AC

 

NGUI 유용한 링크

NGUI 유용한 링크Alpha-based hit detection Nicki's Depth Management system Improved Localization System How to make health bars Action Bar system Packed Font with Outline/Shadow Font using Alpha-tested magnification (Valve's approach) Atlas switching /

specialmylife.tistory.com

 

반응형
Posted by blueasa
, |

Unity 2021.3.14f1

NGUI 2022.06.08

 

NGUI의 UILabel에 있는 Dynamic Font 글자 사용시 가끔 글자가 깨졌다가 돌아오는 현상이 있어서 수정함.

 

UILabel에서 글자가 깨지는 이유는 Dynamic Font Texture 사이즈가 256x256을 기본으로 쓰다가,

텍스쳐 사이즈가 모자라면 512x512로 늘리는데 사이즈가 변경되는 시점에 잠시 글자가 깨졌다가 보이게 된다고 한다.

그래서 사이즈 변경할 필요 없게 사용하는 폰트의 글자를 모두 로드해 버리기로 했다.

(아래 첨부된 txt 파일의 글자를 로드하니 2048x2048이 나온다. 처음부터 로드하고 써버리기로 함.)

 

아래 [참조]링크의 소스를 참조해서 정리해서 아래와 같이 FontManager에 적용했다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FontManagerSGT : MonoSingleton<FontManagerSGT>
{
    public delegate void OnChangeFontDelegate(eLanguage _eLanguage);
    public static event OnChangeFontDelegate OnChangeFontEvent;

    public NGUIFont m_nguiFontMain_Dynamic;
    public Font m_fontMain_GO;  // Global
    public Font m_fontMain_JA;  // Japan

    private eLanguage m_eLanguage_Prev = eLanguage.None;
    private eLanguage m_eLanguage_Current = eLanguage.None;

    private string m_strReferenceTxt_GO = null;
    private string m_strReferenceTxt_JA = null;
    private Texture m_textureFontMainTexture = null;
    private TextAsset m_textReferenceTxt = null;


    void Start()
    {
        SetDontDestroy();
    }

    void OnEnable()
    {

    }

    void OnDisable()
    {

    }

    public void SetFont(eLanguage _eLanguage)
    {
        // 폰트매니저용 언어로 변경(JA 외는 GO(글로벌)로 변환)
        m_eLanguage_Current = GetLanguageForFont(_eLanguage);

        ChangeFont(m_eLanguage_Current);
        m_eLanguage_Prev = m_eLanguage_Current;
    }

    public eLanguage GetLanguageForFont(eLanguage _eLanguage)
    {
        switch (_eLanguage)
        {
            // Japan
            case eLanguage.JA:
                return eLanguage.JA;

            // Global(Less)
            default:
                return eLanguage.GO;
        }
    }

    void ChangeFont(eLanguage _eLanguage)
    {
        Debug.LogFormat("[eLanguage] [Prev] {0} [Current] {1}", m_eLanguage_Prev, _eLanguage);

        // 같은 폰트면 Pass
        if (m_eLanguage_Prev == _eLanguage)
            return;

        Debug.Assert(null != m_nguiFontMain_Dynamic);
        Debug.Assert(null != m_fontMain_GO);
        Debug.Assert(null != m_fontMain_JA);

        switch (_eLanguage)
        {
            // Japan
            case eLanguage.JA:
                {
                    m_nguiFontMain_Dynamic.dynamicFont = m_fontMain_JA;
                    FixBrokenWord_JA();
                }
                break;

            // Global(Less)
            default:
                {
                    m_nguiFontMain_Dynamic.dynamicFont = m_fontMain_GO;
                    FixBrokenWord_GO();
                }
                break;
        }

        OnChangeFontEvent?.Invoke(_eLanguage);
    }

    void FixBrokenWord_JA()
    {
        if (m_strReferenceTxt_JA == null)
        {
            m_textReferenceTxt = Resources.Load("ReferenceTxt/ja") as TextAsset;
            if (null != m_textReferenceTxt)
            {
                m_strReferenceTxt_JA = m_textReferenceTxt.ToString();
            }
        }

        if (null != m_strReferenceTxt_JA)
        {
            m_fontMain_JA.RequestCharactersInTexture(m_strReferenceTxt_JA);
            m_textureFontMainTexture = m_fontMain_JA.material.mainTexture; // Font 내부 텍스쳐
            Debug.LogWarning(string.Format("[m_strReferenceTxt_JA] texture : {0}x{1}", m_textureFontMainTexture.width, m_textureFontMainTexture.height)); // 텍스쳐 크기
        }
        else
        {
            Debug.LogWarning("m_strReferenceTxt_JA is null");
        }
    }

    void FixBrokenWord_GO()
    {
        if (m_strReferenceTxt_GO == null)
        {
            m_textReferenceTxt = Resources.Load("ReferenceTxt/go") as TextAsset;
            if (null != m_textReferenceTxt)
            {
                m_strReferenceTxt_GO = m_textReferenceTxt.ToString();
            }
        }

        if (null != m_strReferenceTxt_GO)
        {
            m_fontMain_GO.RequestCharactersInTexture(m_strReferenceTxt_GO);
            m_textureFontMainTexture = m_fontMain_GO.material.mainTexture; // Font 내부 텍스쳐
            Debug.LogWarning(string.Format("[m_fontMain_GO] texture : {0}x{1}", m_textureFontMainTexture.width, m_textureFontMainTexture.height)); // 텍스쳐 크기
        }
        else
        {
            Debug.LogWarning("m_strReferenceTxt_GO is null");
        }
    }
}

 

GO(Global)는 JA(일본어)를 제외한 모든 폰트를 합친 글로벌용 폰트이다.

일본어 한자가 중국어(번체)(대만)와 ASCII 코드가 겹치는 문제로 폰트 자체를 분리했다.

 

소스상에서 Resources.Load 하고 있는 go.txt와 ja.txt는 아래 올려둔다.

폰트를 2개로 분리해놔서 해당 폰트 사용 시, 맞는 txt를 로드하기 위해 Load 파일도 2개이다.

 

첨부된 파일은 폰트 병합 할 때 쓰는 참조용 데이터이기 때문에 모두 로드하면 폰트에 있는 모든 글자를 쓰게 되므로 이걸 로드해서 쓰게되면 더이상 텍스쳐가 커질일은 없을거라 예상된다.

 

 

[폰트 텍스쳐 확장을 위한 참조용 txt 파일]

go.txt
0.03MB
ja.txt
0.01MB

 

 

[참조] https://blueasa.tistory.com/2688

 

[펌] Unity 동적 글꼴 텍스트 깨짐 솔루션(Dynamic Font Broken)

Unity의 동적 글꼴을 사용하여 텍스트를 그릴 때 두 개의 UI 인터페이스가 열리면 그 뒤에 있는 텍스트가 깨집니다(완전히 엉망이 됨). 내가 사용하는 UI 플러그인은 Daikon Forge입니다. 라벨 업데이

blueasa.tistory.com

[참조2] https://blueasa.tistory.com/2664

 

[펌] NGUI - Dynamic Font 글자 깨짐? 사라짐? 현상

게임도중 핸드폰에서 Home 키를 눌러 배경화면으로 이동 후 Server와 끊기기를 기다리고 다시 Server와 리커넥팅 되도록 해서 팝업 떴는데.. Font가 깨졌다. 뭐지.. .... UILabel 에서 DynamicFont 가 이상한

blueasa.tistory.com

 

반응형
Posted by blueasa
, |

프로젝트는 NGUI를 사용하며 최근에 로딩 인터페이스의 프롬프트 텍스트가 깨지는 버그가 발생했습니다.


다음 기사를 참조했습니다.

http://blog.csdn.net/langresser_king/article/details/22095235

이 문제가 발생하는 이유를 먼저 이해합시다.



이해해야 할 것은 NGUI의 UILabel은 Unity에서 제공하는 글꼴을 글꼴의 입력으로 사용한다는 것입니다.

Unity는 글꼴 생성에 대해 매우 경제적이며 이는 정상적인 엔진이며 엔진은 이와 같아야 합니다.



(1) Loading 인터페이스의 UILabel에 세 단어 로딩을 표시하면 Unity가 이 세 단어의 텍스처를 생성하고 128x128의 FontTexture가 충분해야 합니다. 그런 다음 UILabel은 글꼴에 따라 각 단어의 그래픽을 가져옵니다.



(2) 그런 다음 다른 인터페이스를 열고 이 인터페이스에 많은 문자가 있을 때 128x128 FontTexture가 충분하지 않으면 Unity는 새 FontTexture를 생성하고 콜백을 발생시킵니다.

(3) 그런 다음 NGUI의 UILabel에서 이 콜백을 처리하고 모든 UILabel을 트래버스하고 UILabel의 모든 내용을 Font에 푸시하여 FontTexture에 대한 새 텍스트 그래픽을 생성합니다.

static BetterList<UILabel> mList = new BetterList<UILabel>();
protected override void OnInit ()
{
    base.OnInit();
    mList.Add(this);
    SetActiveFont(trueTypeFont);
}

 

//每次新建一个UILabel,都会读取UILabel上面的内容,在FontTexture上添加一块字体纹理。当FontTexture超过了原来的大小时,就会抛弃原来的FontTexture,用一个新的FontTexture,然后遍历所有的UILabel,往新的FontTexture上添加字体纹理。
static void OnFontTextureChanged ()
{
    for (int i = 0; i < mList.size; ++i)
    {
        UILabel lbl = mList[i];
 
        if (lbl != null)
        {
            Font fnt = lbl.trueTypeFont;
 
            if (fnt != null)
            {
                fnt.RequestCharactersInTexture(lbl.mText, lbl.mPrintedSize, lbl.mFontStyle);
            }
        }
    }
 
    //这段代码让UILabel重新读取UV 重新渲染,下一帧文字就是正常的了
    for (int i = 0; i < mList.size; ++i)
    {
        UILabel lbl = mList[i];
 
        if (lbl != null)
        {
            Font fnt = lbl.trueTypeFont;
 
            if (fnt != null)
            {
                lbl.RemoveFromPanel();
                lbl.CreatePanel();
            }
        }
    }
}


(4) 마지막으로 UILabel이 다시 렌더링됩니다.

깨진 글꼴은 (2)에서 나타납니다 UILabel이 이미 렌더링 중일 때 Unity는 새 FontTexture를 생성하므로 현재 프레임에 표시되는 그래픽에 문제가 있을 것입니다.

해결책은 게임의 모든 캐릭터를 포함하여 처음에 글꼴에 충분한 텍스트를 제공하여 향후 게임을 푸시할 필요가 없고 FontTexture를 영원히 사용할 수 있도록 하는 것입니다.

UILabel에 기능 추가

    private static void DynamicFontBrokenFix(Font fnt)
    {
        if(fnt==null)
        {
            return;
        }
 
        if(fnt.characterInfo.Length<500)
        {
            string tmpStr = Resources.Load<TextAsset>("DynamicFontBrokenFix").text;
            fnt.RequestCharactersInTexture(tmpStr, 32);
        }
    }


그런 다음 글꼴을 설정할 때 호출

protected void SetActiveFont (Font fnt)
{
    if (mActiveTTF != fnt)
    {
        if (mActiveTTF != null)
        {
            int usage;
 
            if (mFontUsage.TryGetValue(mActiveTTF, out usage))
            {
                usage = Mathf.Max(0, --usage);
 
                if (usage == 0)
                {
                    mActiveTTF.textureRebuildCallback = null;
                    mFontUsage.Remove(mActiveTTF);
                }
                else
                {
                    mFontUsage[mActiveTTF] = usage;
                }
            }
			else
            {
            	mActiveTTF.textureRebuildCallback = null;
            }
		}
 
		mActiveTTF = fnt;
        DynamicFontBrokenFix(mActiveTTF);
 
		if (mActiveTTF != null)
		{
			int usage = 0;
 
			// Font hasn't been used yet? Register a change delegate callback
			if (!mFontUsage.TryGetValue(mActiveTTF, out usage))
				mActiveTTF.textureRebuildCallback = OnFontTextureChanged;
#if UNITY_FLASH
			mFontUsage[mActiveTTF] = usage + 1;
#else
			mFontUsage[mActiveTTF] = ++usage;
#endif
		}
	}
}


DynamicFontBrokenFix는 게임의 모든 문자를 저장하는 텍스트입니다.
http://blog.csdn.net/huutu http://www.liveslives.com에서 전송됨

FontTexture가 여러 번 생성되는 것을 방지하기 위한 최적화 지점이기도 합니다.

 

[출처] https://blog.csdn.net/huutu/article/details/61923191

 

NGUI UILabel 文字破碎__Captain的博客-CSDN博客

项目使用NGUI,最近碰到 Loading界面的提示文字破碎的Bug。 参考了以下文章 http://blog.csdn.net/langresser_king/article/details/22095235 转自http://blog.csdn.net/huutu http://www.liveslives.com 下面先来了解一下为什么会

blog.csdn.net

 

반응형
Posted by blueasa
, |

Unity의 동적 글꼴을 사용하여 텍스트를 그릴 때 두 개의 UI 인터페이스가 열리면 그 뒤에 있는 텍스트가 깨집니다(완전히 엉망이 됨). 내가 사용하는 UI 플러그인은 Daikon Forge입니다. 라벨 업데이트 메커니즘으로 인해 최종 성능이 깨진 텍스트 표시보다 나쁠 수 있습니다. 텍스트 컨트롤이 계속 새로 고쳐지고 열릴 새 인터페이스가 표시되지 않을 가능성이 큽니다.

         이것은 근본적으로 Unity의 동적 글꼴 구현이 충분히 똑똑하지 않다는 사실 때문입니다. 이론적으로 NGUI에도 이러한 문제가 있습니다. 동적 글꼴을 사용하고 많은 텍스트를 렌더링하는 한.

         NGUI와 Daikon Forge는 동적 글꼴인 텍스트를 그릴 때 내부적으로 Unity의 글꼴을 사용합니다. RequestCharactersInTexture 함수를 사용하여 글꼴에 텍스트 정보 업데이트를 요청한 다음 GetCharacterInfo를 사용하여 렌더링할 텍스트 정보를 가져옵니다. GetCharacterInfo를 호출할 때 RequestCharactersInTexture를 통해 모든 텍스트가 요청되었는지 확인하십시오.

         요청 시점에 Font 내부에 유지되는 텍스처가 충분하지 않으면 textureRebuildCallback의 콜백이 트리거되어 Font를 사용하는 외부 객체에 내부 텍스처가 업데이트되었고 외부 객체를 새로 고쳐야 함을 알립니다.

        Unity 글꼴의 기본 텍스처 크기는 256x256이며 순수 영어 글꼴의 경우에는 충분합니다. 하지만 한자나 일본어 같은 동양글꼴은 전혀 부족하다. 앞에서 언급한 두 플러그인은 텍스트를 그릴 때 단락을 요청하는 데 사용되며, Unity의 새로 고침 콜백이 트리거되면 모든 텍스트 컨트롤이 새로 고쳐집니다. 이렇게 하면 글꼴이 쉽게 깨질 수 있습니다. 정상적인 상황에서는 한 번에 많은 텍스트를 요청하지 않으며 사용된 텍스처는 256x256을 초과하지 않으며 Unity는 텍스처 크기를 자동으로 확장하지 않습니다. 그리고 콜백 함수에서 글꼴을 다시 새로 고칠 때 텍스처가 충분하지 않아 다른 새로 고침 콜백을 트리거하기 쉽습니다. 따라서 중단되고 지속적으로 새로 고쳐지는 상황을 표시하기 위해 텍스트 컨트롤이 전송됩니다.

        문제의 원인을 알면 해결책이 나옵니다. 충분한 텍스트를 요청하는 한 Unity는 내부적으로 텍스처 크기를 자동으로 확장하므로 지속적인 새로 고침 상황을 피할 수 있습니다. 한자 2000자 텍스트를 준비했는데, 텍스트 정보 요청 후 내부 텍스쳐를 기본적으로 게임에 충분한 크기인 1024x1024 크기로 확장했습니다. 어느 날 이것이 충분하지 않다고 생각되면 한자를 더 준비하고 질감을 2048x1024로 확장하십시오.

 

static string chineseTxt = null;
public UnityEngine.Font baseFont;

public void FixBrokenWord()
{
    if (chineseTxt == null) 
    {
        TextAsset txt = Resources.Load("config/chinese") as TextAsset;
        chineseTxt = txt.ToString();
    }

    baseFont.RequestCharactersInTexture(chineseTxt);
    Texture texture = baseFont.material.mainTexture; // Font的内部纹理
    Debug.Log(string.Format("texture:{0} {1}", texture.width, texture.height)); // 纹理大小
}


그 중 baseFont는 NGUI나 Daikon Forge의 텍스트 렌더링 컨트롤에 사용되는 UnityEngine.Font입니다.baseFont를 초기화할 때 FixBrokenWord 함수를 호출합니다(한 번만 호출하면 됨). 일반적으로 사용되는 한자 목록이 포함된 텍스트를 읽은 다음(인터넷에서 일반적으로 사용되는 한자 목록에서 복사하기만 하면 됨) 이 텍스트의 정보를 요청하면 내부 텍스처가 자동으로 확장됩니다.

 

[출처] https://blog.csdn.net/e295166319/article/details/54861275

 

Unity动态字体文字破碎的解决方法(Dynamic Font Broken)_起个名字真的好难啊的博客-CSDN博客

 使用Unity的动态字体绘制文字的时候,打开两个ui界面的时候,后面的文字会显示破碎(完全乱掉)。我使用的ui插件是Daikon Forge,由于其label的更新机制问题,最终表现的结果可能比一个文本显

blog.csdn.net

 

반응형
Posted by blueasa
, |

게임도중 핸드폰에서 Home 키를 눌러 배경화면으로 이동 후 Server와 끊기기를 기다리고 다시 Server와 리커넥팅 되도록 해서 팝업 떴는데.. Font가 깨졌다. 뭐지..

 

....

 

UILabel 에서 DynamicFont 가 이상한 것 같다는 사수님의 말을 듣고 구글링하고 임시 해결방안으로 Font 스크립트에 접근해 미리(반강제?) 쓸 텍스트스트링을 넣어줌으로 해결을 하였다!! 코드는 대충 이렇다! 

 

Font fc_Font_KR = GameObjectUtil.FindChildComponentByName<UILabel>(MainUIManager.Instance.uiMsgBox.transform, "Txt.Desc").trueTypeFont;//(MainUIManager.Instance.uiMsgBox.transform, "Txt.Desc");

fc_Font_KR.RequestCharactersInTexture(Utility.GetStringFromTBL(250091), 16, FontStyle.Normal);

 

주요 함수는 Font.RequestCharactersInTexture(string str, size, FontStyle); 

 

함수 공부는 개인적으로.. 는..

 

구글링을 하던중 .. 좋은 지식 공유

 

https://www.facebook.com/be2ls/posts/644653395565677

 

밑에는 내용 !

-------------------------------------------------------------------------------------------------------------

 

* 유니티에서 Dynamic Font의 글자 사라짐 문제 대응 방법

유니티에서 지원하는 Dynamic Font는 상당히 편리하다.
기존에 한글 폰트를 화면에 표시하려면,
한글 유니코드에 해당하는 모든 글자를 폰트 텍스쳐에 담아놓고
써야해서, 메모리 낭비가 컸고, 글자 크기도 크게 하기가 힘들었다.

반면 Dynamic Font는 그때 그때 사용하는 글자들만 폰트 텍스쳐에 그려두고 사용하기 때문에
메모리 낭비가 적고, 폰트를 크게 표시할 수 있다는 장점이 있다.

하지만, 유니티 자체의 버그인지 특정 폰의 문제인지 몇몇 폰에서 플레이를 하다보면,
글자들이 깨지거나, 일부 글자가 아예 표시되지 않는 버그가 발견되고 있고,
현재까지 배포된 유니티 버전에서는 아직 해결되지 않은 것으로 보인다.

해결 방법을 구글링 해보았지만, 아직 완벽한 해결책은 없는 것 같아서
직접 여러가지 실험을 해보고 대응 방법을 찾아보았다.

1. Dynamic Font의 텍스쳐 관리 방식

맨 처음 게임이 실행되면 기본 폰트 텍스쳐 크기는 256*256이다.
여기에 글자들이 추가되면서 글자를 추가할 공간이 없을 때
256*256이 256*512, 512*512, 그 다음에는 512*1024 이런식으로 텍스쳐 크기가 증가한다.

폰트 텍스쳐에 새로운 글자가 추가되었는데 더이상 추가할 수 있는 공간이 없을 때, 
폰트 텍스쳐가 재정렬되는데, 이 경우 크기가 변경될 수도 있고, 변하지 않을 때도 있다.
더 이상 글자를 추가할 공간이 없을 때, 사용하지 않는 글자들을 정리하기 때문에
정리를 하고 나서 공간이 남는 경우에는 크기가 그대로 유지 된다.
나름 합리적으로 폰트 텍스쳐를 관리하고 있는 것인데,
문제는 특정 폰에서 이 순간에 글자가 사라지는 것이다.

실험을 해본 결과 문제가 되는 폰에서는 폰트 텍스쳐가 리셋되는 순간 크기의 변화가 있으면
정상적으로 표시가 되는데, 사이즈의 변화가 없으면 일부 글자가 표시되지 않는 문제가 발생하고 있었다.

2. 대응 방법

대응방법은 아름답지는 않지만, 단순한 편이다. 
폰트 텍스쳐가 리셋되는 순간 만약 폰트 텍스쳐의 크기가 변경되지 않았다면,
변경될 때까지 폰트 텍스쳐에 존재하지 않는 글자를 계속 추가해주는 것이다.
코딩은 대략 다음과 같다.

1) 유니티의 Font에는 Font.textureRebuildCallback이라는 콜백함수를 등록할 수 있다.
게임 시작 시점에 콜백 함수에 특정 함수를 지정해두면, 폰트 텍스쳐의 리셋 시점에 해당 함수가 
호출된다. 
ex: this.font.textureRebuildCallback = this.SizeChanged;

2) 시작 시점의 폰트 텍스쳐의 크기를 변수로 저장해둔다.

ex: 
this.fontWidth = this.font.material.mainTexture.width;
this.fontHeight = this.font.material.mainTexture.height;

3) 문제는 폰트 텍스쳐 크기가 변하지 않는 경우이기 때문에, 
콜백함수 안에 폰트 텍스쳐의 크기가 기존 크기와 같은지를 체크한다.

ex: 
if (this.font.material.mainTexture.width == this.fontWidth && this.font.material.mainTexture.height == this.fontHeight)
{
...

4) 기존 사이즈와 똑같으면, 한글을 맨처음부터 한글자씩 집어넣는다.
맨 처음 '가'를 font.GetCharacterInfo를 이용해서, 폰트 텍스쳐에 '가' 글자가 들어있는지 확인해서,
들어있지 않다면 font.RequestCharactersInTexture함수를 이용해서, '가'를 추가해준다.
처음에는 5글자 정도를 추가하고, Invoke함수 등을 이용해서 잠시 기다린다.
만약, 추가한 5글자로 인해서, 크기가 커진다면 다시 콜백함수가 호출될 것이고, 그렇다면 글자들이 제대로 보일 것이므로, 
해당 루틴을 끝낸다.
하지만, 커지지 않았다면, 다시 10글자 정도를 추가하고, 그래도 안되면 15글자로 늘리는 방식이다.
이런 루틴을 반복하다보면 어느 순간 폰트 텍스쳐의 크기가 커진다.
(폰트 사이즈가 너무 작고, 폰트 텍스쳐 크기가 굉장히 커서 모든 한글 글자를 포함할 경우라면 무한루프가 될 수도 있을거 같다.)

한글을 자동으로 하나씩 추가하려면, 
아래와 같은 코드를 이용해서, 초중종성의 값을 증가시켜가면 된다.

ushort mUniCodeKoreanBase = 0xAC00;
int iUniCode = mUniCodeKoreanBase + (curFirst * 21 + curMiddle) * 28 + curEnd;
char temp = System.Convert.ToChar(iUniCode);

3. 결론

문제가 있던 폰에서 테스트해 본 결과, 폰트 텍스쳐의 크기가 리셋되는 순간 잠깐 동안 일부 글자가 안보이다가,
곧바로 글자들이 제대로 복구되었다.
아주 깔끔한 해결책은 아니지만, 이정도면 충분히 쓸만하지 않을까 싶다.

어서 빨리 유니티에서 완벽한 패치가 나오면 좋겠다.

-------------------------------------------------------------------------------------------------------------

 

잉`s 블로그 : 네이버 블로그

I.. N.. G..

blog.naver.com

 

반응형
Posted by blueasa
, |

- ParticleSystem, Spine, TMP 등 Renderer를 가진 오브젝트들을 NGUI 위에 올리고 Depth를 맞추기 위한 소스

  (NGUI Widget의 Depth 제어 부분을 Renderer를 가진 오브젝트에서도 셋팅되도록 함)

- 출처에서는 ParticleSystem용으로 이름을 지어놨는데 어차피 Renderer를 가지고 있는 것들은 다 되는거라 이름 변경함

 

NGUIRendererWidget.cs
0.00MB

 

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


[AddComponentMenu("NGUI/UI/NGUI Renderer Widget")]
public class NGUIRendererWidget : UIWidget
{
    protected Renderer m_Renderer;
    protected Material m_Mat;

    public override Material material
    {
        get
        {
            return m_Mat;
        }

        set
        {
            if (m_Mat != value)
            {
                RemoveFromPanel();
                m_Mat = value;
                MarkAsChanged();
            }
        }
    }

    protected override void OnEnable()
    {
        Init();
        base.OnEnable();
    }

    protected void Init()
    {
        if (null == m_Renderer)
        {
            m_Renderer = GetComponent<Renderer>();
        }

        if (null == m_Renderer)
        {
            return;
        }

        if (null == material)
        {
            if (false == Application.isPlaying)
            {
                material = m_Renderer.sharedMaterial;
                m_Renderer.sharedMaterial = material;
            }
            else
            {
                material = m_Renderer.material;
                m_Renderer.material = material;
            }
        }
    }

    private void OnWillRenderObject()
    {
        if (null != drawCall && drawCall.finalRenderQueue != material.renderQueue)
        {
            material.renderQueue = drawCall.finalRenderQueue;
        }

        if (Application.isPlaying == true && m_Renderer.material != material)
        {
            m_Renderer.material = material;
        }
    }

    public override void OnFill(List<Vector3> verts, List<Vector2> uvs, List<Color> cols)
    {
        for (int i = 0; i < 4; i++)
        {
            verts.Add(Vector3.zero);
            uvs.Add(Vector2.zero);
            cols.Add(color);
        }
    }
}

 

[출처] https://tenlie10.tistory.com/169

 

[Unity | 유니티] NGUI와 ParticleSystem간 Depth 조정

ParticleSystem에 해당 스크립트를 추가하고 타 NGUI 컴포넌트처럼 Depth값을 조정하면 된다. UIParticleWidget.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33..

tenlie10.tistory.com

 

반응형
Posted by blueasa
, |