unity 开发中10个小知识(二)

目录

1、 通过位移对整形变量开关控制

2、 Undo.RegisterCreatedObjectUndo 在编辑情况下,创建的GameObject可以撤销

3、获得和设置MonoScript脚本脚本执行顺序

1)MonoImporter.GetExecutionOrder()函数

 2)MonoImporter.SetExecutionOrder设置脚本执行顺序

 3)editor目录下获得和设置脚本顺序

4、InitializeOnLoad属性

5、AssetModificationProcessor类的使用

6、EditorApplication.update 编辑器情况下Update函数

 7、如何计算帧数fps 

8、关于GC函数

System.GC.GetTotalMemory函数,该函数的代码

System.GC.CollectionCount

GC.Collect 手动GC

 9、获取多个dll modul 所占用内存

10、打印日志时自动调用函数


 

1、 通过位移对整形变量开关控制

    private int _mask = 0;
    public void SetMask(int left) { _mask |= (1 << left); }
    public void ClearMask(int left) { _mask &= ~(1 << left); }

    public bool IsMask(int left)
    {
        if ((_mask & (1 << left))!=0)
            return true;
        return false;
    }

    public void ZeroMask() { _mask = 0; }

2、 Undo.RegisterCreatedObjectUndo 在编辑情况下,创建的GameObject可以撤销

        RegisterCreatedObjectUndo是Unity编辑器专门配备的命令历史记录功能,可以在添加、更新或删除任何Unity对象时自动创建撤销记录。该函数用于在操作Unity对象后立即将其记录在撤销系统中,供之后可以回退或重做。例如,可以将其用于在编辑器中创建物体、添加组件、设置参数等操作,以使用户可以放心地进行修改。

当进行复杂的编辑操作时,可能需要对多个对象进行操作。在这种情况下,可以通过使用RegisterCompleteObjectUndo函数或在Undo操作列表中创建并添加UndoGroup对象来批量记录这些操作。这样做可以使您能够将所有相关操作编组,并以一起撤消或重做。

例如,下面的代码会创建一个新的空物体并将其添加到场景中。同时,使用RegisterCreatedObjectUndo函数来注册该操作,并将其添加到撤销系统中:

GameObject newObject = new GameObject("myGameObject");
Undo.RegisterCreatedObjectUndo(newObject, "Create New Object");

这样,当用户单击编辑器的撤销按钮时,Unity将自动撤消“Create New Object”操作,删除新的空物体。当用户单击重做按钮时,Unity将重新创建该物体,以恢复其原始状态。

总的来说,Unity的Undo/Redo功能是一种非常有用的工具,用于管理编辑器操作的历史记录并允许用户撤消和重做它们。注销创建的对象是一种有效的方式,可以使用此功能来记录创建的新对象,并使Unity撤销和重做操作结果符合预期。

3、获得和设置MonoScript脚本脚本执行顺序

1)MonoImporter.GetExecutionOrder()函数

GetExecutionOrder()是 MonoBehaviour 类中的一个函数,可以用于检索给定脚本当前在脚本执行序列中的执行顺序。脚本执行序列指的是所有的 MonoBehaviour 对象实例的执行顺序,在 Unity 中,MonoBehaviour 对象的执行顺序对于处理和调整游戏对象及其组件之间的依赖关系非常重要。

在 Unity 编辑器中,MonoBehaviour 对象的执行顺序通常由所属 GameObject 中组件的执行顺序控制,或者通过脚本的“Execution Order”(脚本的执行顺序)属性控制。

使用 GetExecutionOrder() 函数,需要注意以下三点:

  1. 此函数筛选当前脚本是否添加到 GameObject 上,如果没有当前脚本,函数将返回 -1。

  2. 此函数的返回值代表当前脚本的执行顺序。这个值是一个整数,越大的执行顺序越靠后,较小的执行顺序先运行。如果两个脚本有相同的执行顺序,根据脚本的添加顺序来确定执行顺序。

  3. 此函数不是特别推荐使用,因为在某些情况下更好的做法是使用依赖关系和事件驱动的设计来减少对顺序的依赖性。应该尽量避免出现依赖于特定顺序的脚本,以免在更新游戏时引入bug。如果确实需要使用脚本的执行顺序,请小心选择执行顺序,并仔细测试您的代码。

如下是 GetExecutionOrder() 函数的用法示例:

using UnityEngine;

