블로그 이미지
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-29 00:03

Unity 2021.3.33f1

----

 

앱 최초 실행 시, 최적화를 위해 그래픽 품질 관련 자동 설정하는 기능을 찾아보다 적용하고 올려 둠.

 

참조한 글을 보니 기본적으로 유니티 SystemInfo에서 지원하는 하드웨어 스펙을 보고 판단해서 Quality Level을 메기고 있다.

해당 레벨은 Unity의 UnityEngine.QualityLevel을 참조하고 있어서 대응 하는 enum을 추가(eQualityLevel)하고,

60프레임을 사용하기 위해 해당 부분 값만 수정했다.

 

필요할 때 AutoChooseQualityLevel()을 호출하고, 반환 받은 eQualityLevel 값에 따라 원하는 설정을 적용해주면 된다.

/// <summary>
/// UnityEngine.QualityLevel에 대응하는 enum
/// </summary>
public enum eQualityLevel : int 
{
    Fastest,
    Fast,
    Simple,
    Good,       // [참고] PC(i7-10700/64GB/RTX3700)에서 Good 나옴
    Beautiful,
    Fantastic
}

private eQualityLevel AutoChooseQualityLevel()
{
    Debug.Assert(Enum.GetNames(typeof(QualityLevel)).Length == Enum.GetNames(typeof(eQualityLevel)).Length, "Please update eQualityLevel to the new quality levels.");

	var shaderLevel = SystemInfo.graphicsShaderLevel;
    var cpus = SystemInfo.processorCount;
    var vram = SystemInfo.graphicsMemorySize;
    var fillrate = 0;

    // ShaderLevel
    if (shaderLevel < 10)
    	fillrate = 1000;
    else if (shaderLevel < 20)
    	fillrate = 1300;
    else if (shaderLevel < 30)
    	fillrate = 2000;
    else
    	fillrate = 3000;
    // CPU Count
    if (6 <= cpus)
    	fillrate *= 3;
    else if (3 <= cpus)
    	fillrate *= 2;
    // VRam
    if (512 <= vram)
    	fillrate *= 2;
    else if (vram <= 128)
    	fillrate /= 2;
        
    var resx = Screen.width;
    var resy = Screen.height;
    var target_fps = 60.0f;		// 현재 게임의 타겟 프레임에 맞게 설정
    var fillneed = (resx * resy + 400f * 300f) * (target_fps / 1000000.0f);
    // Change the values in levelmult to match the relative fill rate
    // requirements for each quality level.
    var levelmult = new float[] { 5.0f, 30.0f, 80.0f, 130.0f, 200.0f, 320.0f };

    const int max_quality = (int)eQualityLevel.Fantastic;
    var level = 0;
    while (level < max_quality && fillneed * levelmult[level + 1] < fillrate)
        ++level;

    var quality = (eQualityLevel)level;
    Debug.Log(string.Format("{0}x{1} need {2} has {3} = {4} level", resx, resy, fillneed, fillrate, quality.ToString()));

    return quality;
}

 

 

P.s. 참조 글 중에 에셋도 적혀있긴한데 사서 써보진 않음.

 

 

[참조] https://stackoverflow.com/questions/20978106/automatic-quality-settings/20978462#20978462

 

Automatic quality settings

a lot of apps that I see nowadays(Android) instead of having an options screen to select the graphical level, it just automatically does it, I know this is kind of a vast question, but how can I do...

stackoverflow.com

 

[참조] https://discussions.unity.com/t/auto-detect-quality-settings/24351

 

auto detect quality settings

is there a way that unity can auto detect client GPU/CPU capability and performance to set best quality settings?

discussions.unity.com

 

[에셋] https://assetstore.unity.com/packages/tools/integration/auto-quality-hardware-assessment-tool-aqhat-74134

 

Auto Quality Hardware Assessment Tool - AQHAT | 기능 통합 | Unity Asset Store

Use the Auto Quality Hardware Assessment Tool - AQHAT from Eager Amoeba® on your next project. Find this integration tool & more on the Unity Asset Store.

assetstore.unity.com

 

반응형
Posted by blueasa
, |

[링크]

유니티 오브젝트의 null 비교 시 유의사항 1

 

유니티 오브젝트의 null 비교 시 유의사항

다음글: 유니티 오브젝트의 null 비교 시 유의사항 2

overworks.github.io

 

유니티 오브젝트의 null 비교 시 유의사항 2

 

유니티 오브젝트의 null 비교 시 유의사항 2

이전글: 유니티 오브젝트의 null 비교 시 유의사항

overworks.github.io

 

반응형
Posted by blueasa
, |

