Unity3d 周分享(15期 2019.4.14 )

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

 

1、 在Unity中 使用 IronPython

https://qiita.com/syutotyou/items/bd9f9a74a054934277c1

环境:

windows10

unity 2018.2.14f1

IronPython 2.7.9

https://ironpython.net/

在Unity项目中导入 在/ IronPython 2.7 / Platforms / Net35 下的

IronPython.dll

IronPython.Modules.dll

Microsoft.Dinamic.dll

Microsoft.Scripting.dll

Microsoft.Scripting.Core.dll

Microsoft.Scripting.Metadata.dll

到Unity 项目的 : Assets / Plugins 下, 然后就可以使用测试了。

Unity3d 周分享(15期 2019.4.14 )_第1张图片

 

 

2、 获取当前属于LayerMask (layer的集合)中的GameObject

    public static GameObject[] FindGameObjectsWithLayerMask(LayerMask layerMask)
    {
        GameObject[] goArray = GameObject.FindObjectsOfType();
        List goList = new List();
        foreach (GameObject go in goArray) {
            // LayerMask bit check
            if (((1 << go.layer) & layerMask.value) != 0) {
                goList.Add(go);
            }
        }
        if (goList.Count == 0) {
            return null;
        }
        return goList.ToArray();
    }

 

3、 使用隔行扫描加速绘图

曾经有一种称为交错的技术。 一种提高FPS的技术。

https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%AC%E3%83%BC%E3 %82%B9

Unity3d 周分享(15期 2019.4.14 )_第2张图片

https://qiita.com/drfters/items/3c4a06619950fa1218ae

它真的加速了吗?

以2688x1242分辨率 且应用后处理效果。

  • 没有开启交错,它是22 FPS。
  • 通过开启交错,它是46 FPS。

 

 

4、 [Unity]在实例化类和结构时GC Alloc的区别

using UnityEngine;
using UnityEngine.Profiling;

public class Example : MonoBehaviour
{
    public class Hoge { }
    public struct Fuga { }
    private void Start()
    {
        var count = 1000000;
        var samplerA = CustomSampler.Create( "AAAAAAAAAAAAAAAAAAAA" );
        samplerA.Begin();
        for ( int i = 0; i < count; i++ )
        {
            new Hoge();
        }
        samplerA.End();
        var samplerB = CustomSampler.Create( "BBBBBBBBBBBBBBBBBBBB" );
        samplerB.Begin();
        for ( int i = 0; i < count; i++ )
        {
            new Fuga();
        }
        samplerB.End();
    }
}

如果我们创建1,000,000 个Hoge类和Fuga结构的实例

Unity3d 周分享(15期 2019.4.14 )_第3张图片

 

 

 

 

 

5、 [Unity]获取游戏对象根路径的扩展方法

using UnityEngine;
public static class GameObjectExt
{
    private static string GetRootPath( this GameObject gameObject )
    {
        var path = gameObject.name;
        var parent = gameObject.transform.parent;
        while ( parent != null )
        {
            path = parent.name + "/" + path;
            parent = parent.parent;
        }
        return path;
    }
}
using UnityEngine;
public class Test : MonoBehaviour
{
    private void Awake()
    {
        Debug.Log( gameObject.GetRootPath() );
    }
}

         我要说的是其实Unity存在这个API, (我没有测试他们之间的性能, 感兴趣的可以试一试)

AnimationUtility.CalculateTransformPath

 

6、 [Unity]编辑器扩展,使Unity编辑器无法播放

using UnityEditor;
[InitializeOnLoad]
public static class Example
{
    static Example()
    {
        EditorApplication.playModeStateChanged += OnChange;
    }
    private static void OnChange( PlayModeStateChange state )
    {
        if ( state == PlayModeStateChange.ExitingEditMode )
        {
            EditorApplication.isPlaying = false;
        }
    }
}

Unity3d 周分享(15期 2019.4.14 )_第4张图片

有什么用? 可以在Play之前做一些检查, 就行如果C#有编译报错Unity就无法Play一样。 如下检查示例,

 

[Unity]一个编辑器扩展,如果在Inspector中没有设置引用的项目,则阻止编辑器播放。

您可以通过将以上脚本添加到Unity项目的“Editor”文件夹中来使用它。

