| 개요

 

기존 AssetBundle 시스템에서는 각 번들별로 지정된 Label을 기준으로 번들파일이 생성되었다.

[Addressable][BundleMode] 설정을 통해 3가지 형태의 패킹 옵션을 제공한다.

(BundleMode를 어떻게 설정 하느냐에 따라 번들의 네이밍에도 영향을 준다.)

 

번들의 네이밍에 관한 옵션은 [Bundle Naming Mode]로 설정이 가능하다.

 

| 설정

에디터 상단 메뉴 > Windows > Asset Management > Addressables > Groups

Addressables Groups 윈도우에서 그룹 선택 > Inspector 윈도우

 

혹은

 

AddressableAssetsData/AssetGroups/{어셋그룹명}.asset 파일선택 > IInspector 윈도우

 

 

| Pack Together

 

그룹 전체가 하나의 번들로 패킹되는 방식

어셋그룹명_assets_all_해쉬값.bundle와 같은 형식으로 네이밍 된다.

예)

Addressable Asset Group: dungeon

Bundle Mode: Pack Together

Bundle Naming Mode: Append Hash to Filename

 

> dungeon_assets_all_5cc5b95cb94769ba209aa37dbb298c73.bundle

 

| Pack Seperately

 

그룹 내부의 어셋마다 개별 번들이 패킹되는 방식

개별 번들이 만들어지지만, 어셋 그룹의 설정은 공유한다.

어셋그룹명_assets_assets/어셋어드레스_해쉬값.bundle와 같은 형식으로 네이밍 된다.

 

예)

Addressable Asset Group: Scenes-DungeonBG

Bundle Mode: Pack Seperately

Bundle Naming Mode: Append Hash to Filename

 

> scenes-dungeonbg_scenes_scenes/dungeonbg/폴더/어셋어드레스_해쉬값.bundle

 

| Pack Together By Label

 

라벨 기준으로 패킹되는 방식 (기존 AssetBundle의 그것과 유사)

Pack Seperatley와 마찬가지로, 번들은 분리되어 패킹되지만 어셋 그룹의 설정은 공유한다.

어셋그룹명_assets_라벨명_해쉬값.bundle와 같은 형식으로 네이밍 된다.

 

예)

Addressable Asset Group: dungeon

Bundle Mode: Pack Together By Label

Bundle Naming Mode: Append Hash to Filename

Label: label1

 

> dungeon_assets_label1_해쉬값.bundle

 

예2) 라벨을 여러개 지정한 경우

> dungeon_assets_label1label2_해쉬값.buddle

 

| 마치며

 

BundleMode옵션을 활용하여, 용도와 상황에 맞게 번들을 분리 / 통합하여 패킹할 수 있는 방법을 알아보았다.

활용방법이나 케이스들을 가정해보았다.

 

Case 1) 사이즈가 큰 씬이 포함된 어셋그룹은 Pack Seperately로 지정해서 분리 패킹, 번들의 파일 사이즈를 줄인다.

Case 2) 업데이트되는 신규 컨텐츠 A 그룹은 Pack Together By Label로 지정해서 신규 컨텐츠 진입점에서 번들을 다운로드

 

신규 컨텐츠 A 어셋 그룹

ㄴSeason 1 > Label: 신규 컨텐츠 A, 시즌 1

ㄴSeason 2 > Label: 신규 컨텐츠 A, 시즌 2

반응형

| 개요

Addressable 시스템에서 추가된 핵심 시스템 중 AnalyzeRule 시스템이 있다.

  • 에디터 상단 메뉴 > Window > Asset Management > Addressables > Analzye

AssetBundle과 관련된 다양한 규칙들을 설정하고, 해당 규칙에 대한 검증 및 문제 해결까지 가능한 시스템이다.

기본적으로 아래와 같은 규칙이 제공된다.

 

  • 중복 번들 종속성
  • 빌트인 리소스와 어드레서블 어셋간 종속성
  • 씬과 어드레서블간 종속성
  • 번들 레이아웃 프리뷰 기능 (명시적, 암묵적으로 포함되는 어셋목록 리스트업)
패키지에 포함되어 있는 Analyze Rule

기존에도 어셋의 종속성이나 Duplicated 이슈를 검증할 수 있는 AssetBundle Rrowser라는 툴을 제공되었으나,

기능을 확장하거나, 번들마다 설정을 달리하는 등의 작업이 용이하진 않았다.

Analyze System은 종전의 문제점들에서 착안 다양한 옵션을 제공하고 있다.