2024년 1월 16일까지(참조:https://support.google.com/admob/answer/14189727?hl=ko) Admob을 사용하려면 유저에게 GDPR 동의를 받으라고 하는 것 같다.

그래서 Google에서 관련 SDK를 내놓은게 UMP(User Messaging Platform) SDK다.

----

 

Unity GoogleMobileAds에 UMP 적용하기 위해 찾아보니 누군가가 github에 정리해 놨길래 가져와서 적당히(?) 개조해서 내 GoogleMobileAdsManager에 추가 했다.

 

[Unity-Google-UMP-Check] https://github.com/TheBossaaa/Unity-Google-UMP-Check

 

GitHub - TheBossaaa/Unity-Google-UMP-Check: Google Admob & UMP Working Script Example

Google Admob & UMP Working Script Example. Contribute to TheBossaaa/Unity-Google-UMP-Check development by creating an account on GitHub.

github.com

 

기본적으로 위 소스만 적용하고 폰에서 실행해보면 아래와 같은 에러가 나온다.

 

[Error Code] GoogleMobileAds.Ump.Api.FormError

 

해당 에러는 GoogleMobileAds Dashboard에서 GDPR 관련 설정이 안돼 있어서 나오는 에러이다.

아래 Admob UMP Docs에서는 딱히 GoogleMobileAds Dashboard 관련 설정을 설명 안해놔서 모르고 넘어갈 수 있다.

Dashboard에 설정을 하자.

애드몹 대쉬보드 - 개인 정보 보호 및 메시지 - GDPR

 

[FormError 관련 참조] https://github.com/googleads/googleads-mobile-unity/issues/2780

 

GoogleMobileAds.Ump.Api.FormError · Issue #2780 · googleads/googleads-mobile-unity

[REQUIRED] Step 1: Describe your environment Unity version: 2021.3.27f1 Google Mobile Ads Unity plugin version: 8.0.0 Platform: Android Platform OS version: Android 10 Any specific devices issue oc...

github.com

 

※ 추가적으로 Admob UMP Docs에 보면 Runtime에 GDPR 변경을 위한 버튼을 구현하는 샘플이 보인다.

    법적으로 Runtime에 GDPR 변경을 해줘야 되는거면 버튼도 별도로 추가해줘야 될 것 같다.

 

[참조] https://github.com/googleads/googleads-mobile-unity/issues/2780

 

GoogleMobileAds.Ump.Api.FormError · Issue #2780 · googleads/googleads-mobile-unity

[REQUIRED] Step 1: Describe your environment Unity version: 2021.3.27f1 Google Mobile Ads Unity plugin version: 8.0.0 Platform: Android Platform OS version: Android 10 Any specific devices issue oc...

github.com

 

[GDPR 테스트]

GDPR 테스트 관련 소스가 들어있는데, Test Device Hashed Id를 넣으라고 돼있고, 실행해보면 Logcat에 뜬다고 돼 있는데 안떠서 찾아보니 아래와 같은 내용이 있다.

(난 현재 테스트 폰이 API 30이 안돼서 Test Device Hashed Id는 안넣어도 되는 것 같다.)

 

I also struggled with the same problem. The reason NOT_REQUIRED is always returned is that the test device ID has not been registered. DebugGeography does not apply to non-test devices. In my case, even if it is a basic Android emulator, I had to register the test device ID to resolve this issue. Your emulator's device ID is displayed in logcat as follows when ConsentInformation.requestConsentInfoUpdate is called.

Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("xxxxxxxxx") to set this as a debug device.

EDIT: I did more tests. It requires a test device ID since Android API 30. Until API 29, it is not necessary to specify the test device ID. I'm not sure if this is only for my development environment.

[참조] https://stackoverflow.com/questions/68529268/ump-consentstatus-always-notrequired-when-testing

 

UMP ConsentStatus always "NotRequired" when testing

I have incorporated Google's UserMessagingPlatform into my Android project for user consent to be compliant with GDPR for AdMob. The problem is that the ConsentStatus always returns "NotRequired&

stackoverflow.com

 

[GoogleMobileAdsManager에 추가된 GDPR 관련 UMP 소스]

        using GoogleMobileAds.Ump.Api;


        #region UMP(User Messaging Platform)
        /// <summary>
        /// Privacy(GDPR) 요청
        /// </summary>
        public void Request_Privacy_GDPR()
        {
            RequestConsentInfo();
        }
        
        ///Summary
        ///Request Consent Information
        ///Warning: Ads can be preloaded by the Google Mobile Ads SDK or mediation partner SDKs
        ///upon calling MobileAds.Initialize(). If you need to obtain consent from users in the European Economic Area (EEA), set any request-specific flags, such as tagForChildDirectedTreatment or tag_for_under_age_of_consent, or otherwise take action before loading ads.
        ///Ensure you do this before initializing the Google Mobile Ads SDK.
        ///Summary

        void RequestConsentInfo()
        {
            Debug.Log("[UMP] RequestConsentInfo()");

            // Set tag for under age of consent.
            // Here false means users are not under age of consent.
            ConsentRequestParameters request = new ConsentRequestParameters
            {
                TagForUnderAgeOfConsent = false,
            };

            // Check the current consent information status.
            ConsentInformation.Update(request, OnConsentInfoUpdated);
        }

        void OnConsentInfoUpdated(FormError consentError)
        {
            if (consentError != null)
            {
                // Handle the error.
                UnityEngine.Debug.LogErrorFormat("[UMP][OnConsentInfoUpdated] {0}", consentError);
                // UMP 동의 Error 시에도 초기화 진행[blueasa / 2024-03-15]
                InitializeGoogleMobileAds();
                return;
            }

            // If the error is null, the consent information state was updated.
            // You are now ready to check if a form is available.
            ConsentForm.LoadAndShowConsentFormIfRequired((FormError formError) =>
            {
                if (formError != null)
                {
                    // Consent gathering failed.
                    UnityEngine.Debug.LogErrorFormat("[UMP][LoadAndShowConsentFormIfRequired] {0}", consentError);
                    // UMP 동의 Error 시에도 초기화 진행[blueasa / 2024-03-15]
                    InitializeGoogleMobileAds();
                    return;
                }

                // Consent has been gathered.
                if (ConsentInformation.CanRequestAds())
                {
                    // UMP 동의 이후 GoogleMobileAds 초기화[blueasa / 2023-12-14]
                    // GDPR 동의 했거나, EU가 아니어도 호출되기 때문에 여기서 초기화 함.
                    InitGoogleMobileAds();
                }
            });
        }

        /// <summary>
        /// Privacy(GDPR) Button 활성화 여부 체크
        /// (설정 창에 버튼 추가함)
        /// </summary>
        public bool CheckActivePrivacyButton()
        {
            bool bActive = false;

            // EU인지 여부에 따라 버튼 활성화 체크 필요

            #region ConsentInformation.PrivacyOptionsRequirementStatus
            bActive = IsPrivacyOptionsRequirement();
            #endregion

            #region ConsentInformation.ConsentStatus
            //bActive = IsConsentForGDPR();
            #endregion

            return bActive;
        }

        public bool IsPrivacyOptionsRequirement()
        {
            // [PrivacyOptionsRequirementStatus enum 참조]
            // https://developers.google.com/admob/unity/reference/namespace/google-mobile-ads/ump/api#namespace_google_mobile_ads_1_1_ump_1_1_api_1a60f41ef4f7e14d5ae1fb5f23b7e0244b
            //public enum PrivacyOptionsRequirementStatus
            //{
            //    Unknown,
            //    NotRequired,
            //    Required
            //}

            bool bIsPrivacyOptionsRequirement = false;

            // 개인정보 보호 옵션을 표시해야 하는지 여부
            switch (ConsentInformation.PrivacyOptionsRequirementStatus)
            {
                // 개인정보 보호 옵션이 표시되어야 함.
                case PrivacyOptionsRequirementStatus.Required:
                    bIsPrivacyOptionsRequirement = true;
                    break;

                // 개인정보 보호 옵션은 표시할 필요가 없음.
                case PrivacyOptionsRequirementStatus.NotRequired:
                    bIsPrivacyOptionsRequirement = false;
                    break;

                // 개인 정보 보호 옵션 요구 사항 상태를 알 수 없음.
                case PrivacyOptionsRequirementStatus.Unknown:
                    bIsPrivacyOptionsRequirement = false;
                    break;

                default:
                    bIsPrivacyOptionsRequirement = false;
                    break;
            }

            return bIsPrivacyOptionsRequirement;
        }

        public bool IsConsentForGDPR()
        {
            // [ConsentStatus enum 참조]
            // https://developers.google.com/admob/unity/reference/namespace/google-mobile-ads/ump/api#namespace_google_mobile_ads_1_1_ump_1_1_api_1aa83ad2ecf6f2a08c584b60cef06f5133
            //ConsentStatus
            //{
            //    Unknown = 0,      // Unknown consent status.
            //    NotRequired = 1,  // Consent not required.
            //    Required = 2,     // User consent required but not yet obtained.
            //    Obtained = 3      // User consent obtained, personalized vs non-personalized undefined.
            //}

            bool bIsConsentForGDPR = false;

            switch (ConsentInformation.ConsentStatus)
            {
                case ConsentStatus.Unknown:     // 동의 상태를 알 수 없습니다.
                case ConsentStatus.Required:    // 사용자 동의가 필요하지만 아직 획득되지 않았습니다.
                case ConsentStatus.NotRequired: // 동의가 필요하지 않습니다.
                    bIsConsentForGDPR = false;
                    break;

                case ConsentStatus.Obtained:    // 사용자 동의 획득, 개인화 및 비개인화 정의되지 않음.
                    bIsConsentForGDPR = true;
                    break;
            }

            return bIsConsentForGDPR;
        }

        /// <summary>
        /// Privacy(GDPR) Button 클릭해서 GDPR 동의 창 띄우기
        /// </summary>
        public void OnClickPrivacyButton()
        {
            UpdatePrivacyButton();
        }

        void PrivacyButton()
        {
            //// Enable the privacy settings button.
            //if (_privacyButton != null)
            //{
            //    _privacyButton.onClick.AddListener(UpdatePrivacyButton);
            //    // Disable the privacy settings button by default.
            //    _privacyButton.interactable = false;
            //}
        }

        private void UpdatePrivacyButton()
        {
            // Logic for updating privacy options
            ShowPrivacyOptionsForm(); // You might want to call your method to show the privacy options form here
            Debug.LogFormat("[UMP][UpdatePrivacyButton] Privacy button clicked!");
        }

        /// <summary>
        /// Shows the privacy options form to the user.
        /// </summary>
        public void ShowPrivacyOptionsForm()
        {
            Debug.Log("[UMP] Showing privacy options form.");

            // PrivacyOptionsForm 무조건 팝업으로 변경[blueasa / 2023-12-14]
            ConsentForm.ShowPrivacyOptionsForm((FormError showError) =>
            //ConsentForm.LoadAndShowConsentFormIfRequired((FormError showError) =>
            {
                if (showError != null)
                {
                    Debug.LogErrorFormat("[UMP][LoadAndShowConsentFormIfRequired] Error showing privacy options form with error: {0}", showError.Message);
                }
                else
                {
                    // 버튼 상태 갱신(필요하면)

                    // Enable the privacy settings button.
                    //if (_privacyButton != null)
                    //{
                    //    _privacyButton.interactable =
                    //        ConsentInformation.PrivacyOptionsRequirementStatus ==
                    //        PrivacyOptionsRequirementStatus.Required;
                    //}
                }

            });
        }

        void GDPRDebugger()
        {
            Debug.Log("[UMP] GDPRDebugger()");

            ///Summary
            ///Use this for debugging
            ///

            // Define the test device ID for debugging
            string testDeviceHashedId = "0B030C0B27FA3A0A7FCF5766D3BBBA1A"; // Replace with your actual test device ID

            // Create debug settings for consent testing
            var debugSettings = new ConsentDebugSettings
            {
                TestDeviceHashedIds = new List<string>
        {
            testDeviceHashedId
        }
            };

            // Set the debug geography for testing in the EEA
            debugSettings.DebugGeography = DebugGeography.EEA;
            Debug.Log("[UMP] GDPRDebugger Set : DebugGeography.EEA");

            // Set tag for under the age of consent.
            // Here false means users are not under the age of consent.
            ConsentRequestParameters request = new ConsentRequestParameters
            {
                TagForUnderAgeOfConsent = false,
                ConsentDebugSettings = debugSettings,
            };

            // Check the current consent information status.
            ConsentInformation.Update(request, OnConsentInfoUpdated);
        }
        #endregion

 

[참조] https://docs.adxcorp.kr/appendix/ump-user-messaging-platform

 

UMP (User Messaging Platform) - ADX Library

IDFA 메시지 작성은 선택사항이지만, GDPR 메시지 사용 설정을 할 경우, IDFA 메시지 작성도 같이 작성하십시오. 애드몹 UMP의 GDPR 동의 화면이 보이는 상태에서, 프로그래밍 방식으로 수동으로 ATT (AP

docs.adxcorp.kr

[사용자 메시지 플랫폼(UMP) Doc] https://developers.google.com/admob/unity/privacy?hl=ko

 

시작하기  |  Unity  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English 시작하기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Google EU 사용자 동의 정책에 따

developers.google.com

[GoogleMobileAds.Ump.Api Doc] https://developers.google.com/admob/unity/reference/namespace/google-mobile-ads/ump/api

 

GoogleMobileAds.Ump.Api Namespace  |  Unity  |  Google for Developers

Stay organized with collections Save and categorize content based on your preferences. GoogleMobileAds.Ump.Api Summary Enumerations ConsentStatus ConsentStatus Consent status values. Properties NotRequired Consent not required. Obtained User consent obtain

developers.google.com

[GitHub/Sample] https://github.com/googleads/googleads-mobile-unity

 

GitHub - googleads/googleads-mobile-unity: Official Unity Plugin for the Google Mobile Ads SDK

Official Unity Plugin for the Google Mobile Ads SDK - googleads/googleads-mobile-unity

github.com

 

 

반응형
Posted by blueasa
, |

Unity 2021.3.37f1

Obfuscator 3.12.0

----

 

[추가] 2024-04-15

Android - Google Play Asset Delivery로 Build 시,

nameTranslation.txt 파일을 상대경로(파일명만)로 지정했을 때 제대로 생성하지 못하는 문제가 있어서 우회하도록 수정함.

BuildReport에서 주는 빌드파일 경로를 쓰지 않고, string.Format(@"{0}/..", Application.dataPath) 로 프로젝트 패스를 쓰도록 변경함.

해당 방식을 쓰기위해 Android 일 때만, Obfuscator의 OptionsManager.cs의 LoadAssetAtPath 함수를 아래와 같이 일부 수정했다.

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

namespace Beebyte.Obfuscator
{
    public class OptionsManager
    {
        ....
       
        private static Options LoadAssetAtPath(string path)
        {
            // [Android] nameTranslationFile Path 변경
            if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
            {
                // Custom
                Options o = AssetDatabase.LoadAssetAtPath<Options>(path);
                if (o == null)
                {
                    return null;
                }

                // 옵션값 덮어쓰지 않도록 Clone해서 사용.
                var clone_o = Object.Instantiate(o);

                /// 현재 프로젝트 절대 경로(Application.dataPath/../)로 수정 반환
                // 파일명(Default:nameTranslation.txt)만 추출해서 저장
                string strnameTranslation = System.IO.Path.GetFileName(clone_o.nameTranslationFile);
                // 현재 프로젝트 Path 적용. 절대경로값 지정
                clone_o.nameTranslationFile = string.Format(@"{0}/../{1}", Application.dataPath, strnameTranslation);
                return clone_o;
            }
            // [iOS] 기존 방식
            else
            {
                // Original
                return AssetDatabase.LoadAssetAtPath<Options>(path);
            }
        }
        
        ....
    }
}

 

 

----

Obfuscator에 난독화 기능을 쓸 때,

난독화 전/후 Naming 매칭 리스트를 뽑아주는 옵션이 있다.(아래 스샷 참조)

ObfuscatorOptions.asset

 

 

체크하면 기본 파일명이 nameTranslation.txt인데 빌드 할 때마다 덮어버리니 관리가 안돼서 빌드마다 별도로 만들어질 수 있도록 PostProcess로 파일명을 Rename 하도록 처리했다.

 

아래 소스를 프로젝트에 추가하면,

[Android] namteTranslation.txt 파일을 빌드 파일명에 매칭해서 자동으로 변경해준다.

ex) 빌드 파일명 : abc_v1.0.0.apk

      변경되는 파일명 : abc_v1.0.0.apk_ namteTranslation.txt

 

[iOS] iOS는 빌드 시점에 파일명이 지정되는게 아니라서 별도의 조합으로 진행되도록 했다.

          ex) abc_live_1.0.0(100)_20231206_183400_iOS_nameTranslation.txt

 

 