using JetBrains.Annotations;
using UnityEngine;
public class Test : MonoBehaviour
{
    [NotNull] public Sprite m_sprite;  // 例如,应用NotNull属性的变量
}

using Sirenix.OdinInspector;
using UnityEngine;
public class Test : MonoBehaviour
{
    [Required] public Sprite m_sprite;  // 对于应用了Odin的Required属性的变量
}

Unity3d 周分享(15期 2019.4.14 )_第5张图片

Unity3d 周分享(15期 2019.4.14 )_第6张图片

 

 

 

7、 Unity单元测试的变更 :

《Unity 2018单元测试 》 https://www.nowsprinting.com/entry/2019/04/01/000000

, 菜单变化, Assembly Definition变化, 是否Play模式的变化。 程序集依赖变化等。

《如果您不能从Unity中的测试代码引用您自己的类,该怎么办?》: https://qiita.com/kazukomati/items/d5f5bec204ce99ee7ad5

 

 

 

 

8、 如何获取一个static event 的注册数。

https://stackoverflow.com/questions/24003458/how-to-easilly-see-number-of-event-subscriptions-while-debugging

using System;
using System.Reflection;

public static class DelegateExt
{
    public static int GetLength( string name )
    {
        return GetLength( typeof( T ), name );
    }

    public static int GetLength( this Type self, string name )
    {
        var attrs =
            BindingFlags.GetField   |
            BindingFlags.Static     |
            BindingFlags.NonPublic  |
            BindingFlags.Public
        ;
        var field = self.GetField( name, attrs );
        if ( field == null )
        {
            throw new ArgumentException( $"name is invalid parameter: {name}" );
        }
        var d = field.GetValue( null ) as Delegate;
        if ( d == null ) return 0;
        var list = d.GetInvocationList();
        if ( list == null ) return 0;
        var length = list.Length;
        return length;
    }
}

测试

using UnityEngine;

public class Example : MonoBehaviour
{
    private void Awake()
    {
        var t = typeof( Application );

        Application.focusChanged                += Application_focusChanged         ;
        Application.lowMemory                   += Application_lowMemory            ;
        Application.lowMemory                   += Application_lowMemory            ;
        Application.logMessageReceived          += Application_logMessageReceived   ;
        Application.logMessageReceived          += Application_logMessageReceived   ;
        Application.logMessageReceived          += Application_logMessageReceived   ;
        Application.logMessageReceivedThreaded  += Application_logMessageReceived   ;
        Application.logMessageReceivedThreaded  += Application_logMessageReceived   ;
        Application.logMessageReceivedThreaded  += Application_logMessageReceived   ;
        Application.logMessageReceivedThreaded  += Application_logMessageReceived   ;
        Application.quitting                    += Application_quitting             ;
        Application.quitting                    += Application_quitting             ;
        Application.quitting                    += Application_quitting             ;
        Application.quitting                    += Application_quitting             ;
        Application.quitting                    += Application_quitting             ;

        Debug.Log( t.GetLength( "focusChanged"                  ) );
        Debug.Log( t.GetLength( "lowMemory"                     ) );
        Debug.Log( t.GetLength( "s_LogCallbackHandler"          ) );
        Debug.Log( t.GetLength( "s_LogCallbackHandlerThreaded"  ) );
        Debug.Log( t.GetLength( "quitting"                      ) );
    }

    private void Application_focusChanged( bool obj ) { }
    private void Application_lowMemory() { }
    private void Application_logMessageReceived( string condition, string stackTrace, LogType type ) { }
    private void Application_quitting() { }

    private void OnDestroy()
    {
        Application.focusChanged                -= Application_focusChanged         ;
        Application.lowMemory                   -= Application_lowMemory            ;
        Application.lowMemory                   -= Application_lowMemory            ;
        Application.logMessageReceived          -= Application_logMessageReceived   ;
        Application.logMessageReceived          -= Application_logMessageReceived   ;
        Application.logMessageReceived          -= Application_logMessageReceived   ;
        Application.logMessageReceivedThreaded  -= Application_logMessageReceived   ;
        Application.logMessageReceivedThreaded  -= Application_logMessageReceived   ;
        Application.logMessageReceivedThreaded  -= Application_logMessageReceived   ;
        Application.logMessageReceivedThreaded  -= Application_logMessageReceived   ;
        Application.quitting                    -= Application_quitting             ;
        Application.quitting                    -= Application_quitting             ;
        Application.quitting                    -= Application_quitting             ;
        Application.quitting                    -= Application_quitting             ;
        Application.quitting                    -= Application_quitting             ;
    }
}