| Custom Analyze Rule 추가방법

using UnityEditor;
using UnityEditor.AddressableAssets.Build.AnalyzeRules;

public class CustomRule : AnalyzeRule
{
        public override string ruleName
        {
            get { return "룰 이름"; }
        }

        // 문제 해결이 가능한지 여부
        public override bool CanFix
        {
            get { return false; }
            set { }
        }

        public override void FixIssues(AddressableAssetSettings settings)
        {
            base.FixIssues(settings);
            // ...
            // 문제 해결 로직
            // ...
        }

        public override List<AnalyzeResult> RefreshAnalysis(AddressableAssetSettings settings)
        {
            var results = new List<AnalyzeResult>();
            // ...
            // 분석 로직
            // ...
            results.Add(new AnalyzeResult()
            {
                    resultName = "Result Message",
                    severity = UnityEditor.MessageType.Info | Warning | Error
            });
            return results;
        }
}

[InitializeOnLoad]
class RegisterCustomRule
{
    static RegisterCustomRule()
    {
        // Analyze System에 등록을 해야만 Analzye 윈도우에서 노출됨      
        AnalyzeSystem.RegisterNewRule<CustomRule>();
    }
}
 

AnalyzeRule 클래스를 상속하고 AnalyzeSystem에 등록만 해주면 간단하게 커스텀 Analyze Rule을 추가할 수 있다.

 

| GroupSchema로 AnalyzeSystem 100% 활용하기

어셋 그룹을 추가하게되면 아래 스키마들이 기본적으로 추가되어 있다.

(Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets 파일 참조)

 

  • BundledAssetGroupScehema: 번들 빌드 및 로드 경로 / 압축 포맷 / 빌드 포함 여부 등 어셋번들에 관련된 다양한 옵션을 설정할 수 있는 스키마
  • ContentUpdateGroupSchema: 해당 컨텐츠가 업데이트 가능한지 여부를 설정
 

패키지에서 제공하는 스키마외에도 커스텀 스키마를 정의, 추가가 가능하다.

 

using UnityEditor;
using UnityEditor.AddressableAssets.Build.AnalyzeRules;
using UnityEditor.AddressableAssets.Settings;

public class CustomSchema : AddressableAssetGroupSchema
{
    [SerializeField] public string Foo;
}

public class CustomRule : AnalyzeRule
{
            // ...

        public override List<AnalyzeResult> RefreshAnalysis(AddressableAssetSettings settings)
        {
            ClearAnalysis();

            var results = new List<AnalyzeResult>();
            foreach (var group in settings.groups)
            {
                if (group.HasSchema<CustomSchema>())
                {
                    var schema = group.GetSchema<CustomSchema>();
                    // ...
                    // 분석 로직
                    // ...
                }
            }

            results.Add(new AnalyzeResult()
            {
                    resultName = "Result Message",
                    severity = UnityEditor.MessageType.Info | Warning | Error
            });
            return results;
        }
}
 

CustomSchema를 추가한 그룹에 대한 별도 처리가 가능하다.

 

이제부터 AnalyzeSystem 활용에 대한 다양한 활용방법이 떠오르는데, 이를테면 이런 것들이다.

 

  • [어셋그룹]내의 텍스쳐 어셋의 포맷 혹은 압축 및 POT 여부를 설정하는 스키마를 추가 > 해당 그룹내의 텍스쳐를 분석 후 지정된 포맷으로 변환
  • [어셋그룹]내의 저해상도 및 저성능 관련 내용을 설정하는 스키마 추가 > 해당 그룹내 저해상도 어셋을 자동생성
  • [어셋그룹]내 시즌 상품에 관련된 모든 어셋을 지정하는 스키마를 추가, 미노출되어야하는 어세에 대한 처리

 

Bundle Mode 중 Pack Together By Label이란 옵션이 있는데, 

(동일한 그룹내에서도 라벨 기준으로 번들 파일을 분리하여서 패키징할 수 있는 옵션)

패킹 레벨에서의 그룹과 Label의 용도의 차이점이 무엇인지 이해하기 어려웠다.

 

하지만 어셋 그룹 스키마와 Custom Analyze Rule에 관련된 예제를 보고는 확실히 개념이 정리가 되었다.

 

  • 라벨링이 필요없는 경우 > Pack Together
  • 번들 자체는 분리되어야 하지만, 어셋 그룹에 대한 설정은 공유하는 경우 > Pack Togher by Label

 

| 마치며

