在Unity编辑模式下运行Mono中的方法

[ExecuteAlways]

最简单的方法当然是直接给Mono加上[ExecuteAlways]修饰,这样Mono中的Awake,Update等等都可以在编辑模式下按照原本的时机运行。

[ExecuteAlways]
public class TestScript : MonoBehaviour {
    void TestMethod(){
        Debug.Log("TestMethod");
    }
    void Update(){
        TestMethod();
    }
}

OnValidate

除了ExecuteAlways以外,还可以用OnValidate,OnValidate 方法会在脚本中的变量发生更改时自动调用,适用于与 Inspector 参数联动的逻辑。

public class TestScript : MonoBehaviour {
    public float value;

    private void OnValidate()
    {
        // 在编辑器中参数变更时自动调用
        Debug.Log($"Value changed to: {value}");
    }
}

OnDrawGizmos+SceneView.RepaintAll()

有的时候如果想让某段代码能在编辑模式下像Update一样一直运行,但又不想用[ExecuteAlways]或者觉得没必要,就可以用这个方法,需要在SceneView里打开Gizmos的开关

public class TestScript : MonoBehaviour {
    void TestMethod(){
        Debug.Log("TestMethod");
    }
    //force update on editormode
    void OnDrawGizmos() {
        if (!Application.isPlaying) {
            //call you method
            TestMethod();
        }
        //force Repaint
        if (!Application.isPlaying) {
            UnityEditor.EditorApplication.QueuePlayerLoopUpdate();
            UnityEditor.SceneView.RepaintAll();
        }
    }
}

扩展

一般来说上面提到的几个算是够用了,但我在使用OnDrawGizmos方法的时候,依旧觉得太麻烦,就没有什么更方便的方法吗,于是我想到了C#的Attribute,如果可以像[ExecuteAlways]那样简单的标记一个方法,让这方法可以不断的被调用,那写起来不就方便多了吗,于是

EditorUpdateMethodManager

//==============================================================================
// Filename:     EditorUpdateMethodManager
// Description:  
//               
// Version:      1.0
// Compiler:     2022.3.40f1 (cbdda657d2f0)
// Author:       Akota
// CreateTime:   2024-12-02 10:14:40
//==============================================================================
using System;
using UnityEngine;
#if UNITY_EDITOR
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
#endif

[ExecuteAlways]
public class EditorUpdateMethodManager : MonoBehaviour {
    private void Awake() {
        if (Application.isPlaying) {
            Destroy(gameObject);
        }
    }
#if UNITY_EDITOR
    private  List<RegisteredMethod> RegisteredMethods = new List<RegisteredMethod>();
    private void OnEnable() {
        if (!Application.isPlaying) { 
            RegisterMethodsFromScene();
            EditorApplication.hierarchyChanged += RegisterMethodsFromScene; // 监听层级变化
        }
    }

    private void OnDisable() {
        if (!Application.isPlaying) { 
            EditorApplication.hierarchyChanged -= RegisterMethodsFromScene; // 移除监听
        }
    }

    private void Update() {
        if (!Application.isPlaying) { 
            foreach (var registeredMethod in RegisteredMethods) {
                // 仅调用激活的 MonoBehaviour 实例上的方法
                if (registeredMethod.MonoInstance != null &&
                    registeredMethod.MonoInstance.enabled &&
                    registeredMethod.MonoInstance.gameObject.activeInHierarchy) {
                    registeredMethod.Method.Invoke(registeredMethod.MonoInstance, null);
                }
            }
        }
    }

    /// <summary>
    /// 注册当前场景中的所有标记方法
    /// </summary>
    public void RegisterMethodsFromScene() {
        RegisteredMethods.Clear();

        // 获取当前场景中所有 MonoBehaviour
        var monoBehaviours = FindObjectsOfType<MonoBehaviour>(true); // 包括未激活的对象

        foreach (var mono in monoBehaviours) {
            // 查找带有 RegisterEditorUpdateMethodAttribute 的方法
            var methods = mono.GetType()
                .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                .Where(m => m.GetCustomAttribute<EditorUpdateMethodAttribute>() != null);

            foreach (var method in methods) {
                // 记录方法和实例
                RegisteredMethods.Add(new RegisteredMethod(mono, method));
            }
        }
    }