Unity3d 周分享(15期 2019.4.14 )_第7张图片

注意上方代码中: 例如,Application.logMessageReceived和Application.logMessageReceivedThreaded具有

s_LogCallbackHandler和s_LogCallbackHandlerThreaded的内部名称,因此

必须确保DelegateExt.GetLength中指定的名称正确。

using System.Reflection;
using System.Text;
using UnityEngine;

public class Example : MonoBehaviour
{
    private void Awake()
    {
        var attrs =
            BindingFlags.GetField   |
            BindingFlags.Static     |
            BindingFlags.NonPublic  |
            BindingFlags.Public
        ;

        var t       = typeof( Application );
        var fields  = t.GetFields( attrs );
        var sb      = new StringBuilder();
        foreach ( var n in fields )
        {
            sb.AppendLine( n.Name );
        }
        Debug.Log( sb.ToString() );
    }
}

内部名称使用如上所述的反射

lowMemory 
s_LogCallbackHandler 
s_LogCallbackHandlerThreaded 
OnAdvertisingIdentifierCallback 
focusChanged 
wantsToQuit 
quitting 
s_RegisterLogCallbackDeprecated

 

 

 

 

9、 垃圾回收问题:

网上有说 Unity GC.Collect() 不会立即执行垃圾回收? 有谁知道出处来源哪。Unity官方有说过么?

https://onevcat.com/2012/11/memory-in-unity3d/

http://ask.manew.com/question/36611

这里提到立即收集:

Unity3d 周分享(15期 2019.4.14 )_第8张图片

https://books.google.com.hk/books?id=80NADwAAQBAJ&pg=PA411&lpg=PA411&dq=Unity++GC.Collect()+immediately++%EF%BC%9F&source=bl&ots=6cNkzg5aCY&sig=ACfU3U0_zPtrjXSlO_uPdlZia_Ts7tjREw&hl=zh-CN&sa=X&ved=2ahUKEwizlv612b_hAhWbKqYKHT3nDjE4ChDoATABegQICBAB#v=onepage&q=Unity%20%20GC.Collect()%20immediately%20%20%EF%BC%9F&f=false

 

官方:

https://unity3d.com/de/learn/tutorials/topics/performance-optimization/optimizing-garbage-collection-unity-games?playlist=44069

Unity3d 周分享(15期 2019.4.14 )_第9张图片

https://docs.unity3d.com/Manual/UnderstandingAutomaticMemoryManagement.html?_ga=2.245838532.133659043.1554634170-1366382285.1535636176

或者这种代码. 具体什么结论 不知道~。

Unity3d 周分享(15期 2019.4.14 )_第10张图片

 

 

 

10、 [Unity] 仅描绘模型轮廓的着色器

https://qiita.com/HhotateA/items/6b08dff9babdb08e9f77

Unity3d 周分享(15期 2019.4.14 )_第11张图片

 

 

11、比较DI框架Zenject和Spring

https://qiita.com/segur/items/d0b636529483af6cb1c4

可能是因为C#和Java相似,但我认为Zenject和Spring非常相似。

Spring是一个Web后端开发,Zenject是一个游戏系统开发,使用场景完全不同,我再次认为在其中任何一个中使用的DI架构真的很棒。

 

 

 

12、 项目在优化, 发现跨语言的交互 还是特别消耗的, C# <=> Lua, C# <=> C/C++

Unity项目优化中肯定会提到缓存Transform 组件等等。

Unity3d 周分享(15期 2019.4.14 )_第12张图片

下面的博文, 作者用了很多中方式来实现缓存组件的功能。 每一种都会有性能比较。

https://habr.com/ru/post/303562/

https://github.com/KonH/UnityCache

 

 

 

13、 看到一篇博文 其中提到一下工具 : C#是一种低级语言?https://habr.com/ru/post/443804/

 

InliningAnalyzer 内联分析仪

https://marketplace.visualstudio.com/items?itemName=StephanZehetner.InliningAnalyzer