public class Example : MonoBehaviour
{
    void Awake()
    {
        int order = GetExecutionOrder(this);
        Debug.Log($"Script execution order for {this.gameObject.name}: {order}");
    }


}

 2)MonoImporter.SetExecutionOrder设置脚本执行顺序

int ReporterExecOrder = -10000;

MonoImporter.SetExecutionOrder(reporterScript, ReporterExecOrder);

 3)editor目录下获得和设置脚本顺序

//myMonoScript是MonoBehaviour对象

int order = -10000;

MonoScript monScript = MonoScript.FromMonoBehaviour(myMonoScript);

if (MonoImporter.GetExecutionOrder(monScript ) != order ) {
    MonoImporter.SetExecutionOrder(monScript , order );
    //Debug.Log("Fixing exec order for " + myMonoScript.name);
}

4、InitializeOnLoad属性

InitializeOnLoad是Unity编辑器中的一个内部静态构造函数,用于在运行时初始化静态成员变量。他们扮演着与类构造函数类似的作用,而不是像普通构造函数初始化对象实例一样。这意味着这些静态构造函数在类第一次被加载时调用,并且只调用一次。

InitializeOnLoad函数通常用于执行后台任务,并在Unity加载时初始化它们,例如自定义的AssetPostProcessor或不需要在编辑器界面中显示的自动化脚本等。例如,您可以使用此特性创建一个初始化任务,该任务将在运行时启动,并负责在编辑器启动后自动执行一些后台任务,例如预编译各种资源、初始化插件和其他管理任务。

下面是一个使用InitializeOnLoad实现自动化任务的示例:

using UnityEngine;
using UnityEditor;

[InitializeOnLoad]
public class MyAutoTask 
{
    static MyAutoTask()
    {
        EditorApplication.delayCall += () =>
        {
            Debug.Log("Initializing auto task...");
            // Execute your background task here
        };
    }

在这个例子中,我们创建了一个名为MyAutoTask的自动任务类,并使用InitializeOnLoad特性将其标记为在Unity加载时自动初始化。在该类静态构造函数中,我们定义了将在EditorApplication.delaycall中前等待执行的,用于初始化后台任务的代码块。请注意,此代码块将在编辑器启动后立即执行,而不需要用户手动运行。

总的来说,InitializeOnLoad是Unity编辑器中的一个有用的静态构造函数,可用于加载时执行预初始化后台任务、插件管理和其他自动化任务▍

5、AssetModificationProcessor类的使用

AssetModificationProcessor 是 Unity 的脚本 API 中的一个类,允许您在资源导入到项目之前修改它们。该类提供了一些方法,您可以使用这些方法在资源被导入时拦截和修改资源。

您可以使用 AssetModificationProcessor 以多种方式修改资源,例如更改其导入设置、修改其内容,甚至防止它们被导入。这可用于自动化某些任务或确保团队开发人员在整个项目中一致地导入资源。

要使用 AssetModificationProcessor,您必须创建一个从中继承的类,并实现所需的方法。可用方法包括 OnWillCreateAssetOnWillDeleteAssetOnWillMoveAsset 和 OnWillSaveAssets。当发生某些事件时,例如正在创建新资源或正在保存资源时,Unity 会调用这些方法。

总的来说,AssetModificationProcessor 是一个强大的工具,可用于简化 Unity 项目中的资源管理。

AssetModificationProcessor 是 Unity 的脚本 API 中的一个类,允许您在资源导入到项目之前修改它们。该类提供了一些方法,您可以使用这些方法在资源被导入时拦截和修改资源。

您可以使用 AssetModificationProcessor 以多种方式修改资源,例如更改其导入设置、修改其内容,甚至防止它们被导入。这可用于自动化某些任务或确保团队开发人员在整个项目中一致地导入资源。

要使用 AssetModificationProcessor,您必须创建一个从中继承的类,并实现所需的方法。可用方法包括 OnWillCreateAssetOnWillDeleteAssetOnWillMoveAsset 和 OnWillSaveAssets。当发生某些事件时,例如正在创建新资源或正在保存资源时,Unity 会调用这些方法。

总的来说,AssetModificationProcessor 是一个强大的工具,可用于简化 Unity 项目中的资源管理。

 
   
using UnityEditor;
using UnityEngine;

public class AssetNameModifier : AssetModificationProcessor
{
    public static void OnWillCreateAsset(string path)
    {
        // 获取资源名称和扩展名
        string assetName = System.IO.Path.GetFileNameWithoutExtension(path);
        string assetExtension = System.IO.Path.GetExtension(path);

        // 如果资源是脚本,则不修改它
        if (assetExtension == ".cs")
        {
            return;
        }

        // 将资源名称转换为小写,并在名称中添加“_new”
        assetName = assetName.ToLower() + "_new";

        // 生成新的资源路径
        string newAssetPath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(path), assetName + assetExtension);