using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEditor.Callbacks;
using System;

/// <summary>
/// [PostProcess] Obfuscator : Rename nameTranslation.txt -> {BuildFileName.ext}_nameTranslation.txt
/// </summary>
public sealed class PostProcessBuild_Obfuscator_NameTransition_Renamer : IPreprocessBuildWithReport, IPostprocessBuildWithReport
{
    public int callbackOrder => 0;

    private static readonly string m_strNameTranslation_Src = "nameTranslation";    // Obfuscator 기본값
    private static readonly string m_strExt_txt = "txt";


    public void OnPreprocessBuild(BuildReport report)
    {
        // 1. ./BUILD_PATH/nameTranslation.txt 파일 유무 체크
        // 2. 있으면 - Delete(Clear)

        string strBuildPath = string.Empty;
        string strNameTranslation_txt_Src = string.Empty;

        switch (report.summary.platform)
        {
            case BuildTarget.Android:
                {
                    // 현재 프로젝트 Path 적용(절대경로 지정)
                    // [Obfuscator-OptionsManager.cs:line 140] 연관됨
                    strBuildPath = string.Format(@"{0}/..", Application.dataPath);
                    strNameTranslation_txt_Src = string.Format(@"{0}/{1}.{2}", strBuildPath, m_strNameTranslation_Src, m_strExt_txt);
                }
                break;

            case BuildTarget.iOS:
                {
                    strBuildPath = Path.GetDirectoryName(report.summary.outputPath);
                    strNameTranslation_txt_Src = string.Format(@"{0}/{1}.{2}", strBuildPath, m_strNameTranslation_Src, m_strExt_txt);
                }
                break;
        }

        if (true == File.Exists(strNameTranslation_txt_Src))
        {
            File.Delete(strNameTranslation_txt_Src);
            Debug.LogWarningFormat("[Delete] {0}", strNameTranslation_txt_Src);
        }
    }