AnalzyeSystem의 사용법커스텀 룰을 추가하는 방법어셋 그룹 스키마와 활용하는 방법까지 알아보았다.

 

테스트나 번들 별 옵션 지정 등 다양한 기능을 제공하면서도 잘 정리된 느낌이 들었다.

종전의 시스템으론 커버하기 힘든 요구사항까지 다 핸들링 가능한 시스템으로 적극적으로 사용한다면 다양한 용도로 사용할 수 있을 것으로 기대된다.

 

 

| 참고

 

반응형
UnassignedReferenceException: The variable {variable} of {script} has not been assigned.
You probably need to assign the {variable} variable of the {script} script in the inspector.
 
 

MonoBehaviour에 SerializeField로 정의된 변수에 아무 오브젝트도 설정하지 않은 (None) 상태일 때 해당 변수를 참조하면 발생하게 된다.

 

사실 기본적인 내용이라 보기 어려운 에러인데,

문제는 유니티가 C# 최신버전을 적극적으로 지원하게 되면서 발생했다.

 

[Unity3d C# Compiler 관련 문서]

 

Null 조건 연산자나 Null 병합(결합) 할당 연산자를 유니티에서도 사용할 수 있게 되었다.

 

public class Foo : MonoBehaviour {
  [SerializeFIeld]
  GameObject _MemberFoo;

  public void MethodFoo() {
    // UnassignedReferenceException !
    _MemberFoo?.SetActive(false);  
  }
}
 

코드만 봤을 땐 문제될 게 없어보이지만, UnassignedReferenceException은 할당되지 않은 SerializeFIeld를 접근하기만 해도 발생하는 유니티 시스템 에러이기 때문에 해당 오류가 발생한다.

 

반응형

 

| 현상

라이브러리내의 로그 파일이 백엔드 프로세스에서 사용중으로 IO Exception이 발생하며 유니티 에디터가 실행 불가능한 상태가 되는 현상

 

 

| 해결방법

해당 프로세스를 종료한 뒤, 유니티 에디터 재실행

 

반응형

[C#] Warning 숨기기 에 이어서 Unity3d 환경에서 Warning을 숨길 수 있는 방법을 알아보자.

C# 포스팅에선 Warning disable을 기준으로 전처리에 대한 내용을 정리했다면, Unity3d에선 커스텀 스크립팅 심볼까지 추가적으로 알아보자.

 

| RSP 파일을 통한 설정

// 커스텀 Define Symbol 정의
-define:FOO_BAR

// Warning Disable
-nowarn:1234
 

우선 전처리 지시문을 작성한 뒤

csc.rsp 파일을 <ProjectPath>/Assets 폴더 하위 위치시키면 된다.

 

※smcs.rsp, mcs.rsp 파일을 지원 중단 예정

 

 

| 에디터를 통한 설정

File > Build Settings > Player Settings > Player > Script Compilation / Additional Compiler Arguments

 

  • 커스텀 Define Symbol의 경우 Scripting Define Symbols 섹션에 `+` 버튼을 클릭 후 내용을 입력
  • 추가적인 컴파일 인수는 Additional Compiler Arguments 섹션에서 `+` 버튼을 클릭 후에 지정
  • 커스텀 스크립팅 심볼 - Unity 매뉴얼

 

| 스크립트를 통한 설정

 

위 3가지 API를 사용해서 스크립팅 심볼을 정의할 수 있다.

다만, PlayerSettings.SetScriptingDefineSymbolsForGroup 의 경우 주의 사항이 몇 가지 있다.

 

  • 해당 메서드는 즉시 적용되지 않고, 스크립트를 다시 컴파일 하지 않음
  • 배치 모드에서 헤드리스로 실행되기 때문에 editor loop(스크립팅 심볼이 다시 컴파일 되는)가 발생하지 않음

따라서 즉시 적용하려면 csc.rsp 파일을 비동기적 상황에선 API를 이용해야 한다.

 

| 관련

 

반응형

| 개요

 

커스텀 설정 창의 예시

SettingsProvider클래스와 Attribute를 사용해서 Project Settings 혹은 Preferences 창을 커스터마이즈하는 법을 소개하고자 한다.

 

| 설정파일 추가

[FilePath("UserSettings/CustomSettings.asset",
          FilePathAttribute.Location.ProjectFolder)]
public sealed class CustomSettings: ScriptableSingleton<CustomSettings>
{
    // 설정 값 정의
    public string customSettingsValue = null;

    // 저장 함수 추가 (인자는 설정파일의 텍스트 저장 여부)
    public void Save() => Save(true);

    // 비활성화 되었을 때, 설정이 저장되도록
    void OnDisable() => Save();
}
 

| SettingsProvider를 재정의할 클래스 정의

 

sealed class CustomSettingsProvider : SettingsProvider
{
    public CustomSettingsProvider ()
      : base("Project/Custom Settings", SettingsScope.Project) {}

    public override void OnGUI(string search)
    {
        var settings = CustomSettings.instance;

        // Properties...
        var customSettingsValue = settings.customSettingsValue;

        EditorGUI.BeginChangeCheck();

        customSettingsValue = EditorGUILayout.TextField("Custom Settings", customSettingsValue);

        // 값이 변경되었을 경우 설정 값을 저장
        if (EditorGUI.EndChangeCheck())
        {
            settings.customSettingsValue = customSettingsValue;
            settings.Save();
        }
    }

    [SettingsProvider]
    public static SettingsProvider CreateCustomSettingsProvider()
      => new CustomSettingsProvider();
}
 
환경 설정에 커스텀 설정을 추가한 모습

| 키워드 설정

public CustomSettingsProvider ()
      : base("Project/Custom Settings", SettingsScope.Project) 
{
    keywords = new HashSet<string>( new []{ "CustomSetting" });
}
 

위와 같이 keywords를 설정해주면 검색창에서 해당 키워드로 리스트업할 수 있다.

 

| 커스텀 설정 바로가기

    void OnGUI()
    {
        if (GUILayout.Button("Open my custom preference"))
        {
            SettingsService.OpenUserPreferences("Preferences/MyCustomPref");
        }

        if (GUILayout.Button("Open my custom project settings"))
        {
            SettingsService.OpenProjectSettings("Project/MyCustomSettings");
        }
    }
 

 

 

| 마치며

SettingsProvider 클래스와 Attribute, ScriptableSingleton을 활용해서 간단히 설정창을 커스텀하는 방법을 알아보았다.

 

SettingsProvider를 상속한 추가 클래스 정의 없이, 아래의 Action 멤버변수를 지정하면 위 예제와 같이 설정 상세 UI를 커스터마이즈할 수 있다.

 

  • IMGUI: guiHandler
  • UI Toolkit: activeHandler

 

| 참고

 

 

반응형

Unity3d il2cpp 빌드 중 clang++와 같은 ndk 관련 툴 사용 중 위와 같은 에러가 발생

| 현상

stdout:
Building libil2cpp.so with AndroidToolChain
    Output directory: /Users/{UserName}/.jenkins/workspace/ANDROID_BUILD/Temp/StagingArea/assets/bin/Data/Native/armeabi-v7a
    Cache directory: /Users/{UserName}/.jenkins/workspace/ANDROID_BUILD/Library/il2cpp_android_armeabi-v7a/il2cpp_cache
ObjectFiles: 998 of which compiled: 371
    Time Compile: 74211 milliseconds Il2CppTypeDefinitions.cpp
    Time Compile: 68481 milliseconds Il2CppMethodPointerTable.cpp
    Time Compile: 55285 milliseconds Il2CppAttributes.cpp
    Time Compile: 41421 milliseconds Il2CppMetadataUsage.cpp
    Time Compile: 26292 milliseconds Il2CppInvokerTable.cpp
    Time Compile: 17629 milliseconds Il2CppGenericMethodPointerTable.cpp
    Time Compile: 12918 milliseconds Il2CppGenericClassTable.cpp
    Time Compile: 10404 milliseconds Bulk_Assembly-CSharp_26.cpp
    Time Compile: 9629 milliseconds Bulk_Assembly-CSharp_49.cpp
    Time Compile: 9063 milliseconds Bulk_Assembly-CSharp_144.cpp
Total compilation time: 98643 milliseconds.
il2cpp.exe didn't catch exception: Unity.IL2CPP.Building.BuilderFailedException: /Users/{UserName}/Library/Android/sdk/ndk/android-ndk-r16b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ @"/var/folders/wp/pskqw4ns3cn27r6k10158xkc0000gn/T/tmpWedXj6.tmp" -o "/Users/{UserName}/.jenkins/workspace/ANDROID_BUILD/Library/il2cpp_android_armeabi-v7a/il2cpp_cache/linkresult_6F0877EF66902EFEC137A171BB702A20/libil2cpp.so" -shared -Wl,-soname,libil2cpp.so -Wl,--no-undefined -Wl,-z,noexecstack -Wl,--gc-sections -Wl,--build-id --sysroot "/Users/{UserName}/Library/Android/sdk/ndk/android-ndk-r16b/platforms/android-16/arch-arm" -gcc-toolchain "/Users/{UserName}/Library/Android/sdk/ndk/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64" -target armv7-none-linux-androideabi -Wl,--wrap,sigaction -L "/Users/{UserName}/Library/Android/sdk/ndk/android-ndk-r16b/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a" -lgnustl_static -llog -rdynamic -fuse-ld=gold
 
clang++: error: unable to execute command: Killed: 9
clang++: error: linker command failed due to signal (use -v to see invocation)
 
 
   at Unity.IL2CPP.Building.CppProgramBuilder.PostprocessObjectFiles(HashSet`1 objectFiles, CppToolChainContext toolChainContext)
   at Unity.IL2CPP.Building.CppProgramBuilder.Build(IBuildStatistics& statistics)
   at il2cpp.Program.DoRun(String[] args)
   at il2cpp.Program.Run(String[] args)
   at il2cpp.Program.Main(String[] args)
stderr:
 
Unhandled Exception: Unity.IL2CPP.Building.BuilderFailedException: /Users/{UserName}/Library/Android/sdk/ndk/android-ndk-r16b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ @"/var/folders/wp/pskqw4ns3cn27r6k10158xkc0000gn/T/tmpWedXj6.tmp" -o "/Users/{UserName}/.jenkins/workspace/ANDROID_BUILD/Library/il2cpp_android_armeabi-v7a/il2cpp_cache/linkresult_6F0877EF66902EFEC137A171BB702A20/libil2cpp.so" -shared -Wl,-soname,libil2cpp.so -Wl,--no-undefined -Wl,-z,noexecstack -Wl,--gc-sections -Wl,--build-id --sysroot "/Users/{UserName}/Library/Android/sdk/ndk/android-ndk-r16b/platforms/android-16/arch-arm" -gcc-toolchain "/Users/{UserName}/Library/Android/sdk/ndk/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64" -target armv7-none-linux-androideabi -Wl,--wrap,sigaction -L "/Users/{UserName}/Library/Android/sdk/ndk/android-ndk-r16b/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a" -lgnustl_static -llog -rdynamic -fuse-ld=gold
 
clang++: error: unable to execute command: Killed: 9
 

| 원인

macOS Catalina 업데이트 이후 보안이 강화되어 확인되지 않은 개발자의 앱을 열려는 경우, 경고 팝업이 발생.

 

| 해결방법

차단된 앱을 허용하기 위해선 시스템 환경 설정 > 보안 및 개인 정보 보호 > 일반 > 확인 없이 허용 버튼을 눌러서 차단을 해제하는 방법도 있지만

NDK에서 새로운 앱을 사용할때마다 차단을 하나하나 해줘야하는 번거로움이 있습니다.

보안엔 안좋을 수 있지만, 터미널을 통해서 다운로드한 앱을 모두 허용해주도록 합니다.

 

sudo spctl --master-disable
 

해당 명령어를 터미널에서 입력 후 적용을 위해서 재부팅 필요.

 

 

 

반응형

| 문제

빌드에서 아래와 같은 에러와 함께 어드레서블 어셋 로드에 실패하는 경우

 

E/Unity: System.MissingMethodException: Default constructor not found for type UnityEngine.ResourceManagement.AsyncOperations.ProviderOperation`1[[UnityEngine.AddressableAssets.Initialization.ResourceManagerRuntimeData, Unity.Addressables, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]

 

| 해결방법

<linker>
    <assembly fullname="Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" preserve="all">
        <type fullname="UnityEngine.ResourceManagement.ResourceProviders.LegacyResourcesProvider" preserve="all" />
        <type fullname="UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider" preserve="all" />
        <type fullname="UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider" preserve="all" />
        <type fullname="UnityEngine.ResourceManagement.ResourceProviders.InstanceProvider" preserve="all" />
        <type fullname="UnityEngine.ResourceManagement.AsyncOperations" preserve="all" />
    </assembly>
    <assembly fullname="Unity.Addressables, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" preserve="all">
        <type fullname="UnityEngine.AddressableAssets.Addressables" preserve="all" />
    </assembly>
	<assembly fullname="UnityEngine">
		<type fullname="UnityEngine.Rigidbody" preserve="all"/>
	</assembly>
</linker>

 

Assets폴더 하위에 link.xml 파일을 생성 위와같이, 어드레서블 내에 정의된 타입들을 정의

 

 

반응형

+ Recent posts