        // 重命名资源
        AssetDatabase.RenameAsset(path, assetName);

        // 如果资源不是文件夹,则移动它
        if (!AssetDatabase.IsValidFolder(newAssetPath))
        {
            AssetDatabase.MoveAsset(path, newAssetPath);
        }
    }
}

在这个示例中,我们创建了一个名为 AssetNameModifier 的类,它继承自 AssetModificationProcessor。我们实现了 OnWillCreateAsset 方法,这个方法在资源被创建时被调用。我们获取了资源的名称和扩展名,并检查了资源是否是脚本。如果资源是脚本,则不修改它。否则,我们将资源名称转换为小写,并在名称中添加“_new”。然后,我们生成新的资源路径,并使用 AssetDatabase.RenameAsset 方法重命名资源。最后,如果资源不是文件夹,则移动它

 

6、EditorApplication.update 编辑器情况下Update函数

EditorApplication.update 是 Unity 编辑器脚本 API 中的一个方法,用于在编辑器运行时每帧调用。该方法可以用于执行一些需要在编辑器运行时持续进行的操作,例如更新编辑器窗口中的 UI 或执行某些后台任务。

EditorApplication.update 方法的调用频率与编辑器的帧率相同,通常为每秒 60 帧。因此,您可以使用此方法来执行实时操作,例如在编辑器中显示运行时数据或进行实时计算。

要使用 EditorApplication.update 方法,您需要在编辑器脚本中实现一个方法,并将其传递给 EditorApplication.update。例如:

public class MyEditorScript
{
    [InitializeOnLoadMethod]
    static void Initialize()
    {
        EditorApplication.update += Update;
    }

    static void Update()
    {
        // 在每帧执行操作
    }
}

 

在上面的示例中,我们使用 InitializeOnLoadMethod 特性来确保在编辑器加载时调用 Initialize 方法,并将 Update 方法添加到 EditorApplication.update 中以在编辑器运行时每帧调用。

总之,EditorApplication.update 方法是 Unity 编辑器脚本 API 中的一个强大工具,可用于执行实时操作并更新编辑器窗口中的 UI。

 7、如何计算帧数fps 

public class FPS : MonoBehaviour
{

    private float _lastUpdate = -1;

    private int _frames = 0;

    private const float _updateInterval = 0.25f;

    private int _fps;
    // Update is called once per frame
    void Update()
    {
        _frames++;
        float dt = Time.realtimeSinceStartup - _lastUpdate;
        if (dt > _updateInterval && _frames > 1)
        {
            _fps = (int)(_frames / dt);
            _lastUpdate = Time.realtimeSinceStartup;
        }
    }

    public int Fps => _fps;
}

8、关于GC函数

获取堆内存

System.GC.GetTotalMemory函数,该函数的代码
 

    [SecuritySafeCritical]
    [__DynamicallyInvokable]
    public static long GetTotalMemory(bool forceFullCollection)
    {
      long totalMemory = GC.GetTotalMemory();
      if (!forceFullCollection)
        return totalMemory;
      int num1 = 20;
      long num2 = totalMemory;
      float num3;
      do
      {
        GC.WaitForPendingFinalizers();
        GC.Collect();
        long num4 = num2;
        num2 = GC.GetTotalMemory();
        num3 = (float) (num2 - num4) / (float) num4;
      }
      while (num1-- > 0 && (-0.05 >= (double) num3 || (double) num3 >= 0.05));
      return num2;
    }

System.GC.CollectionCount

System.GC.CollectionCount 是 .NET Framework 中的一个方法,用于获取垃圾回收器已经执行的垃圾回收次数。在 Unity 中,您可以使用此方法来监视垃圾回收器的性能和行为,并识别潜在的性能问题。