    public void OnPostprocessBuild(BuildReport report)
    {
        Debug.LogWarningFormat("[OnPostprocessBuild][platform] {0} [pathToBuildProject] {1}", report.summary.platform, report.summary.outputPath);

        string strBuildPath = string.Empty;                 // 빌드 패스
        string strBuildFileName_with_Ext = string.Empty;    // 파일네임.확장자
        string strExt = string.Empty;                       // 확장자(.apk/.aab/.ipa)
        string strNameTranslation_txt_Src = string.Empty;   // [Src] nameTranslation.txt
        string strNameTranslation_txt_Dest = string.Empty;  // [Dest] nameTranslation.txt

        string strLunarConsole = "";
        if (true == LunarConsolePluginInternal.LunarConsoleConfig.consoleEnabled)
        {
            strLunarConsole = string.Format("_LunarConsole");
        }

        switch (report.summary.platform)
        {
            case BuildTarget.Android:
                {
                    // 현재 프로젝트 Path 적용(절대경로 지정)
                    // [Obfuscator-OptionsManager.cs:line 140] 연관됨
                    strBuildPath = string.Format(@"{0}/..", Application.dataPath);
                    //strBuildPath = Path.GetDirectoryName(report.summary.outputPath);
                    strBuildFileName_with_Ext = Path.GetFileName(report.summary.outputPath);
                    strExt = Path.GetExtension(report.summary.outputPath);  // .apk / .aab
                    strNameTranslation_txt_Src = string.Format(@"{0}/{1}.{2}", strBuildPath, m_strNameTranslation_Src, m_strExt_txt);

                    string strProductName = string.Format("{0}", Application.productName);
                    string strServer = string.Format("{0}", ClientSettings.ServerType.ToString());
                    string strVersion = string.Format("{0}", Application.version);
                    string strBundleVersionCode = string.Format("{0}", PlayerSettings.Android.bundleVersionCode);
                    string strDateTime = string.Format("{0}", DateTime.Now.ToString("yyyyMMdd_HHmmss", System.Globalization.CultureInfo.InvariantCulture));
                    string strPlatform = string.Format("{0}", report.summary.platform);

                    strNameTranslation_txt_Dest = string.Format(@"{0}/{1}_{2}_{3}({4})_{5}{6}_{7}{8}_{9}.{10}",
                                                                    strBuildPath,               // {0}
                                                                    strProductName,             // {1}
                                                                    strServer,                  // {2}
                                                                    strVersion,                 // {3}
                                                                    strBundleVersionCode,       // {4}
                                                                    strDateTime,                // {5}
                                                                    strExt,                     // {6}
                                                                    strPlatform,                // {7}
                                                                    strLunarConsole,            // {8}
                                                                    m_strNameTranslation_Src,   // {9}
                                                                    m_strExt_txt                // {10}
                                                                    );

                    #region OutputPath 기준 방식(Google Asset Delivery 대응 안됨)
                    //strBuildPath = Path.GetDirectoryName(report.summary.outputPath);
                    //strBuildFileName_with_Ext = Path.GetFileName(report.summary.outputPath);
                    //strNameTranslation_txt_Src = string.Format(@"{0}/{1}.{2}", strBuildPath, m_strNameTranslation_Src, m_strExt_txt);
                    //// Build File과 매칭하기 쉽도록
                    //// BuildFileName.ext_nameTranslation.txt 형태로 Dest 생성
                    //strNameTranslation_txt_Dest = string.Format("{0}/{1}_{2}.{3}", strBuildPath, strBuildFileName_with_Ext, m_strNameTranslation_Src, m_strExt_txt);
                    #endregion
                }
                break;

            case BuildTarget.iOS:
                {
                    /// iOS outputPath는 폴더만 있음(파일명.확장자는 없음.)
                    /// ex) [report.summary.outputPath] /User/{Users}/project_folder/build_folder

                    // ex) [Path.GetDirectoryName] /User/{Users}/project_folder
                    strBuildPath = Path.GetDirectoryName(report.summary.outputPath);
                    // ex) [Path.GetFileName] build_folder
                    strBuildFileName_with_Ext = Path.GetFileName(report.summary.outputPath);
                    //strExt = Path.GetExtension(report.summary.outputPath);  // .ipa   // empty
                    strExt = string.Format(".ipa"); // 직접 추가
                    strNameTranslation_txt_Src = string.Format(@"{0}/{1}.{2}", strBuildPath, m_strNameTranslation_Src, m_strExt_txt);

                    string strProductName = string.Format("{0}", Application.productName);
                    string strServer = string.Format("{0}", Anne.ClientSettings.ServerType.ToString());
                    string strVersion = string.Format("{0}", Application.version);
                    string strBuildNumber = string.Format("{0}", PlayerSettings.iOS.buildNumber);
                    string strDateTime = string.Format("{0}", DateTime.Now.ToString("yyyyMMdd_HHmmss", System.Globalization.CultureInfo.InvariantCulture));
                    string strPlatform = string.Format("{0}", report.summary.platform);

                    strNameTranslation_txt_Dest = string.Format(@"{0}/{1}_{2}_{3}({4})_{5}{6}_{7}{8}_{9}.{10}",
                                                                    strBuildPath,               // {0}
                                                                    strProductName,             // {1}
                                                                    strServer,                  // {2}
                                                                    strVersion,                 // {3}
                                                                    strBuildNumber,             // {4}
                                                                    strDateTime,                // {5}
                                                                    strExt,                     // {6}
                                                                    strPlatform,                // {7}
                                                                    strLunarConsole,            // {8}
                                                                    m_strNameTranslation_Src,   // {9}
                                                                    m_strExt_txt                // {10}
                                                                    );
                }
                break;
        }

        Debug.LogWarningFormat("[strNameTranslation_txt_Src] {0} [strNameTranslation_txt_Dest] {1}", strNameTranslation_txt_Src, strNameTranslation_txt_Dest);
        
        // 1. ./PROJECT_PATH/nameTranslation.txt 파일 유무 체크
        // 2. 있으면 - Rename : 해당 버전명 날짜/시간 파일명에 포함
        if (true == File.Exists(strNameTranslation_txt_Src))
        {
            File.Move(strNameTranslation_txt_Src, strNameTranslation_txt_Dest);
            Debug.LogWarningFormat("[Rename] {0} -> {1}", strNameTranslation_txt_Src, strNameTranslation_txt_Dest);
        }
        else
        {
            Debug.LogWarningFormat("[File Not Exists] {0}", strNameTranslation_txt_Src);
        }
    }
}
반응형
Posted by blueasa
, |

[추가2] 2024-04-08

Unity 2021.3.37f1

GoogleMobileAds 8.6.0

Firebase SDK 11.8.0

----

Unity 2021.3.37f1에서 기본 Gradle 버전이 Gradle 4.2.2로 버전업 되면서 별도로 Gradle 4.2.0을 다운받고 설정할 필요가 없어졌다.

그리고, Gradle 4.2.0에서 체크해야됐던 GoogleMobileAdsSettings 옵션을 더이상 체크 안해도 되게 됐다.

정리하자.

 

----

Unity 2021.3.32f1

GoogleMobileAds 8.6.0

Firebase SDK 11.6.0

----

 

[추가]

gradle을 Unity 2021 기본인 gradle plugin 4.0.1에서 gradle plugin 4.2.0으로 변경하고 나서

Unity Editor에서는 빌드가 잘되는데 이상하게 jenkins Android 에서만 빌드가 실패해서 삽질하면서 알아보니

jenkins를 Mac에 셋팅해뒀는데 gradle cache 폴더가 뭔가 꼬인 것 같다.

아래와 같은 Warning Log가 엄청나게 뜬다.

 

[Warning Log]

WARNING:/Users/{UserAccount}/.gradle/caches/transforms-2/files-2.1/ea30c3c071cd48c926311878c13eb08b/jetified-unity-classes.jar: D8: Expected stack map table for method with non-linear control flow.

 

그래서 아래 위치의 gradle cache 하위 있는 것들을 모두 삭제하고 새로 빌드를 실행해서 잘 돌아가는 것을 확인했다.

 

[Mac gradle cache 위치] /Users/{UserAccount}/.gradle/caches/

 

----

 

이번에 메모리 누수 이슈로 Unity 메이저 버전을 2022 -> 2021로 내렸는데,

GoogleMobileAds가 업데이트 돼서 기존 사용하던 8.4.1 -> 8.6.0으로 올리면서 Unity 버전관련해서 체크해야 될 사항들 간단히 정리해 둠.

 

일단 Unity 2022.3 이상은 상관 없었는데 2021.3으로 내려오면서 gradle 버전이 낮아지면서 처리할 문제가 생겼다.

아래 2가지 이슈를 수정하고 빌드 하자.

 

1) Unity 2021.3.32f1이 기본이 com.android.tools.build:gradle:4.0.1로 돼 있는데,

    GoogleMobileAds 8.6.0이 com.android.tools.build:gradle:4.2.0을 요구하고 있다.

    아래 링크의 내용을 참조해서 Base Gradle Templete에서 gradle plugin을 4.2.0으로 수정하고, gradle 6.7.1을 설치 및 연동하자.

 

   [설명 링크] https://developers.google.com/admob/unity/gradle?hl=ko#unity-integrated-builds

 

Android용 Gradle 업그레이드  |  Unity  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English Android용 Gradle 업그레이드 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. The Google Mobile Ad

developers.google.com

 

['gradle 6.7.1 bin' Download Link] https://gradle.org/next-steps/?version=6.7.1&format=bin

 

Gradle | Thank you for downloading Gradle!

Next steps after downloading Gradle

gradle.org

 

2) 그리고, GoogleMobileAds 셋팅 파일에 Unity 2022.1 이하 프로젝트 관련 옵션이 2개 추가 되어 있는데,

     Unity 2021.3을 쓰고 있어서 둘 다 체크해야 된다.

    아래 내용과 링크를 참조하자.

GoogleMobileAds 8.6.0 Settings

 

[참조 링크] https://github.com/googleads/googleads-mobile-unity/issues/2930

 

play-services-ads:22.4.0 issue · Issue #2930 · googleads/googleads-mobile-unity

[REQUIRED] Step 1: Describe your environment Unity version: 2021.3.30f1 Google Mobile Ads Unity plugin version: 8.5.2 Platform: Android - Unity Editor Mediation ad networks used, and their versions...

github.com

 

[잡담]

Google도 이제 대충 하는건지..

이번에 새로 추가된 class 중에 Utils가 있는데 다른데서도 많이 쓰는 이름인데 namespace를 지정 안해놔서 클래스 명 충돌이 나서 namespace GoogleMobileAds로 처리 했다.

 

 

----

[참고]

Firebase도 당장은 아니지만(현재 Firebase SDK 11.6.0 기준 Firebase Android BoM 32.3.1) 추후 버전에서 곧 Android Gradle Plugin 버전을 4.2.0으로 강제하게 될 것 같다.

Firebase Android BoM Release Notes

[링크] https://firebase.google.com/support/release-notes/android

 

Firebase Android SDK Release Notes

 

firebase.google.com

반응형
Posted by blueasa
, |

[2023-11-17 기준 사용 버전] Unity 2022.3.13f1

----

 

2022.3 LTS 버전이 나오고 2022.3.5까지 나온걸 보고 업데이트를 하고 사용했었는데,

2023-11-17 기준 최신 버전인 2023-11-17까지 빌드하고 테스트 해본 결과 아래와 같은 크리티컬한 크래쉬 이슈가 여전히 있었다.

안그래도 Unity 2021보다 Unity 2022가 무거워서 마음에 안들었는데 너무 크리티컬한 버그를 아직도 안고치고 있으니 갑갑함.

덕분에 스토어 올려진 앱이 크래시 증가로 경고까지 뜨고 있다.

 

  [크래쉬 이슈]

- 엔진 자체 메모리 누수로 인한 메모리 부족으로 느려지거나 크래쉬 남

- 파티클 관련 무한 재귀 관련 버그가 있는 건지 이펙트(파티클)가 터지면 간헐적으로 앱이 멈춤(이건 예상)

 

2022.3의 .3은 LTS라고 불리는 버전인데 2022.3 모든 버전에서 메모리 누수가 여전히 나고 있고,

몇일 전에 올라온 2022.3.13f1도 위 2개의 크래쉬 이슈가 이전보다는 줄었지만 여전히 나고 있다.

(유니티는 LTS의 정의를 잘못 생각하고 있는건 아닌지 의심스럽다.)

 

더이상 2022 버전 버그 수정을 기대하면서 쓰는건 뻘짓이라 판단하고 2022 버전을 완전히 버리기로 생각함.

현재 2023 버전은 LTS가 나오려면 멀었기 때문에 2021.3으로 엔진 메이저 버전을 다운그레이드 하고 앱 업데이트를 완료 했다.

 

테스트 상에서는 2022에 있던 크래쉬 이슈가 2021에선 나오지 않는걸 확인했다.

2021.3 버전을 쓰다가 2023.3으로 바로 넘어가야 될 것 같다.

 

P.s. 오늘 유니티 뉴스 보니 버전 레벨링을 개편한다면서 2023을 Unity 6으로 바꾸고 앞으로 버전으로 다시 돌아간다고 함.

       매년 업데이트 하기 힘들었나벼..그래서 출시하면 안되는 버전인 Unity 2022가 나오게 된건 아닌지 의심스럽다

 

[결론]

Unity 2022는 쓰레기니 쓰지맙시다.

Unity 2021 쓰세요~

 

[2022.3 Memory Leak 참조 링크]

https://forum.unity.com/threads/strange-memory-leak-on-2022-3-x.1460224/

 

Question - Strange Memory leak on 2022.3.x

Hi all, So this is an odd one. My game when run on 2021.3, memory sits around 1.3GB on launch. However, ive had reports since making changes over the...

forum.unity.com

https://forum.unity.com/threads/rthandles-api-introduced-catastrophic-memory-leak-bug-in-2022-3-8.1486035/

 

Bug - RTHandles API introduced catastrophic memory leak bug in 2022.3.8

Hi all, It seems there was a very large memory leak bug introduced in 2022.3.8 (on my PC it went as far as filling 32GB RAM and reserving a 32GB page...

forum.unity.com

 

반응형
Posted by blueasa
, |

[링크] https://learnandcreate.tistory.com/642

 

유니티에서 패키지를 기본값으로 재설정하기(reset pacakges to defaults)

유니티에서 패키지를 기본값으로 재설정하기(reset pacakges to defaults) 패키지를 기본값으로 재설정하면 프로젝트에서 사용자가 설치한 모든 패키지들을 제거하고 기본값으로 초기화합니다. 이 작

learnandcreate.tistory.com

 

반응형
Posted by blueasa
, |

[링크] https://j2su0218.tistory.com/503

 

[유니티Unity]유니티에서 인앱 업데이트 지원 하기

유니티에서 인앱 업데이트 지원 하기 구글 콘솔에 새로운 버전을 업데이트했을 때, 인앱 업데이트 기능을 넣어주지 않으면 사용자가 직접 앱을 업데이트시켜주어야 한다. 하지만 중요한 기능의

j2su0218.tistory.com

 

반응형
Posted by blueasa
, |

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

 

[UnityNGUI] ScrollView에 Particle Clipping하기

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

cho22.tistory.com

 

 

 

 

반응형
Posted by blueasa
, |

Xcode 15

Unity 2023.3.10f1

----

 

[Xcode Build Error]

Assertion failed: (false && "compact unwind compressed function offset doesn't fit in 24 bits"), function operator(), file Layout.cpp, line 5758.

 

Xcode 15로 업데이트 후 빌드를 했더니,

유니티 빌드는 정상적으로 됐는데 Xcode로 넘어와서는 위와 같은 에러를 내면서 빌드가 안된다.

혹시나해서 Mac OS 14로 업데이트도 해봤는데 빌드 실패함.

 

OTHER_LDFLAGS에 -ld_classic을 추가하라는 내용을 보고(아래 링크 참조) 추가해서 정상적으로 빌드 되는 것을 확인 함.

 

    ...
    var pbxProject = new PBXProject();
    pbxProject.ReadFromFile(projectPath);
 
    //Unity Framework
    target = pbxProject.GetUnityFrameworkTargetGuid();
    pbxProject.AddBuildProperty(target, "OTHER_LDFLAGS", "-ld_classic");

 

 

[참조] https://forum.unity.com/threads/project-wont-build-using-xode15-release-candidate.1491761/#post-9355667

 

Bug - Project won't build using Xode15 release candidate.

Hi there, just tried to build the project using Xcode15 RC (Released yesterday). And failed with the following error: Showing Recent Issues...

forum.unity.com

[참조] https://issuetracker.unity3d.com/issues/building-projects-with-il2cpp-scripting-backend-for-apple-platforms-fails-with-xcode-15-dot-0b6-or-newer

 

Unity IssueTracker - Building projects with IL2CPP scripting backend for Apple platforms fails with Xcode 15.0b6 or newer

Xcode >= 15.0b6 contains a change to the "ProductName" in the "version.plist", which Unity will fail to parse. Because of that, U...

issuetracker.unity3d.com

 

 

반응형
Posted by blueasa
, |