Skip to content
This repository has been archived by the owner on Aug 13, 2023. It is now read-only.

LastAssertor/Unity-Attributes-Example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Unity-Attributes-Example


项目介绍

Unity Attribute 例子整理

添加组件菜单

引擎会把该程序集下的全部类视为编辑器类

设置Application.onBeforeRender回调函数的执行顺序

DHR拾色器界面

为脚本添加快捷菜单

为脚本字段添加快捷菜单

添加ScriptableObject的创建菜单

自定义Tilemap笔刷

延迟更新字段的值

不允许多次挂载同一个脚本到同一个物体上

添加[ExcludeFromObjectFactory]后,调用ObjectFactory.AddComponent<对应组件>时,会失败并报错

添加[ExcludeFromPreset]后,即便用代码生成Preset,Preset的参数也不支持设置

添加[ExcludeFromPreset]后,不能选择和保存为Preset
using UnityEngine;

[ExecuteAlways]
public class ExampleClass : MonoBehaviour
{
    void Start()
    {
        if (Application.IsPlaying(gameObject))
        {
            // Play logic
        }
        else
        {
            // Editor logic
        }
    }
}
    /// <summary>
    /// https://docs.unity3d.com/ScriptReference/ExecuteInEditMode.html
    /// </summary>

    // The PrintAwake script is placed on a GameObject.  
    // The Awake function is usually
    // called when the GameObject is started at runtime.  
    // Due to the ExecuteInEditMode
    // attribute, the script is also called by the Editor.  
    // The Awake() function will be called,
    // for example, when the Scene is changed to a
    // different Scene in the Project window.
    // The Update() function is called, 
    // for example, when the GameObject transform
    // position is changed in the Editor.

    [ExecuteInEditMode]
    public class PrintAwake : MonoBehaviour {
        void Awake() {
            Debug.Log("Editor causes this Awake");
        }

        void Update() {
            Debug.Log("Editor causes this Update");
        }
    }

颜色渐变界面

// 控制OnGUI函数调用,在哪些显示设备上有效

// GUITarget attribute allows to control for which display the OnGUI is called.

[GUITarget(1, 2)]
void OnGUI() {
    var color = GUI.color;
    GUI.color = Color.red;
    GUILayout.Space(30f);
    GUILayout.Label("     This information is visible on 'Display 2' and 'Display 3' only.");
    GUI.color = color;
}

[Header("Health Settings")]
public int health = 0;
public int maxHealth = 100;

[Header("Shield Settings")]
public int shield = 0;
public int maxShield = 0;

点击组件上的问号按钮,会打开指定的网站

// 隐藏字段
[HideInInspector]
public int p = 5;

// 指定字符串能显示的行数(我也不知道为什么只显示了7行)
[Multiline(8)]
public string value;

还有,需要按回车才能换行

(继承ScriptableObject的类)添加[PreferBinarySerialization]后,
对应的asset文件的存储格式,会变成二进制格式

滑条样式

1. 自动添加依赖的组件
2. 不能手动移除依赖的组件(必须先移除该组件,才能移除依赖的组件)

运行时初始化,触发调用(全都只触发一次)

class RuntimeInitializeOnLoadMethodExample {

    // Callback used for registration of subsystems
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
    static void OnSubsystemRegistration() {
        Debug.Log("0.OnSubsystemRegistration");
    }

    // Callback when all assemblies are loaded and preloaded assets are initialized.
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
    static void OnAfterAssembliesLoaded() {
        Debug.Log("1.OnAfterAssembliesLoaded");
    }

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
    static void OnBeforeSplashScreen() {
        Debug.Log("2.Before the splash screen is shown.");
    }

    // 加载第一个场景时会被调用
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void OnBeforeSceneLoad() {
        Debug.Log("3.Before first Scene loaded");
    }

    // 等价于[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
    [RuntimeInitializeOnLoadMethod]
    static void OnAfterSceneLoad1() {
        Debug.Log("4-1.After first Scene loaded");
    }

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
    static void OnAfterSceneLoad2() {
        Debug.Log("4-2.After first Scene loaded");
    }

    [RuntimeInitializeOnLoadMethod]
    static void OnAfterSceneLoad3() {
        Debug.Log("4-3.After first Scene loaded");
    }

}

在Scene窗口,
点击没带[SelectionBase]的组件的物体时,
自动选择父级里,带[SelectionBase]的组件的物体

    //This field gets serialized because it is public.
    public string firstName = "John";

    //This field does not get serialized because it is private.
    private int age = 40;

    //This field gets serialized even though it is private
    //because it has the SerializeField attribute applied.
    [SerializeField]
    private bool hasHealthPotion = true;

    void Start()
    {
        if (hasHealthPotion)
            Debug.Log("Person's first name: " + firstName + " Person's age: " + age);
    }


添加后,能在不同Animator间共享同一个StateMachineBehaviour

// 测试例子

// SharedBetweenAnimatorsExampleCounter.cs

public List<SharedBetweenAnimatorsExample> StateMachineBehaviours;
public List<Animator> Animators;

// SharedBetweenAnimatorsExample.cs