内联分析器显示方法调用是否将由 JIT 编译器内联。方法调用会在源代码中突出显示, 并显示 JIT 编译器给出的内联失败的原因。

        内联是一种编译器优化, 它将函数调用站点替换为被调用方的主体。在公共语言运行时, 这是由 JIT 编译器完成的。对于性能关键代码, 了解方法是否内联可能很重要。更多的内联通常更好, 但并不总是这样。如果调整代码, 请使用要内联的方法, 始终使用探查器或基准库来测试它是否确实对您的方案产生了影响。

 

 

其他工具: VS2019 Add-in -- 单击任何方法或类以查看.NET Core的JIT为它们生成的内容(ASM)。 https://github.com/EgorBo/Disasmo

Unity3d 周分享(15期 2019.4.14 )_第13张图片

 

 

或者是这个网站, 之前介绍过 SharpLab.io

类似的 C++ 的汇编代码查看工具: https://godbolt.org/z/l2QZLY

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

 

 

 

 

 

 

14、 一个比较新的 开源游戏引擎 , 也是基于C# 的 。

https://habr.com/ru/post/441608/

https://www.duality2d.net/

https://github.com/AdamsLair/duality/

像评论中提到的, 同样基于C#开源的Defold或Godot Engine。 旧XNA / Monogame 。 都是很好的学习资料。

 

 

15、 20个游戏教孩子编程

https://habr.com/ru/post/440376/

 

 

 

 

 

16、 今天看Unity的文档发现一个工具:

https://plugins.jetbrains.com/plugin/11701-heap-allocations-viewer/update/56415

Unity3d 周分享(15期 2019.4.14 )_第15张图片

代码中会有特殊颜色的下划线标记问题:

 

 

类似的工具还搜索到 :

https://www.youtube.com/watch?v=Tw-wgT-cXYU

https://github.com/Microsoft/RoslynClrHeapAllocationAnalyzer

https://marketplace.visualstudio.com/items?itemName=MukulSabharwal.ClrHeapAllocationAnalyzer

 

 

 

17、 用一只小型汽车为您的鼠标围绕桌面进行竞赛。

说明:

  • 启动exe并单击汽车来控制它。
  • 使用箭头键开车
  • 按空格键以单击事物。
  • 按escape键解锁光标。
  • 关闭任务栏中的应用程序或使用ALT + F4。

免责声明:由于应用程序窃取焦点,上下文菜单无法运行。 在Explorer中使用时,可能还会发生一些 奇怪的事情。

免费下载 : https://papercookies.itch.io/cursor-car

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

 

 

 

18、Debug.Log 原来支持两个参数, 第二个参数就是当点击log的时候可以 让对象高亮

Unity3d 周分享(15期 2019.4.14 )_第17张图片

 

 

 

 

 

19、 相机一旦固定视角(能看到的角度范围有限),会带来很多优化方案, 比如场景模型制作的时候直接背面剔除。 天空盒的处理等。

下面是

“这是我在短途徒步旅行中用来渲染海洋的一个小技巧! 水只是跟随玩家的飞机。 纹理和顶点动画都在世界空间中完成!”

https://twitter.com/adamgryu/status/1112783007566450691

 

 

 

20、 绝对建议在Unity的实体组件系统上查看@Icetigris的面向数据设计 ppt !

主要解释 什么是 ECS , 通过比较 面向对象和 面向数据的设计差异。 特别是对高速缓存的利用上的优缺点。

《GDC 2019 Understanding Data-Oriented Design for Entity Component Systems》

很好地可视化为什么DOD不仅有利于缓存性能,还可以大大简化您的应用程序代码!

https://docs.google.com/presentation/d/1s8XV63Dy092i1FSfUFUvfXrne2OmisrlxM5zbr4F8gQ/edit#slide=id.g52aa31112b_0_139

 

可以在CSDN资源 中搜索 下载: GDC 2019 Understanding Data-Oriented Design for Entity Component Systems

Unity3d 周分享(15期 2019.4.14 )_第18张图片

Unity3d 周分享(15期 2019.4.14 )_第19张图片

 

 

 

 

 

============================================= 性能分析 =======================

 

 

21、 让结构体更快。

结构体跟类比 速度上要快的。 结构非常适合控制内存布局和避免使用GC

。 下面两个结构体, 他们的内存占用不同, 这里测试 更新位置:player.Position += player.Velocity * time.