    /// <summary>
    /// 注册方法信息
    /// </summary>
    private class RegisteredMethod {
        public MonoBehaviour MonoInstance { get; }
        public MethodInfo Method { get; }

        public RegisteredMethod(MonoBehaviour monoInstance, MethodInfo method) {
            MonoInstance = monoInstance;
            Method = method;
        }
    }
#endif
}

/// <summary>
/// 标记可以被 EditorUpdateMethodManager 调用的方法
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class EditorUpdateMethodAttribute : Attribute {
}

原理也很简单,就是利用[ExecuteAlways]来标记EditorUpdateMethodManager,然后在EditorUpdateMethodManager里面查找当前场景下的所有Mono里被EditorUpdateMethod标记的方法,并在Manager的Update中统一调用。

示例

public class TestScript : MonoBehaviour {
    [EditorUpdateMethod]
    void TestMethod(){
        Debug.Log("TestMethod");
    }
}

要是手动在场景里创建一个Manager还是嫌麻烦的话

EditorUpdateManagerInitializer

//==============================================================================
// Filename:     EditorUpdateManagerInitializer
// Description:  
//               
// Version:      1.0
// Compiler:     2022.3.40f1 (cbdda657d2f0)
// Author:       Akota
// CreateTime:   2024-12-02 10:16:02
//==============================================================================
#if UNITY_EDITOR
using System;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;

[InitializeOnLoad]
public static class EditorUpdateManagerInitializer {
    static EditorUpdateManagerInitializer() {
        // 监听层级变化
        EditorApplication.hierarchyChanged += EnsureEditorUpdateMethodManagerExists;
        EnsureEditorUpdateMethodManagerExists(); // 初次调用
    }

    /// <summary>
    /// 检查场景中是否存在标记方法,并确保 EditorUpdateMethodManager 存在
    /// </summary>
    private static void EnsureEditorUpdateMethodManagerExists() {
        // 检查场景中是否存在 EditorUpdateMethodManager
        var existingManager = GameObject.FindObjectOfType<EditorUpdateMethodManager>();
        if (existingManager == null) { 
            if (HasRegisterEditorUpdateMethodsInScene()) {
                // 没有 EditorUpdateMethodManager,创建一个
                var managerObject = new GameObject("EditorUpdateMethodManager");
                managerObject.AddComponent<EditorUpdateMethodManager>();

                // 标记场景为脏
                Undo.RegisterCreatedObjectUndo(managerObject, "Create EditorUpdateMethodManager");
                EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());

                Debug.Log("[EditorUpdateManagerInitializer] 自动创建了 EditorUpdateMethodManager");
            }
        }
        else {
            // 如果不需要且已有 Manager,则移除
            //Debug.Log("[EditorUpdateManagerInitializer] 场景中没有标记方法,移除了 EditorUpdateMethodManager");
            //Undo.DestroyObjectImmediate(existingManager.gameObject);
            //EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
        }
    }

    /// <summary>
    /// 检查场景中是否有被 RegisterEditorUpdateMethodAttribute 标记的方法
    /// </summary>
    private static bool HasRegisterEditorUpdateMethodsInScene() {
        // 获取当前场景中所有 MonoBehaviour
        var monoBehaviours = GameObject.FindObjectsOfType<MonoBehaviour>(true); // 包括未激活的对象

        foreach (var mono in monoBehaviours) {
            // 检查该 MonoBehaviour 类型是否有标记的方法
            var methods = mono.GetType()
                .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                .Where(m => m.GetCustomAttribute<EditorUpdateMethodAttribute>() != null);

            if (methods.Any()) {
                return true;
            }
        }
        return false;
    }
}
#endif