[SharedBetweenAnimators]
public class SharedBetweenAnimatorsExample : StateMachineBehaviour {

    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {

        var Animators = SharedBetweenAnimatorsExampleCounter.Instance.Animators;
        if (!Animators.Contains(animator)) {
            Animators.Add(animator);
        }

        var StateMachineBehaviours = SharedBetweenAnimatorsExampleCounter.Instance.StateMachineBehaviours;
        if (!StateMachineBehaviours.Contains(this)) {
            StateMachineBehaviours.Add(this);
        }
    }

}

运行结果:

StateMachineBehaviours长度为1,Animators长度为2,
两个动画共享同一个SharedBetweenAnimatorsExample

在字段间加一段间隔

可自动换行的字符串界面样式

光标停留在字段名字上一段时间后,显示提示

指定旧字段的名字,防止字段改名后造成的数据丢失

// FormerlySerializedAsExample.cs

public class FormerlySerializedAsExample : MonoBehaviour {
			public string myValue;
}

====>

public class FormerlySerializedAsExample : MonoBehaviour {

      [FormerlySerializedAs("myValue")]
      [SerializeField]
      string m_MyValue;

      public string myValue {
            get { return m_MyValue; }
            set { m_MyValue = value; }
      }

}

允许同时编辑多个

自定义预览界面

[CustomPreview(typeof(MyObject))]
public class MyPreview : ObjectPreview {

    public override bool HasPreviewGUI() {
        return true;
    }

    public override void OnPreviewGUI(Rect r, GUIStyle background) {
        GUI.Label(r, target.name + " is being previewed");
    }

}

自定义属性界面

自定义Scene窗口的Gizmo

public class MyScriptGizmoDrawer {

    [DrawGizmo(GizmoType.Selected | GizmoType.Active)]
    static void DrawGizmoForMyScript(MyScript scr, GizmoType gizmoType) {
        Vector3 position = scr.transform.position;

        if (Vector3.Distance(position, Camera.current.transform.position) > 10f)
            Gizmos.DrawIcon(position, "MyScript Gizmo.png", true);
    }
}

// 打开项目后,生成该类,并调用该类的构造函数

/// <summary>
/// https://docs.unity3d.com/ScriptReference/InitializeOnLoadAttribute.html
///
/// Allows you to initialize an Editor class when Unity loads,
/// and when your scripts are recompiled.
/// Static constructors with this attribute are called
/// when scripts in the project are recompiled (also known as a Domain Reload).
/// when Unity first loads your project,
/// but also when Unity detects modifications to scripts
/// (depending on your Auto Refresh preferences),
/// and when you enter Play Mode (depending on your Play Mode configuration).
/// </summary>

// Running Editor Script Code on Launch

[InitializeOnLoad]
public class InitializeOnLoadExample {

    static InitializeOnLoadExample() {

        /// https://docs.unity3d.com/Manual/RunningEditorCodeOnLaunch.html

        //Debug.Log("Up and running");

        // editor frame update

        //EditorApplication.update += Update;

    }

    //static void Update() {
    //    Debug.Log("Updating");
    //}

}


// 打开Unity引擎后触发调用

[InitializeOnLoadMethod]
static void OnProjectLoadedInEditor() {
      /// https://docs.unity3d.com/ScriptReference/InitializeOnLoadMethodAttribute.html
      //Debug.Log("Project loaded in Unity Editor");
}

打开资源时(在Project窗口双击文件后)触发调用
发布程序后触发调用(每次)

// PostProcessBuildExample.cs

[PostProcessBuild(1)]
public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) {
      // after build
      Debug.Log(target + ":" + pathToBuiltProject);
}


每次进入场景前触发调用

// PostProcessSceneExample.cs

[PostProcessScene(2)]
static void OnPostprocessScene() {
    OnBeforeSceneLoaded();
}

static void OnBeforeSceneLoaded() {
    var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
    Debug.LogWarning("OnBeforeSceneLoaded ======> " + scene.name + ".unity");
}

GridPaintSorting(the sorting of Active Targets in the Active Tilemap list of the Tile Palette window)

自定义脚本的图标


自定义枚举显示的名字

public enum MyEnum {
    A,
    [InspectorName("B评分")]
    B,
    [InspectorName("C评分")]
    C,
}

限定最小值

不可排序列表

// NonReorderableExample.cs

/// <summary>
/// 不可排序
/// </summary>
[NonReorderable]
public List<int> nonReorderableList = new List<int> { 1, 4, 2, 99 };
/// <summary>
/// 可排序
/// </summary>
public List<string> reorderableList = new List<string> { "哈哈", "哈", "哈哈哈哈" };


打开搜索窗口,可以通过关键字和query text来搜索资源,
但这个query text的具体语法,我暂时没能在官方文档里找到相关的链接或教程
编辑器里点播放按钮后触发调用

[InitializeOnEnterPlayMode]
static void OnEnterPlaymodeInEditor(EnterPlayModeOptions options) {
			Debug.Log("Entering PlayMode");
}

配合ScriptableSingleton<T>使用,指定数据的保存位置


指定Editor对应的组件

// CustomEditorExampleEditor.cs

[CustomEditor(typeof(CustomEditorExample), true)]
public class CustomEditorExampleEditor : Editor { ... }

CustomEditorExample2继承与CustomEditorExample,
组件面板上都带有名为A button的按钮


脚本编译结束后触发调用

public class DidReloadScriptsExample {
    [DidReloadScripts]
    static void AfterReloadedScripts() {
        Debug.LogWarning("AfterReloadedScripts");
    }
}

About

Unity Attribute 例子整理

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages