Unity3d 周分享(14期 2019.4.1 )

选自过去1~2周 自己所看到外文内容:https://twitter.com/unity3d 和各种其他博客来源吧   

 

1、 看到一个帖子: https://qiita.com/chocho/items/51b65c2601c67e5cc6d2

通过脚本克隆UI.Button时,也会复制onClick事件 ! 具体说是:

1).如果在脚本中完成AddListener()。(这个将不是永久性的)

onClick事件未复制。不能接管

2).从Inspector 面板上进行事件绑定时。(这个将是永久的)

onClick事件也被复制。被接管

想要拷贝之后删除原来的监听 就要关闭:

int persistentEventCount = btn.onClick.GetPersistentEventCount();
for (int i = 0; i < persistentEventCount; i++)
    btn.onClick.SetPersistentListenerState(i, UnityEventCallState.Off);

 

https://docs.unity3d.com/2018.3/Documentation/ScriptReference/UI.Button.ButtonClickedEvent.html

https://docs.unity3d.com/jp/current/ScriptReference/Events.UnityEventCallState.html

https://docs.unity3d.com/jp/current/ScriptReference/Events.UnityEventBase.GetPersistentEventCount.html

 

 

2、 在UI.Image上绘画 一条线。

https://qiita.com/chocho/items/d5125ed051b22e95709c

Unity3d 周分享(14期 2019.4.1 )_第1张图片

 

 

 

3、 获取[Unity] AssetDatabase中的所有资产类型名称

public Type[] GetAllAssetClassTypeInfo()
{
    return AssetDatabase.FindAssets("t:object").Select(guid => new
    {
        var assetPath = AssetDatabase.GUIDToAssetPath(guid);
        return AssetDatabase.GetMainAssetTypeAtPath(assetPath);
    }).Distinct();       // 重复的类型信息将不会在Distinct中重复
}

 

 

 

4、 当Unity的Log 中发生错误类型时, 打开一个窗口:

public class ErrorReceivedWindow : EditorWindow
{
   [InitializeOnLoadMethod]
   private static void _onInit()
   {
        Application.logMessageReceived += ApplicationOnLogMessageReceived;
   }
   private static void ApplicationOnLogMessageReceived(string condition, string stacktrace, LogType type)
   {
       if (type == LogType.Exception)
       {
           GetWindow();
       }
   }
}

 

 

 

5、 原来 Inspector上显示的 Rotation角度并不一定就是 。 transform.localEulerAngles

我之前以为就是一个值。

Unity3d 周分享(14期 2019.4.1 )_第2张图片

 

 

Debug.LogError(" " + transform.localEulerAngles);

Debug.LogError(" " + transform.localRotation.eulerAngles);

UnityEditor.SerializedObject serializedObject = new UnityEditor.SerializedObject(transform);

UnityEditor.SerializedProperty serializedEulerHint = serializedObject.FindProperty("m_LocalEulerAnglesHint");

Debug.LogError(serializedEulerHint.vector3Value);

Unity3d 周分享(14期 2019.4.1 )_第3张图片

参考帖子: https://forum.unity.com/threads/how-to-get-euler-rotation-angles-exactly-as-displayed-in-the-transform-inspector-solved.425244/

 

 

 

 

 

 

 

6、 如果查看 Unity开源出来的 UnityCsReference 会发现一个 不知道的隐藏功能。我们来介绍一个例子。

https://connect.unity.com/p/unitycsreferencededuo-kunozhi-shi-woqu-riru-reyou

AboutWindow类有一个名为 ListenForSecretCodes的方法。当我阅读代码时...在窗口中输入Internal似乎切换到InternalMode 。 如果退出这个模式 就在输入一下 Internal

然后就会出现一些菜单:

Unity3d 周分享(14期 2019.4.1 )_第4张图片

 

 

 

7、【Unity】示例编辑器扩展,以获取Unity项目中包含的所有场景文件的路径

        var list = AssetDatabase
            .FindAssets( "t:scene" )
            .Select( AssetDatabase.GUIDToAssetPath )
        ;
// 或者  
        var list = AssetDatabase
            .GetAllAssetPaths()
            .Where( c => c.EndsWith( ".unity" ) )
        ;

 

 

[Unity]“EditorSceneManager.GetSceneManagerSetup”,可以获取 层次结构中存在的所有场景的信息

Unity3d 周分享(14期 2019.4.1 )_第5张图片

    [MenuItem( "Tools/Hoge" )]
    private static void Hoge()
    {
        var list = EditorSceneManager.GetSceneManagerSetup();

        foreach ( var n in list )
        {
            var sb = new StringBuilder();
            sb.AppendLine( $"path: {n.path}" );
            sb.AppendLine( $"isLoaded: {n.isLoaded}" );
            sb.AppendLine( $"isActive: {n.isActive}" );
            Debug.Log( sb.ToString() );
        }
    }

Unity3d 周分享(14期 2019.4.1 )_第6张图片

 

 

 

8、 【Unity】如何将附加到游戏对象的所有MonoBehaviour的信息输出为JSON

using System.Linq;
using UnityEngine;
public class Example : MonoBehaviour
{
    private void Start()
    {
        var list = GetComponents()
            .Select( c => JsonUtility.ToJson( c, true ) )
        ;

        var str = string.Join( "\n", list );

        Debug.Log( str );
    }
}

 

 

9、 [Unity]扩展方法,当从脚本中切换uGUI的isOn时,不希望触发onValueChanged

using UnityEngine.UI;
public static class ToggleExt
{
    public static void SetIsOnWithoutCallback( this Toggle self, bool isOn )
    {
        var onValueChanged = self.onValueChanged;
        self.onValueChanged = new Toggle.ToggleEvent();
        self.isOn = isOn;
        self.onValueChanged = onValueChanged;
    }
}
            怎么用?
var toggle = GetComponent();
toggle.isOn = true;                    // onValueChanged 触发
toggle.SetIsOnWithoutCallback( true ); // onValueChanged 不触发

 

 

10、 [Unity]如何创建可在创建新项目时选择的自定义模板

http://baba-s.hatenablog.com/entry/2019/03/18/090000

Unity3d 周分享(14期 2019.4.1 )_第7张图片

 

11、 宣雨凇 分享:一个查堆内存泄漏的神器。

简介: https://forum.unity.com/threads/wip-heap-explorer-memory-profiler-debugger-and-analyzer-for-unity.527949/

下载: https://bitbucket.org/pschraut/unityheapexplorer/src

Unity3d 周分享(14期 2019.4.1 )_第8张图片

Unity3d 周分享(14期 2019.4.1 )_第9张图片

 

 

好文值得收藏!

GPU大百科全书前传 看图形与装修的关系 O网页链接

GPU大百科全书第一章:美女 方程与几何 OGPU大百科全书第一章:美女 方程与几何

GPU大百科全书第二章 凝固生命的光栅化 O网页链接

GPU大百科全书第三章:像素处理那点事儿 O网页链接

GPU大百科全书第四章:虚与实共舞的TMU O网页链接

GPU大百科全书第五章 桌面显卡的捍卫者 O网页链接

GPU大百科全书第六章 谁也离不开的缓冲 O网页链接

GPU大百科全书最终章:33毫秒的咏叹调 O网页链接

Unity3d 周分享(14期 2019.4.1 )_第10张图片

 

 

还在 <<1 符号 | 符号 来添加多个layer层吗? ​​​​

Unity3d 周分享(14期 2019.4.1 )_第11张图片

 

 

发现一个深坑,unity的RichText标签会生成大量废顶点。我就说怎么玩家名字一长就无法合批了

Unity3d 周分享(14期 2019.4.1 )_第12张图片

​​​​Unity3d 周分享(14期 2019.4.1 )_第13张图片

最近发现一个问题, 虽然关闭了日志输出但是字符串拼接部分依然会产生大量堆内存。最好的办法还是用条件编译Conditional 将无用的函数整体剥离出去。 ​​​​

Unity3d 周分享(14期 2019.4.1 )_第14张图片

 

不影响原有布局拓展RectTransform。 ​​​​

Unity3d 周分享(14期 2019.4.1 )_第15张图片Unity3d 周分享(14期 2019.4.1 )_第16张图片

Unity3d 周分享(14期 2019.4.1 )_第17张图片

 

 

 

在 LINE 的官方工程师博客上发了一篇关于通过 wrap native library 做 Unity 插件的文章,包括一些挑战和集成方面的内容..以后还是要多锻炼用英文写文章- -…

https://engineering.linecorp.com/en/blog/wrapping-a-native-sdk-for-unity/

Unity3d 周分享(14期 2019.4.1 )_第18张图片

 

 

12、 Unity3d UI Automation with Selenium

https://medium.com/@autoplay.automation/unity3d-ui-automation-with-selenium-87030ab7b62a

一个开源的自动化测试: GitHub: https://github.com/AutoplayAutomation

Unity3d 周分享(14期 2019.4.1 )_第19张图片

 

 

 

 

13、 又是一个在Inspector上弄游戏的人: 如何在Unity编辑器中制作太空侵略者

https://github.com/bzgeb/AlienInvadersEditorDemo/

https://connect.unity.com/p/how-to-make-space-invaders-inside-a-unity-editor

Unity3d 周分享(14期 2019.4.1 )_第20张图片

 

 

14、 [Unity]“渲染队列Unity扩展”简介,可以显示Unity项目中存在的所有材质和着色器的渲染队列。

https://github.com/FreyaHolmer/Render-Queue

Unity3d 周分享(14期 2019.4.1 )_第21张图片

 

 

 

 

 

15、[Unity]“Unity Heap Crawler”简介,您可以在其中看到静态变量,Hierarchy对象,预制件,ScriptableObject等使用了多少堆变量的细分。

https://github.com/vasyab/UnityHeapCrawler


 

using System.Linq;
using UnityEngine;

public class Example : MonoBehaviour
{
    public class Pokemon
    {
        public string m_name = "皮卡丘";
    }

    public static Pokemon[] m_pokemons = Enumerable
        .Range( 0, 1000000 )
        .Select( c => new Pokemon() )
        .ToArray()
    ;
}

例如,使用静态变量定义类Pokemon的数组,

创建1,000,000个元素,执行“Unity Heap Crawler

”并打开输出“2-static-fields.txt”。 ,

 

 

因此,您可以看到m_pokemons数组使用30.5 MB

Example.m_pokemons [Pokemon[]] 30.5 MB ★

Keywords.keywords [LookupTable] 545.5 KB

root [Node] 545.4 KB