struct PlayerExtras
{
    public Vector3 Position;      // 3 * 4 = 12
    public Vector3 Velocity;      // 3 * 4 = 12
    public int Health;            //          4
    public int MaxHealth;         //          4
    public int NumLives;          //          4
    public int Score;             //          4
    public int TeamId;            //          4
    public int LeftHandWeaponId;  //          4
    public int RightHandWeaponId; //          4
    public int NumWins;           //          4
    public int NumLosses;         //          4
    public int MatchmakingRank;   //          4
                                  // Total:  64
}
struct PlayerBasics
{
    public Vector3 Position; // 3 * 4 = 12
    public Vector3 Velocity; // 3 * 4 = 12
    public int Health;       //          4
    public int MaxHealth;    //          4
                             // Total:  32
}

Unity3d 周分享(15期 2019.4.14 )_第20张图片

结果 Basis这种结构 速度快。

原因其实和 Unity推出的 ECS 差不多, 更好的利用CPU的高速缓存, 增加命中率。

https://jacksondunstan.com/articles/5131

 

正如这个 https://people.eecs.berkeley.edu/~rcs/research/interactive_latency.html 页面整齐地显示的那样,对于数组的一半元素使用CPU缓存意味着我们只需要等待1-4纳秒的内存而不是100纳秒来从RAM中获取它。25-100倍的加速为我们提供了巨大的速度提升,使得使用结构的“Basis”版本的速度更快。

通常,如果需要高效迭代结构,请尽量让结构很小。这样可以提高代码的性能,即使不通过更有效地使用CPU缓存来改变代码也是如此。

Unity3d 周分享(15期 2019.4.14 )_第21张图片

 

 

注意: 顺便在说一些, 《 使用对象句柄使结构更有用》

结构可以是保持垃圾收集器不受影响并更有效地使用CPU数据缓存的好方法。如果您的结构中包含任何引用类型【最常见的就是string】字段,则无法再使用sizeof(MyStruct)。这确实限制了它的实用性。 string我们存储的东西不是直接存储在结构中,而是存储可用于获取string的。可以用一个简单的int。因此,我们将在结构体外部存储托管对象,并使用它int来识别个体object。

 

https://jacksondunstan.com/articles/3860

  • 数据缓存: 从广义上讲,所有现代计算机都具有相同的配置。他们有CPU,GPU和RAM。CPU还有两个或三个级别的缓存:L1,L2和L3。这些缓存用于加速RAM访问(因为它非常慢)。确切的数字会因设备而异,但根据这个网站,这里的速度很快:

L1缓存访问:1纳秒

L2缓存访问:4纳秒

RAM访问:100纳秒

这意味着使用CPU缓存中的内存与RAM中的内存相比,有25-100倍的优势。

粗略地说,通过CPU缓存和RAM访问时间,我们通过使用缓存友好的结构体而不是类来获得13.46倍的加速。

  • 指令缓存:“指令缓存”。就像数据缓存一样,它存储组成程序的指令。当CPU执行您的指令时,它会从RAM中提取它们。为了加快速度,它首先检查指令缓存。这一切都与数据缓存非常相似。 https://jacksondunstan.com/articles/3879

解决方式是避免 虚函数virtual function调用 和 Delegates

 

 

 

 

22、 C# 语言中集合使用的常见错误。

https://jacksondunstan.com/articles/5145

 

①、 字典查找调用两边