这段代码会自动检测场景中是否有被EditorUpdateMethod修饰的方法,有的话就自动创建Manager

更新

考虑到要在场景里创建物体,可能显得有点累赘,于是新版放弃[ExecuteAlways],改为利用EditorApplication.update来实现,更精简了

//==============================================================================
// Filename:     EditorUpdateMethodManager
// Description:  
//               
// Version:      1.0
// Compiler:     2022.3.40f1 (cbdda657d2f0)
// Author:       Akota
// CreateTime:   2024-12-02 14:13:12
//==============================================================================
#if UNITY_EDITOR
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;

public class EditorUpdateMethodManager{
    public static EditorUpdateMethodManager instance = null;
    [InitializeOnLoadMethod]
    public static void Init() {
        instance = new EditorUpdateMethodManager();
    }
    public EditorUpdateMethodManager() {
        if (!Application.isPlaying) {
            RegisterMethodsFromScene();
        }
        EditorApplication.hierarchyChanged += RegisterMethodsFromScene; // 监听层级变化
        EditorApplication.update += Update;
    }
    private List<RegisteredMethod> RegisteredMethods = new List<RegisteredMethod>();
    void Update() {
        if (!Application.isPlaying) {
            foreach (var registeredMethod in RegisteredMethods) {
                // 仅调用激活的 MonoBehaviour 实例上的方法
                if (registeredMethod.MonoInstance != null &&
                    registeredMethod.MonoInstance.enabled &&
                    registeredMethod.MonoInstance.gameObject.activeInHierarchy) {
                    registeredMethod.Method.Invoke(registeredMethod.MonoInstance, null);
                }
            }
        }
    }
    /// <summary>
    /// 注册当前场景中的所有标记方法
    /// </summary>
    public void RegisterMethodsFromScene() {
        RegisteredMethods.Clear();

        // 获取当前场景中所有 MonoBehaviour
        var monoBehaviours = Object.FindObjectsOfType<MonoBehaviour>(true); // 包括未激活的对象

        foreach (var mono in monoBehaviours) {
            // 查找带有 RegisterEditorUpdateMethodAttribute 的方法
            var methods = mono.GetType()
                .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                .Where(m => m.GetCustomAttribute<EditorUpdateMethodAttribute>() != null);

            foreach (var method in methods) {
                // 记录方法和实例
                RegisteredMethods.Add(new RegisteredMethod(mono, method));
            }
        }
    }

    /// <summary>
    /// 注册方法信息
    /// </summary>
    private class RegisteredMethod {
        public MonoBehaviour MonoInstance { get; }
        public MethodInfo Method { get; }

        public RegisteredMethod(MonoBehaviour monoInstance, MethodInfo method) {
            MonoInstance = monoInstance;
            Method = method;
        }
    }
}
#endif
/// <summary>
/// 标记可以被 EditorUpdateMethodManager 调用的方法
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Method)]
public class EditorUpdateMethodAttribute : System.Attribute {
}

其他

还有其他的一些,比如编写CustomEditor,在Editor中一直调用方法,又或者用Editor画个按钮,通过在Inspector上手动点击来调用代码,当然也可以用Odin插件,直接用Button修饰Mono中的方法,就会在Inspector上出现按钮了。

结语

代码写的很随便也很丑陋,望包涵

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/929290.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PDF与PDF/A的区别及如何使用Python实现它们之间的相互转换

目录 概述 PDF/A 是什么&#xff1f;与 PDF 有何不同&#xff1f; 用于实现 PDF 与 PDF/A 相互转换的 Python 库 Python 实现 PDF 转 PDF/A 将 PDF 转换为 PDF/A-1a 将 PDF 转换为 PDF/A-1b 将 PDF 转换为 PDF/A-2a 将 PDF 转换为 PDF/A-2b 将 PDF 转换为 PDF/A-3a 将…

【超图】iClient3D for Cesium 以动静结合方式加载WMTS服务

作者&#xff1a;taco 一、问题来源 在最近支持的项目中&#xff0c;我们面临一个挑战&#xff1a;客户需要在前端动态加载高达3亿级别的白模底面数据。这样做的主要原因是客户的数据库会频繁更新&#xff0c;因此我们需要采用动态加载的方式来确保用户界面能够实时反映最新的…

Y20030026 VUE+Springboot+MYSQL+LW+实体店推广平台的设计与实现 源代码 配置 文档 PPT

实体店推广平台的设计与实现 1.摘要2.开发目的和意义3.系统功能设计4.系统界面截图5.源码获取 1.摘要 随着互联网的普及和电子商务的快速发展&#xff0c;消费者的购物习惯发生了显著变化。越来越多的消费者倾向于在线购物&#xff0c;享受便捷、丰富的选择和个性化的购物体验…

基数排序(代码+注释)

#include <stdio.h> #include <stdlib.h>// 获取数组中的最大值 int GetMax(int* a, int n) {int max a[0];for (int i 1; i < n; i) {if (a[i] > max) {max a[i];}}return max; }// 对数组按照某个位数进行计数排序 void CountingSortForRadix(int* a, i…

esp8266 编译、烧录环境搭建

一、准备 xtensa-lx106-elf-gcc8-4-0-esp-2020r3-win32下载&#xff1a;点击跳转 MSYS2 压缩包文件&#xff1a; 固件烧录工具&#xff1a;点击跳转 esp8266源码地址&#xff1a;点击跳转 二、搭建编译环境 1、在D盘创建一个ESP8266目录&#xff0c;解压MSYS2.zip到里面&a…

【Delphi】modbus-TCP 协议库

在日常开发中&#xff0c;也会遇到使用modbus的部件&#xff0c;比如温度控制器、读卡器等等&#xff0c;那么使用Delphi开发&#xff0c;也就必须遵守modbus-TCP协议&#xff0c;如果自己使用TCP控件写也没有问题&#xff0c;不过如果有开源的三方库&#xff0c;别人已经调试过…

AgGrid 组件封装设计笔记:自定义 icon 以及每个 icon 的点击事件处理

文章目录 问题目前解决效果 v1思路 目前解决效果 v0思路 代码V1 问题 自己封装的 AgGrid 如何自定义传递 icon &#xff0c;以及点击事件的处理&#xff1f; 目前解决效果 v1 思路 目前解决效果 v0 思路 一张图片说明一下 代码 V1 父组件使用 <template><MyPageL…

MySQL——MySQL 日志

文章目录 MySQL 文件介绍二进制日志&#xff08;bin log&#xff09;概念binlog 日志的三种格式两阶段提交binlog 落盘更新语句执行流程 慢查询日志&#xff08;slow query log&#xff09;重做日志&#xff08;redo log&#xff09;redo log 日志的理解WAL 技术redo log 的工作…

解决git did not exit cleanly (exit code 128)问题

解决 git did not exit cleanly &#xff08;exit code 128&#xff09;问题 1、错误描述2、解决方法2.1 方法一2.2 方法二 1、错误描述 使用TortoiseGit进行操作时&#xff0c;总是提示下述错误。 2、解决方法 2.1 方法一 打开 TortoiseGit -> Settings 点击 Network&…

家校通小程序实战教程05教师登录

目录 1 搭建角色选择页面2 设置登录方法3 创建加入班级页面4 创建教师主页页面5 完善登录方法总结 我们上一篇开发了教师管理的后台功能&#xff0c;后台一般是给管理员提供的。教师一般是使用小程序开展各类业务&#xff0c;本篇介绍一下教师如何通过小程序登录。 1 搭建角色选…

yolov5 解决:export GIT_PYTHON_REFRESH=quiet

当我们在第一次运行YOLOv5中的train.py程序时&#xff1a;可能会出现以下报错&#xff1a; This initial warning can be silenced or aggravated in the future by setting the $GIT_PYTHON_REFRESH environment variable. Use one of the following values: - quiet|q|silen…

neo4j如何存储关于liquidity structure的层次和关联结构

在 Neo4j 中存储关于流动性结构&#xff08;liquidity structure&#xff09;的层次和关联结构非常适合&#xff0c;因为 Neo4j 是一个基于图的数据库&#xff0c;能够自然地建模和存储复杂的关系和层次结构。下面是如何在 Neo4j 中设计和实现这样的数据模型的详细步骤和示例。…

SpringBoot高级-底层原理

目录 1 SpringBoot自动化配置原理 01-SpringBoot2高级-starter依赖管理机制 02-SpringBoot2高级-自动化配置初体验 03-SpringBoot2高级-底层原理-Configuration配置注解 04-SpringBoot2高级-底层原理-Import注解使用1 05-SpringBoot2高级-底层原理-Import注解使用2 06-S…

C++模拟堆

模板题目 图片来源Acwing 堆的基础知识 代码实现 #include<iostream> #include<algorithm>using namespace std;const int N 1e5 10; int a[N]; int n, m;void down(int u) {int t u;if (2 * u < n && a[2 * u] < a[u]){t 2 * u;}if (2 * u …

RNACOS:用Rust实现的Nacos服务

RNACOS是一个使用Rust语言开发的Nacos服务实现&#xff0c;它继承了Nacos的所有核心功能&#xff0c;并在此基础上进行了优化和改进。作为一个轻量级、快速、稳定且高性能的服务&#xff0c;RNACOS不仅包含了注册中心、配置中心和Web管理控制台的功能&#xff0c;还支持单机和集…

Flink常见面试题

1、Flink 的四大特征&#xff08;基石&#xff09; 2、Flink 中都有哪些 Source&#xff0c;哪些 Sink&#xff0c;哪些算子&#xff08;方法&#xff09; 预定义Source 基于本地集合的source&#xff08;Collection-based-source&#xff09; 基于文件的source&#xff08;…

BERT和RoBERTa;双向表示与单向的简单理解

目录 BERT和RoBERTa大型预训练语言模型 BERT的原理 RoBERTa的原理 举例说明 双向表示与单向的简单理解 除了预训练语言模型,还有什么模型 一、模型类型与结构 二、训练方式与数据 三、应用场景与功能 四、技术特点与优势 BERT和RoBERTa大型预训练语言模型 BERT(Bi…

OpenAI 推出了 Canvas 和 SearchGPT

今天的故事从 ChatGPT 推出的 Canvas 和 SearchGPT 开始。 ©作者|Ninja Geek 来源|神州问学 ChatGPT 在最近推出了令人兴奋的 Canvas 功能&#xff0c;Canvas 不仅带来了 ChatGPT 界面上的变化&#xff0c;还完全改变了人们撰写文档和书写代码的体验&#xff01; Canvas…

CentOS 9 配置静态IP

文章目录 1_问题原因2_nmcli 配置静态IP3_使用配置文件固定IP4_重启后存在的问题5_nmcli 补充 1_问题原因 CentOS 7 于 2014年6月发布&#xff0c;基于 RHEL 7&#xff0c;并在 2024年6月30日 结束维护。 CentOS 9 作为目前的最新版本&#xff0c;今天闲来闲来无事下载下来后…

ProjectSend 身份认证绕过漏洞复现(CVE-2024-11680)

0x01 产品描述: ProjectSend 是一个开源文件共享网络应用程序,旨在促进服务器管理员和客户端之间的安全、私密文件传输。它是一款相当流行的应用程序,被更喜欢自托管解决方案而不是 Google Drive 和 Dropbox 等第三方服务的组织使用。0x02 漏洞描述: ProjectSend r1720 之前…