UIElementsUtility.s_UIElementsCache [Dictionary`2] 380.1 KB

entries [Entry[]] 379.2 KB

...

EditorGUIUtility.s_GUIContents [Hashtable] 326.6 KB

buckets [bucket[]] 326.5 KB

Keywords.keywords [LookupTable] 285.5 KB

root [Node] 285.5 KB

 

...

 

 

16、 【Unity】编辑器扩展示例

[Unity] 如何从编辑器扩展中的Unity项目中获取指定类型的ScriptableObject。例如,获取名为HogeSettings的ScriptableObject

var settings = AssetDatabase
    .FindAssets( "t:HogeSettings" )
    .Select( c => AssetDatabase.GUIDToAssetPath( c ) )
    .Select( c => AssetDatabase.LoadAssetAtPath( c ) )
    .FirstOrDefault();

 

 

【Unity】示例编辑器扩展,以获取Unity项目中包含的所有材料

using System.Linq;
using UnityEditor;
using UnityEngine;
public static class Example
{
    [MenuItem( "Tools/Hoge" )]
    private static void Hoge()
    {
        var list = AssetDatabase
            .FindAssets( "t:Material" )
            .Select( AssetDatabase.GUIDToAssetPath )
            .Select( c => AssetDatabase.LoadAssetAtPath( c ) )
            .Where( c => c != null )
        ;

        foreach ( var n in list )
        {
            Debug.Log( n.name );
        }
    }
}

【Unity】示例编辑器扩展,用于获取Unity项目中包含的所有预制件

using System.Linq;
using UnityEditor;
using UnityEngine;
public static class Example
{
    [MenuItem( "Tools/Hoge" )]
    private static void Hoge()
    {
        var list = AssetDatabase
            .FindAssets( "t:Prefab" )
            .Select( AssetDatabase.GUIDToAssetPath )
            .Select( c => AssetDatabase.LoadAssetAtPath( c ) )
            .Where( c => c != null )
            .Where( c => c.name != "DataPrivacyButton" )
            .SelectMany( c => c.GetComponentsInChildren( true ) )
            .Select( c => c.gameObject )
        ;

        foreach ( var n in list )
        {
            Debug.Log( n.name );
        }
    }
}

您可以通过编写如上所述的代码来获取Unity项目中包含的所有预制件(包括子对象)。

我尝试在Unity 2018.3中 排除它,因为我有一个名为“DataPrivacyButton”的预制件,它不包含在Unity项目中

 

 

[Unity]如何在真实机器上获取已加载材料的列表

public class Example : MonoBehaviour
{
    private void Awake()
    {
        var list = Resources
            .FindObjectsOfTypeAll()
            .Where( c => ( c.hideFlags & HideFlags.NotEditable ) == 0 )
            .Where( c => ( c.hideFlags & HideFlags.HideAndDontSave ) == 0 )
            .ToArray()
        ;
        var sb = new StringBuilder();
        foreach( var n in list )
        {
            sb.AppendLine( n.name );
        }
        Debug.Log( sb.ToString() );
    }
}

您可以使用Resources.FindObjectsOfTypeAll 获取实际机器上已加载材料的列表

我还检查了hideFlags ,以确保列表中不包含Unity内部使用的材料

在Unity编辑器中,列表还包括编辑器加载的材料,因此这仅适用于真实设备。

 

 

【Unity】如何获取真实机器上加载的纹理列表

public class Example : MonoBehaviour
{
    private void Awake()
    {
        var list = Resources
            .FindObjectsOfTypeAll()
            .Where( c => ( c.hideFlags & HideFlags.NotEditable ) == 0 )
            .Where( c => ( c.hideFlags & HideFlags.HideAndDontSave ) == 0 )
            .ToArray()
        ;
    }
}

通过使用Resources.FindObjectsOfTypeAll,您可以获得实际计算机上加载的纹理列表。

我还检查了hideFlags ,以确保Unity内部使用的纹理不包含在列表中

Unity Editor还包含编辑器在列表中加载的纹理,因此这仅适用于真实设备。

 

 

 

17、 [Unity]“CustomSampler”,可以测量任意代码和GC Alloc的执行时间,并在Profiler上显示

using UnityEngine;
using UnityEngine.Profiling;
public class Example : MonoBehaviour
{
    private void Start()
    {
        var sampler = CustomSampler.Create( "Pokemon" );
        sampler.Begin();

        for ( int i = 0; i < 10000; i++ )
        {
            Debug.Log( "啊啊啊啊啊啊啊" );
        }

        sampler.End();
    }
}

Unity3d 周分享(14期 2019.4.1 )_第22张图片您可以测量任何代码或GC Alloc的执行时间并在Profiler中显示它

与Profiler.BeginSample 类似,但开销比Profiler.BeginSample少

此外,条件属性已应用,并且未包含在发布版本中

 

 

 

 

18、[Unity]“Profiler.GetRuntimeMemorySizeLong”,它可以获取指定Unity对象和资产的内存使用情况

例如,在Android上构建一个如上所示绘制3个纹理的场景

using System.Linq;
using System.Text;
using TMPro;
using UnityEngine;
using UnityEngine.Profiling;
public class Example : MonoBehaviour
{
    public TMP_Text m_text;

    private void Start()
    {
        var list = Resources
            .FindObjectsOfTypeAll()
            .Where( c => ( c.hideFlags & HideFlags.NotEditable ) == 0 )
            .Where( c => ( c.hideFlags & HideFlags.HideAndDontSave ) == 0 )
            .ToArray()
        ;
        var sb = new StringBuilder();
        foreach ( var n in list )
        {
            var memory = Profiler.GetRuntimeMemorySizeLong( n );
            var mb = ( memory >> 10 ) / 1024f;
            sb.AppendLine( $"{n.name}: {mb.ToString( "0.00" )} MB" );
        }
        var text = sb.ToString();
        m_text.text = text;
    }
}

如果你获得当前由Resources.FindObjectsOfTypeAll加载的所有纹理, 使用

Profiler.GetRuntimeMemorySizeLong 获取每个纹理的

内存使用情况并在屏幕上绘制它

Unity3d 周分享(14期 2019.4.1 )_第23张图片

如您所见,每个纹理使用0.37 MB的内存

(Emoji One和LiberationSans SDF Atlas是TextMesh Pro使用的图像)

Unity3d 周分享(14期 2019.4.1 )_第24张图片

如果在Unity Editor Inspector中查看纹理体积,可以看到

它们大致一致(0.382 MB)

Unity3d 周分享(14期 2019.4.1 )_第25张图片

Profiler还显示纹理体积大致相同

通过这种方式,您可以使用Profiler.GetRuntimeMemorySizeLong

来获取指定Unity对象或资产的内存使用情况。

 

 

 

19、 [Unity]脚本获取托管内存的使用状态(已用过的Mono内存)

using UnityEngine.Profiling;
public sealed class MonoMemoryChecker
{
    public float Used { get; private set; }
    public float Total { get; private set; }
    public string UsedText { get; private set; }
    public string TotalText { get; private set; }
    public void Update()
    {
        Used = ( Profiler.GetMonoUsedSizeLong() >> 10 ) / 1024f;
        
        // 较大的堆需要更多的GC时间,但运行频率较低
        Total = ( Profiler.GetMonoHeapSizeLong() >> 10 ) / 1024f;
        UsedText = Used.ToString( "0.0" ) + " MB";
        TotalText = Total.ToString( "0.0" ) + " MB";
    }
}
using System.Text;
using TMPro;
using UnityEngine;
public class Example : MonoBehaviour
{
    public TMP_Text m_text;
    private readonly MonoMemoryChecker m_monoMemoryChecker = 
        new MonoMemoryChecker();
    private void Update()
    {
        m_monoMemoryChecker.Update();
        var sb = new StringBuilder();
        sb.AppendLine( "Mono" );
        sb.AppendLine();
        sb.AppendLine( $"    Used: {m_monoMemoryChecker.UsedText}" );
        sb.AppendLine( $"    Total: {m_monoMemoryChecker.TotalText}" );
        var text = sb.ToString();
        m_text.text = text;
    }
}

 

Unity3d 周分享(14期 2019.4.1 )_第26张图片

  • Profiler.GetMonoUsedSizeLong

https://docs.unity3d.com/ja/current/ScriptReference/Profiling.Profiler.GetMonoUsedSizeLong.html

  • Profiler.GetMonoHeapSizeLong

https://docs.unity3d.com/ja/current/ScriptReference/Profiling.Profiler.GetMonoHeapSizeLong.html

 

[Unity]用于获取Unity分配的内存使用状态(Unity使用的内存)的脚本

using UnityEngine.Profiling;
public sealed class UnityMemoryChecker
{
    public float Used { get; private set; }
    public float Unused { get; private set; }
    public float Total { get; private set; }
    public string UsedText { get; private set; }
    public string UnusedText { get; private set; }
    public string TotalText { get; private set; }
    public void Update()
    {
        // Unity分配的内存
        Used = ( Profiler.GetTotalAllocatedMemoryLong() >> 10 ) / 1024f;

        // 保留但未分配内存
        Unused = ( Profiler.GetTotalUnusedReservedMemoryLong() >> 10 ) / 1024f;

        // Unity为当前和未来分配保留的总内存
        Total = ( Profiler.GetTotalReservedMemoryLong() >> 10 ) / 1024f;

        UsedText = Used.ToString( "0.0" ) + " MB";
        UnusedText = Unused.ToString( "0.0" ) + " MB";
        TotalText = Total.ToString( "0.0" ) + " MB";
    }
}
public class Example : MonoBehaviour
{
    public TMP_Text m_text;
    private readonly UnityMemoryChecker m_unityMemoryChecker =
        new UnityMemoryChecker();
    private void Update()
    {
        m_unityMemoryChecker.Update();
        var sb = new StringBuilder();
        sb.AppendLine( "Unity" );
        sb.AppendLine();
        sb.AppendLine( $"    Used: {m_unityMemoryChecker.UsedText}" );
        sb.AppendLine( $"    Unused: {m_unityMemoryChecker.UnusedText}" );
        sb.AppendLine( $"    Total: {m_unityMemoryChecker.TotalText}" );
        var text = sb.ToString();
        m_text.text = text;
    }
}

Unity3d 周分享(14期 2019.4.1 )_第27张图片

( 两个图片之间存在细微差别,可能是由于Profiler数据使用的内存和音频驱动程序使用的内存等的影响)

  • Profiler.GetTotalAllocatedMemoryLong

https://docs.unity3d.com/ja/current/ScriptReference/Profiling.Profiler.GetTotalAllocatedMemoryLong.html

  • Profiler.GetTotalUnusedReservedMemoryLong

https://docs.unity3d.com/ja/current/ScriptReference/Profiling.Profiler.GetTotalUnusedReservedMemoryLong.html

  • Profiler.GetTotalReservedMemoryLong

https://docs.unity3d.com/ja/current/ScriptReference/Profiling.Profiler.GetTotalReservedMemoryLong.html

 

 

 

 

20、 [Unity]“UnityEngine.Diagnostics.Utils.ForceCrash”可以故意杀死应用

using UnityEngine;
using UnityEngine.Diagnostics;
public class Example : MonoBehaviour
{
    private void Update()
    {
        if ( Input.GetKeyDown( KeyCode.Space ) )
        {
            Utils.ForceCrash( ForcedCrashCategory.AccessViolation );
        }
    }
}

您可以使用ForceEngine.Diagnostics.Utils.ForceCrash故意杀死。

强制终止的类型

项目

内容

AccessViolation

内存访问无效导致崩溃

致命错误FatalError

由于本机致命错误导致崩溃

Abort

因中止功能而崩溃

PureVirtualFunction

由于纯虚函数异常导致崩溃

我试图找出我手边的iOS / Android设备会有什么样的结果

项目

iOS版

Android的

AccessViolation

它被打死

冻结的

致命错误FatalError

输出错误日志

输出错误日志

Abort

它被打死

它被打死

PureVirtualFunction

它被打死

它被打死

除了FatalError,我们可以获得iOS的CrashReport

 

 

 

 

 

21、 [Unity]自制类,用于测量GC发生的次数

public sealed class GCWatcher
{
    //=变量
    private int m_startCount;
    //=属性
    public int Count { get; private set; }
    ///开始测量
    public void Start()
    {
        m_startCount = GC.CollectionCount( 0 );
        Count = 0;
    }
    //结束测量
    public void Stop()
    {
        Count = GC.CollectionCount( 0 ) - m_startCount;
    }
}
using UnityEngine;
public class Example : MonoBehaviour
{
    private void Awake()
    {
        var watcher = new GCWatcher();
        watcher.Start();

        for ( int i = 0; i < 10000; i++ )
        {
            Debug.Log( "【您要测量的过程】" );
        }
        watcher.Stop();
        Debug.Log( watcher.Count );
    }
}

您可以测量GC发生的次数

可以像System.Diagnostics.Stopwatch一样使用

 

 

 

 

22、 C# 4.6 和 之前的string.format 的GC Alloc 一样就是这个结论。

        m_sampler1.Begin();
        var str1 = $"{count}{max}";
        m_sampler1.End();

        m_sampler2.Begin();
        var str2 = $"{count.ToString()}{max.ToString()}";
        m_sampler2.End();

        m_sampler3.Begin();
        var str3 = string.Format( "{0}{1}", count, max );
        m_sampler3.End();

        m_sampler4.Begin();
        var str4 = string.Format( "{0}{1}", count.ToString(), max.ToString() );
        m_sampler4.End();

Unity3d 周分享(14期 2019.4.1 )_第28张图片

顺便说一下, 我同事测试lua的字符串拼接三种方式的内存消耗是一样的。 string.format , .. , string.concat .

 

 

 

23、 [Unity]封装一个类, 可以通过事件检测GC是否发生

public static class GCEvent
{
    private static int m_count;
    public static int Count => m_count;
    public static event Action mOnCollect;
    //初始化时调用
    public static void Initialize()
    {
        m_count = GC.CollectionCount( 0 );
    }
    public static void Update()
    {
        var oldCount = m_count;
        m_count = GC.CollectionCount( 0 );
        if ( m_count == oldCount ) return;
        mOnCollect?.Invoke();
    }
}

using UnityEngine;
public class Example : MonoBehaviour
{
    private void Awake()
    {
        GCEvent.Initialize();
        GCEvent.mOnCollect += OnCollect;
    }
    private void OnDestroy()
    {
        GCEvent.mOnCollect -= OnCollect;
    }
    private void OnCollect()
    {
        Debug.Log( "皮卡丘" );
    }
    private void Update()
    {
        GCEvent.Update();
    }
}

 

 

 

24、 [Unity]一种扩展方法,如果Animator Controller的任何状态的Motion为null,则返回true。

using System.Linq;
using UnityEditor.Animations;
public static class AnimatorControllerExt
{
    public static bool HasNullMotion( this AnimatorController self )
    {
        foreach ( var n in self.layers )
        {
            if ( n.stateMachine.states.Any( c => c.state.motion == null ) )
            {
                return true;
            }
        }
        return false;
    }
}
    private static void Hoge()
    {
        var path = AssetDatabase.GetAssetPath( Selection.activeObject );
        var controller = AssetDatabase.LoadAssetAtPath( path );
        Debug.Log( controller.HasNullMotion() );
    }

 

或者编写如下测试检查代码 作为工具使用:

using NUnit.Framework;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEditor.Animations;
public partial class UniCommonTestRunner
{
    /// 
    /// Animator Controller 测试状态是否为空
    /// 
    [Test]
    public void CheckAnimatorControllerState()
    {
        var list = AssetDatabase
            .FindAssets( "t:AnimatorController" )
            .Select( AssetDatabase.GUIDToAssetPath )
            .Select( c => AssetDatabase.LoadAssetAtPath( c ) )
            .Where( c => c != null )
            .Where( c => HasNullMotion( c ) )
            .Select( c => AssetDatabase.GetAssetPath( c ) )  ;
        if ( !list.Any() ) return;
        var sb = new StringBuilder();
        foreach ( var n in list )
        {
            sb.AppendLine( n );
        }
        Assert.Fail( sb.ToString() );
    }
    private bool HasNullMotion( AnimatorController controller )
    {
        foreach ( var layer in controller.layers )
        {
            if ( layer.stateMachine.states.Any( c => c.state.motion == null ) )
            {
                return true;
            }
        }
        return false;
    }
}

 

 

[Unity]代码,可以测试iOS插件的目标平台是否合适

using NUnit.Framework;
using System.Linq;
using System.Text;
using UnityEditor;
public partial class UniCommonTestRunner
{
    [Test]
    public void CheckiOSPlugin()
    {
        var list = AssetDatabase
            .GetAllAssetPaths()
            .Where( c => c.Contains( "Plugins" ) )
            .Where( c => c.Contains( "iOS" ) )
            .Select( c => AssetImporter.GetAtPath( c ) )
            .OfType()
            .Where( c => c != null )
            .Where( c => c.GetCompatibleWithPlatform( BuildTarget.Android ) )
            .Select( c => AssetDatabase.GetAssetPath( c ) )
        ;
        if ( !list.Any() ) return;
        var sb = new StringBuilder();
        foreach ( var n in list )
        {
            sb.AppendLine( n );
        }
        Assert.Fail( sb.ToString() );
    }
}

您可以测试Android是否设置为“插件”文件夹中“iOS”文件夹中包含的插件的目标平台

Unity3d 周分享(14期 2019.4.1 )_第29张图片

同理, 可以检测安卓插件。

 [Test]
    public void CheckAndroidPlugin()
    {
        var list = AssetDatabase
            .GetAllAssetPaths()
            .Where( c => c.Contains( "Plugins" ) )
            .Where( c => c.Contains( "Android" ) )
            .Select( c => AssetImporter.GetAtPath( c ) )
            .OfType()
            .Where( c => c != null )
            .Where( c => c.GetCompatibleWithPlatform( BuildTarget.iOS ) )
            .Select( c => AssetDatabase.GetAssetPath( c ) )
        ;

https://pimdewitte.me/2016/11/03/optimizing-ios-builds-in-unity-a-tutorial-for-getting-under-the-100mb-over-the-air-download-limit-on-the-app-store/

https://answers.unity.com/questions/1187539/why-does-unity-54-include-the-contents-of-pluginsa.html

 

 

 

[Unity] 可以测试2D场景中是否禁用全局照明的代码

因为 2d 游戏根本不需要。

public partial class UniCommonTestRunner
{
    [Test]
    public void CheckGlobalIllumination()
    {
        var sceneList = AssetDatabase
            .FindAssets( "t:scene" )
            .Select( AssetDatabase.GUIDToAssetPath )
            .Where( c => c.Contains( "2D" ) )
            .OrderBy( c => c )  ;
        var result = new List();
        foreach ( var n in sceneList )
        {
            var scene = EditorSceneManager.OpenScene( n );
            var isValid = !Lightmapping.realtimeGI && !Lightmapping.bakedGI;
            if ( isValid ) continue;
            result.Add( n );
        }
        if ( result.Count <= 0 ) return;
        var sb = new StringBuilder();
        foreach ( var n in result )
        {
            sb.AppendLine( n );
        }
        Assert.Fail( sb.ToString() );
    }
}

Unity3d 周分享(14期 2019.4.1 )_第30张图片

http://baba-s.hatenablog.com/entry/2019/03/06/141858

http://baba-s.hatenablog.com/entry/2019/03/06/143028

http://baba-s.hatenablog.com/entry/2019/03/06/143224

 

 

 

 


25、 [Unity]可以测试加速度传感器是否被禁用的代码

public partial class UniCommonTestRunner
{
    /// 
    /// 测试加速度计是否被禁用
    /// 
    [Test]
    public void CheckAccelerometerFrequency()
    {
        Assert.IsTrue( PlayerSettings.accelerometerFrequency == 0 );
    }
}

您可以测试在不使用加速度计的项目中是否禁用了加速度计

 

 

[Unity]发布了“iPhone X Safe Area Drawer”,它可以在GitHub上的Unity编辑器的Game视图中显示iPhone X安全区域。

https://github.com/ianwaldrop/iPhoneX-overlay

Unity3d 周分享(14期 2019.4.1 )_第31张图片

 

 

你可能感兴趣的:(学unity涨知识,unity3d,周分享)