垃圾回收是一种自动的内存管理技术,用于在运行时检测和释放不再使用的内存。每当垃圾回收器执行垃圾回收时,它都会增加垃圾回收计数器的值。通过检查此计数器的值,您可以确定垃圾回收器执行了多少次垃圾回收操作。

要使用 System.GC.CollectionCount 方法,您可以在 Unity 中编写以下代码:

nt generation = 0; // 代表垃圾回收的代数
int count = System.GC.CollectionCount(generation);
Debug.Log("Generation " + generation + " has been collected " + count + " times.");

在上面的示例中,我们使用 System.GC.CollectionCount 方法来获取第 0 代的垃圾回收计数器的值,并将其记录在 Unity 的日志中。

总的来说,System.GC.CollectionCount 方法是一个有用的工具,可用于监视垃圾回收器的性能和行为,并帮助您识别潜在的性能问题。

GC.Collect 手动GC

GC.Collect

 9、获取多个dll modul 所占用内存

在 C# 中,您可以使用 System.Diagnostics.Process 类和 System.Diagnostics.ProcessModule 类来获取有关进程和进程模块的信息。具体来说,您可以使用以下方法和属性:

  • System.Diagnostics.Process.GetCurrentProcess:获取当前进程的实例。
  • System.Diagnostics.ProcessModule 类的 ModuleMemorySize 属性:获取进程模块在进程的虚拟地址空间中占用的内存大小。
  • System.Diagnostics.ProcessModuleCollection 类的 GetEnumerator 方法和 Count 属性:获取进程的模块集合中的所有模块和模块的数量。
  • System.GC.GetTotalMemory:获取当前应用程序使用的所有堆内存的总量(以字节为单位)。

例如,以下代码演示了如何使用 System.Diagnostics.Process 类和 System.Diagnostics.ProcessModule 类来获取堆中所有对象的信息:

System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();
System.Diagnostics.ProcessModuleCollection modules = process.Modules;
long totalMemory = System.GC.GetTotalMemory(false);
foreach (System.Diagnostics.ProcessModule module in modules)
{
    int moduleMemorySize = module.ModuleMemorySize;
    float moduleMemoryUsage = (float)moduleMemorySize / totalMemory;
    Debug.Log("Module " + module.ModuleName + " uses " + moduleMemoryUsage.ToString("P") + " of the heap.");
}

在上面的示例中,我们使用 System.Diagnostics.Process 类和 System.Diagnostics.ProcessModule 类来获取当前进程的实例和进程模块集合。然后,我们使用 System.GC.GetTotalMemory 方法来获取当前应用程序使用的所有堆内存的总量,并遍历进程模块集合来计算每个模块在堆中所占的内存比例。

10、打印日志时自动调用函数

Application.logMessageReceivedThreaded 在Debug.Log等日志函数时被调用,该函数注意线程安全

Application.logMessageReceivedThreaded 是 Unity 的脚本 API 中的一个方法,用于在单独的线程中接收 Unity 日志消息。该方法可以用于在运行时监视应用程序的日志,并在发生错误或警告时采取适当的行动。

在 Unity 中,日志消息通常在主线程中发送。这意味着如果您在主线程中处理日志消息,则可能会影响应用程序的性能。为了避免这种情况,您可以使用 Application.logMessageReceivedThreaded 方法在单独的线程中接收日志消息。

要使用 Application.logMessageReceivedThreaded 方法,您需要在 Unity 中编写一个方法,并将其传递给 Application.logMessageReceivedThreaded。例如:

public class MyLogger : MonoBehaviour
{
    void OnEnable()
    {
        Application.logMessageReceivedThreaded += HandleLog;
    }

    void OnDisable()
    {
        Application.logMessageReceivedThreaded -= HandleLog;
    }

    void HandleLog(string logString, string stackTrace, LogType type)
    {
        // 在单独的线程中处理日志消息
    }
}

在上面的示例中,我们创建了一个名为 MyLogger 的 MonoBehaviour,并实现了 OnEnable 和 OnDisable 方法来添加和删除日志消息处理程序。然后,我们实现了一个名为 HandleLog 的方法,用于在单独的线程中处理日志消息。

总之,Application.logMessageReceivedThreaded 方法是一个有用的工具,可用于在单独的线程中接收 Unity 日志消息。通过使用此方法,您可以避免在主线程中处理日志消息时影响应用程序的性能。

你可能感兴趣的:(工作笔记,unity,unity,游戏引擎,c#)