if (myDictionary.Contains(myKey)) { int value = myDictionary[myKey];

 

最好是 :

int value; if (myDictionary.TryGetValue(myKey, out value))

 

②、没有设置初始容量

没有初始容量, 我们并不总是事先知道正确的尺寸,但是一个好的猜测至少可以节省大部分的重新分配和复制。

集合类型List和Dictionary具有允许我们设置初始容量的构造函数。

如何没有指定, 反编译发现。

private static readonly T[] EmptyArray = new T[0]; public List() { _items = EmptyArray; }

构造的数组大小就是0 , 之后会有比较多的重新分配 1,2,4,8,16,32,64 等等。

 

至于Dictionary,这是它的默认构造函数: 初始容量10。 有可能浪费等

public Dictionary ()

{ Init (10,null ); }

 

③、结构体装箱。

这种情况发生在类似的泛型类中List,Dictionary

已知的是 枚举作为字典的Key 会产生装箱。 自行百度 怎么避免。

其它情况就是比较 Object.Equals 时需要装箱。

struct IntStruct { public int Value; }

return list.Contains(new IntStruct { Value = 1 }); // 此时Contains会装箱

return dict[new IntStruct { Value = 1 }];

Unity3d 周分享(15期 2019.4.14 )_第22张图片

一个因为要得到 Object.GetHashCode 哈希值。 一个是 Object.Equals ,都是object操作。

 

 

如何避免 请参考文章: https://jacksondunstan.com/articles/5148

 

④、 使用LINQ而不是内置方法

LINQ提供的扩展System.Linq方法通常具有与集合类型(如List和Dictionary)提供的内置名称相似的名称。

在使用的时候注意。

 

⑤ 、参数传递接口

集合类型实现了许多接口。List就包含 ICollection,IEnumerable,IList,IReadOnlyCollection,IReadOnlyList,ICollection,IEnumerable,和IList 接口。

使用的时候就可能这样

void TakeList(List list){}

void TakeInterface(IList list){}

List list = new List();

TakeList(list); // 传递类 list.Add(1); //非虚方法调用 快点。

TakeInterface(list); // 传递接口(多态) list.Add(1); //虚方法调用。慢点。

 

而且 接口类型IList 它会导致foreach循环 产生GC.

List 进行 foreach循环 不会导致GC 分配。

 

评论中: 关于最后两点,发现LinqFaster(https://github.com/jackmott/LinqFaster)在使用Lists时是LINQ的绝佳替代品。

 

 

 

 

23、 现在Unity的代码都是选择 il2cpp , mono 方式很快要废弃了。

看到一个系列, 可以更好的了解细节。

C#7.3的IL2CPP输出:元组

C#7.3的IL2CPP输出:模式匹配

C#7.3的IL2CPP输出:ref返回值和局部变量

C#7.3的IL2CPP输出:本地功能fixed,和stackalloc

C#7.3的IL2CPP输出:其他一切

IL2CPP Output for Iterators, Switch, and Using

IL2CPP Output for 无用代码的影响

IL2CPP中的C#6

IL2CPP输出:readonly,sizeof,IntPtr,typeof,GetType

IL2CPP减速的常见功能

不安全代码的IL2CPP输出

C#如何查看生成Il2CPP/C++代码什么样

IL2CPP中的循环

IL2CPP Output: Abstract Classes, Sealed Classes, and Delegates

IL2CPP Function and Boxing Costs

另外三个IL2CPP令人惊讶: 字符串文字,泛型,异常

三个IL2CPP优化 :转换,数组边界检查和空检查。

在编译时获取结构的大小:我们知道,我们的游戏是要在任的x86或ARM处理器和运行x,y以及z字段将是连续的。每个float字段是四个字节,因此向量的大小为12个字节。不幸的是,1)struct包含非值类型,例如a string。应该将结构转换为使用对象句柄,但直到那时我们根本无法知道它的大小。 2)第二个原因是struct包含一个指针。指针具有不同的大小。 文章中提供一个Editor脚本,生成一个名为的文件TypeSizes.cs。它包含一个静态类,其中包含静态字段,指示游戏中没有任何object字段的所有结构的大小。当没有指针字段时,它使用const。当有指针字段,它使用static readonly和sizeof。

阅读IL2CPP输出时遇到的三个惊喜:建议:考虑使用常量和参数而不是静态变量。建议:考虑使用自定义构造函数而不是默认构造函数和对象初始值设定项。建议:当需要保存每个实例的内存时,请考虑使用结构而不是类。

 

 

 

 

 

 

Unity 2018.3中的作业安全(Job-Safe)API.

下过下面编辑器脚本打印出所有 安全API . 目前仅在macOS上运行,但如果需要,可以通过更改managedDirPath以下内容轻松修改以支持Windows或Linux :

现在高达301作业安全的API,但很多都被Unity 给private或者internal

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEditor;
 
public static class JobSafeMenuItem
{
    [MenuItem("Help/List Job Safe APIs")]
    private static void JobSafe()
    {
#if !UNITY_EDITOR_OSX
        throw new Exception("Only macOS is supported at this time");
#endif
        string managedDirPath = Path.Combine(
            Path.Combine(
                EditorApplication.applicationPath,
                "Contents"),
            "Managed");
        Type nativeMethodAttribute = Assembly.LoadFile(
            Path.Combine(
                Path.Combine(
                    managedDirPath,
                    "UnityEngine"),
                "UnityEngine.SharedInternalsModule.dll"))
            .GetType("UnityEngine.Bindings.NativeMethodAttribute");
        string[] paths = Directory.GetFiles(
            managedDirPath,
            "*.dll",
            SearchOption.AllDirectories);
        var methodsByType = new Dictionary>(512);
        object[] emptyParameters = { };
        void AddIfThreadSafe(Type type, Attribute attribute, string method)
        {
            if (nativeMethodAttribute.IsInstanceOfType(attribute)
                && (bool)nativeMethodAttribute
                    .GetProperty("IsThreadSafe")
                    .GetGetMethod()
                    .Invoke(attribute, emptyParameters))
            {
                List list;
                if (!methodsByType.TryGetValue(type, out list))
                {
                    list = new List(32);
                    methodsByType[type] = list;
                }
                list.Add(method);
            }
        }
        BindingFlags allFlags =
            BindingFlags.NonPublic
            | BindingFlags.Static
            | BindingFlags.Instance;
        foreach (var path in paths)
        {
            Assembly assembly = Assembly.LoadFile(path);
            foreach (Type type in assembly.GetTypes())
            {
                foreach (MethodInfo meth in type.GetMethods(allFlags))
                {
                    foreach (Attribute attribute in meth.GetCustomAttributes())
                    {
                        AddIfThreadSafe(type, attribute, meth.ToString());
                    }
                }
                foreach (PropertyInfo prop in type.GetProperties(allFlags))
                {
                    foreach (Attribute attribute in prop.GetCustomAttributes())
                    {
                        AddIfThreadSafe(type, attribute, prop.ToString());
                    }
                }
            }
        }
 
        StringBuilder builder = new StringBuilder(1024 * 10);
        var sortedMethodsByType = new List>>(
            methodsByType);
        sortedMethodsByType.Sort((a, b) => a.Key.Name.CompareTo(b.Key.Name));
        foreach (var pair in sortedMethodsByType)
        {
            pair.Value.Sort();
            foreach (string method in pair.Value)
            {
                builder.Append(pair.Key.Name);
                builder.Append(":    ");
                builder.AppendLine(method);
            }
        }
        Debug.Log(builder.ToString());
        EditorGUIUtility.systemCopyBuffer = builder.ToString();   // 复制到系统剪贴板。将其粘贴到文本编辑器中以查看完整结果。
    }
}

 

 

 

24、 FixedUpdate如何工作 ?

早些年做个测试一个测试 , 但是还是为了测试 使用协程倒计时和 正常倒计时是否有偏差。 测试结果肯定是有偏差的。 当时顺便测试 TimeScale的 Update, FixedUpdate 的影响。

log结果上看 FixedUpdate 的执行频率确认相当有规律。 那是因为在正常情况下。

阅读 文章 https://jacksondunstan.com/articles/4824 请注意,有时FixedUpdate在一个帧中有两个调用,有时只有一个调用。

当 FixedUpdate 比FPS 更频繁地调用,这意味着不是每个帧都会调用FixedUpdate。

假设我们的游戏由于某种原因表现非常糟糕,我们的每帧运行速度为5 FPS,总共200毫秒。

然后我们将所需的FixedUpdate速率设置为100 Hz,这应该意味着每10毫秒调用一次。最后,我们将最大更新间隔配置为FixedUpdate50毫秒Edit > Project Settings > Time。以下是此方案的表现:

Unity3d 周分享(15期 2019.4.14 )_第23张图片

在这种情况下, FixedUpdate当应该被调用20次时,但是实际上只调用10次。

首先,Unity确实会尝试以FixedUpdate所需的速率调用。但是,这些调用需要适合离散帧。这意味着当Unity动态调整到实际帧速率时,任何给定帧上可能会有零个,一个或多个调用。这也意味着调用FixedUpdate不一定在指定的时间间隔内。

当FixedUpdate函数本身是帧速率减慢的原因时,会发生一个特别令人讨厌的情况。这很可能导致Unity试图“赶上”额外的调用,FixedUpdate以便平均所需的速率。这些额外的调用将进一步降低帧速率,因为FixedUpdate在这种情况下很昂贵的。情况可能会复合,直到达到最大间隔。此时帧速率可能已经完全消失。

 

 

 

 

 

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