首页 > 其他分享 >Unity工具开发教程笔记(1/4)

Unity工具开发教程笔记(1/4)

时间:2023-05-24 19:57:44浏览次数:48  
标签:教程 Color void transform 笔记 Unity position public

目录

源视频教程地址为Youtube

什么是Unity工具开发程序员

工具开发程序员目标客户是项目组里其他成员,它们创造出unity原本没有的功能,优化项目的流程。并且你只需要会C#就能搞定这些。

FieldAttributes

比如说我们有ExplosiveBarrel这样的类。比如我们可以首先在radius字段上添加 Range注解,这样在编辑器中,字段会呈现为slider,并且范围为(1f, 8f)。

public class ExplosiveBarrel : MonoBehaviour
{
    [Range(1f, 8f)]
    public float radius = 2;
    public float damage = 10;
    public Color Color = Color.red;
}

辅助图标 Gizmos

工具使用起来越顺手越好。我们希望调整radius的时候,能够在场景(scene view)窗口看到炸yao桶的作用半径。我们可以使用OnDrawGizmos。如果我们在场景中取消了Gizmos的显示,这个函数也将不会被调用。

OnDrawGizmos 函数会被UnityEditor调用,并将效果显示在Scene面板。
也可以使用OnDrawGizmosSelected如果你想在选中物件的时候才显示Gizmos。

具体可以再看看B站的教程

private void OnDrawGizmos()
{
    Gizmos.color = Color;
    Gizmos.DrawWireSphere(transform.position, radius);
    Gizmos.color = Color.yellow;
    Gizmos.DrawLine(transform.position, transform.position+Vector3.up*radius);

}

虽然上面的代码很少,但是对项目的作用是巨大的。

程序集Assembly和ExecuteInEditMode注解

Unity程序集定义中说到每次修改一处代码,Unity会重新编译所有其他的代码,对于代码迭代来说这是有害的,因为增加了编译时间。

  • Every time you change one script, Unity has to recompile all the other scripts, increasing overall compilation time for iterative code changes.

Unity之所以会立刻编译代码,是因为它希望代码的修改在编辑器中是立刻可见的。并且编译结束后,它会尝试重新加载编译生成的程序集。也就是我们编写的代码实时地更新到了Unity这个应用程序中。

如果我们对Barrel类添加构造函数,

public ExplosiveBarrel()
{
    Debug.Log("Create new Barrel");
}

那么当我们拖拽一个MonoBehaviorGameObejct上的时候,我们会发现这个构造函数被调用。也就是说,这个时候Unity在运行中创建了一个ExplosiveBarrel类。虽然是编辑器,但编辑器中也运行着游戏逻辑。只不过默认情况下,有些逻辑在Play Mode中被调用(Awake , Start), 有些逻辑在Edit Mode中会被调用(OnDrawGizmos)。

如果此时,我们添加一个OnEnable函数,这个函数不会被调用,但不意味着Unity此时不能调用它。

private void OnEnable()
{
    Debug.Log("OnEnable Explosive Barrel");
}

只要将这个脚本拖拽到对应的GameObject上,就会生成一个ExplosiveBarrel类。只是Edit模式下,Unity选择了不调用OnEnable等函数。我们可以通过对类添加ExecuteInEditMode注解来实现Unity调用这些函数的效果。

ExecuteInEditMode注解让组件能够在编辑模式执行LifeCycle中的回调函数

[ExecuteAlways]
public class ExplosiveBarrel : MonoBehaviour

By default, MonoBehaviours are only executed in Play Mode. By adding this attribute, any instance of the MonoBehaviour will have its callback functions executed while the Editor is in Edit Mode too.

管理类 ExplosiveBarrelManager

这里我们建立一个管理类,用于追踪所有的ExplosiveBarrel。为了避免Unity序列化以及初始化先后顺序带来的问题,这里原作者将字段设置为static。

public class ExplosiveBarrelManager : MonoBehaviour
{
    public static List<ExplosiveBarrel> ExplosiveBarrelList = new List<ExplosiveBarrel>();


    private void OnDrawGizmos()
    {
        foreach(var barrel in ExplosiveBarrelList)
        {
            Gizmos.DrawLine(transform.position, barrel.transform.position);
        }
    }
}

Handles类 & 预处理器

对于管理类中的Gizmos.DrawLine,我们可以用Handles.DrawAAPolyLine来替换。Handles类有着更为丰富的接口。需要注意的是,Handles类属于UnityEditor,而不属于UnityEngine,因此项目打包不加预处理器会找不到对应的类库

using System.Collections.Generic;
using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
#endif

public class ExplosiveBarrelManager : MonoBehaviour
{
    public static List<ExplosiveBarrel> ExplosiveBarrelList = new List<ExplosiveBarrel>();

    private void OnDrawGizmos()
    {
#if UNITY_EDITOR
        foreach(var barrel in ExplosiveBarrelList)
        {
            Handles.DrawAAPolyLine(transform.position, barrel.transform.position);
        }
#endif
    }
}

贝塞尔曲线 Drawing Bezier Curves

Unity自己的文档有个示例,可以看看。感觉就是3次贝塞尔曲线,startTangent参数就是3次贝塞尔曲线第二个点的位置。

原视频作者也有个视频讲贝塞尔曲线

private void OnDrawGizmos()
{
    foreach(var barrel in ExplosiveBarrelList)
    {

        var height = transform.position.y - barrel.transform.position.y;
        var offset = Vector3.up* height * 0.5f;
        
		Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
        Handles.DrawBezier(transform.position, barrel.transform.position, 
                           transform.position - offset, barrel.transform.position + offset,
                           barrel.Color, EditorGUIUtility.whiteTexture, 1f);
    }
}

Curves and Splines 讲了如何自己画一个贝塞尔曲线。

Material & Mesh Modification Pitfalls

如果我们想修改材质的颜色,我们可以通过下面的代码实现。

//实例化一个material,额外的draw call
// will duplicate the material
GetComponent<MeshRenderer>().material.color = Color;

// will modify the asset
GetComponent<MeshRenderer>().sharedMaterial.color = Color;//修改材质的 asset

但它们都有各自的问题,一个会引入额外的draw call,一个会修改asset。

材质属性块 MaterialPropertyBlock

MaterialPropertyBlock is used by Graphics.DrawMesh and Renderer.SetPropertyBlock. Use it in situations where you want to draw multiple objects with the same material, but slightly different properties. For example, if you want to slightly change the color of each mesh drawn. Changing the render state is not supported.

Note that this is not compatible with SRP Batcher. Using this in the Universal Render Pipeline (URP), High Definition Render Pipeline (HDRP) or a custom render pipeline based on the Scriptable Render Pipeline (SRP) will likely result in a drop in performance.

MaterialPropertyBlock 可以帮我们使用同一个材质(material)渲染不同的物品,并且修改其中的一些属性。

static readonly int shaderPropColor = Shader.PropertyToID("_Color");
private MaterialPropertyBlock materialPropertyBlock;
public MaterialPropertyBlock MaterialPropertyBlock
{
    get
    {
        if (materialPropertyBlock == null) materialPropertyBlock = new MaterialPropertyBlock();
        return materialPropertyBlock;
    }
}

private void ApplyColor()
{
    MeshRenderer meshRenderer = GetComponent<MeshRenderer>();
    MaterialPropertyBlock.SetColor(shaderPropColor, Color);
    meshRenderer.SetPropertyBlock(materialPropertyBlock);
}

调用ApplyColor函数我们就可以通过MaterialPropertyBlock设置shader中属性。

接着我们需要调用ApplyColor函数。

private void OnEnable()
{
    ApplyColor();
    ExplosiveBarrelManager.ExplosiveBarrelList.Add(this);
}

private void OnValidate()
{
    ApplyColor();
}

OnValidate会在脚本的值发生改变,或者脚本被加载的时候调用。

ScriptableObjects

游戏设计中,炸弹桶的类型是有限的,通常我们不会一个个设置炸yao桶的属性,而是可以给予一个类型,比如烈性炸yao桶,小型炸yao桶,被浸湿的炸yao桶。这个可以通过Prefab实现,本次教程,作者使用了ScriptableObjects.

One of the main use cases for ScriptableObjects is to reduce your Project’s memory usage by avoiding copies of values.

You can use the CreateAssetMenu attribute to make it easy to create custom assets using your class

[CreateAssetMenu(menuName = "ScriptableObjects/BarrelType")]
public class BarrelType : ScriptableObject 
{
    [Range(1f, 8f)]
    public float radius = 1;
    public float damage = 5;
    public Color Color = Color.red;
}

序列化

The Inspector window shows the value of the serialized fields of the inspected objects.

字段序列化必须满足特定的条件,比如不能是静态字段(static),字段必须是public或者有SeriazlizeField注解,不能是HashSet等类型。

对于自定义类型的序列化,如果类型派生自UnityEngine.Object并且unity能够保存这个字段(比如MonoBehaviorScriptableObject),Unity会将这个字段序列化为到那个实例的一个引用(通过FileID?)。自己随随便便定义一个类派生自Object,Unity不知道怎么序列化它。

如果类型不派生自UnityEngine.Object,那么这个字段的数据会被内联到引用这个字段的类的序列化的数据里。详情见脚本序列化。

自定义属性面板 Make a Custom Inspector

官方现在强烈推荐使用UI Toolkit来自定义属性面板(新)

这里教程讲的是老方法。用IMGUI自定义属性面板(旧)的文档也可以看看。

Note: It’s strongly recommended to use the UI Toolkit to extend the Unity Editor, as it provides a more modern, flexible, and scalable solution than IMGUI.

新建一个Editor文件夹,这里的代码会归为新的程序集 Assembly-CSharp-Editor ,该程序集不会被打包到构建的游戏中,只在开发阶段使用。不新建文件夹也能运行,但是生成项目时会有问题。

The CustomEditor attribute informs Unity which component it should act as an editor for. The CanEditMultipleObjects attribute tells Unity that you can select multiple objects with this editor and change them all at the same time. Unity executes the code in OnInspectorGUI it displays the editor in the Inspector.

using UnityEditor;

//CustomEditor表示这个Inspector应该作用在哪里类上
[CustomEditor(typeof(BarrelType))]
[CanEditMultipleObjects]
public class BarrelTypeEditor : Editor
{
    public override void OnInspectorGUI()
    {
        GUILayout.Label("Draw your inspector here");
    }
}

IMGUI布局模型分为固定布局(Fixed Layout)和自动布局(Automatic Layout)。如果想使用自动布局,使用GUILayout代替GUI。另外GUIGUILayout在编辑模式和Play Mode都可以使用。EditorGUIEditorGUILayout只能在编辑模式使用。

原作者此时罗列了一些她觉得有用的函数EditorGUILayout.EnumPopupGUILayout.HorizontalSliderGUILayout.BeginHorizontal(HorizontalScope),GUILayout.WidthEditorGUILayout.ObjectField

另外还可以使用GUI.skin,EditorStyles.helpBox等修改样式。

[CustomEditor(typeof(BarrelType))]
[CanEditMultipleObjects]
public class BarrelTypeEditor : Editor
{

    public override void OnInspectorGUI()
    {
        BarrelType barrelType= (BarrelType)target; //The object being inspected.

        barrelType.radius = EditorGUILayout.FloatField("radius", barrelType.radius);
        barrelType.damage = EditorGUILayout.FloatField("damage", barrelType.damage);
        barrelType.Color = EditorGUILayout.ColorField("color", barrelType.Color);
    }
}

SerializedProperty and SerializedObject are classes for editing properties on objects in a completely generic way that automatically handles undo, multi-object editing and Prefab overrides.

上面这样的代码有个坏处是无法Undo。我们可以使用SerializedProperty来实现undo。

Note that flushing data to a Unity object via SerializedObject.ApplyModifiedProperties will not respect any data validation logic you may have in property setters associated with the serialized fields.

[CustomEditor(typeof(BarrelType))]
[CanEditMultipleObjects]
public class BarrelTypeEditor : Editor
{
    SerializedProperty propRadius;
    SerializedProperty propDamage;
    SerializedProperty propColor;

    private void OnEnable()
    {
        propRadius = serializedObject.FindProperty("radius");
        propDamage = serializedObject.FindProperty("damage");
        propColor = serializedObject.FindProperty("Color");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        EditorGUILayout.PropertyField(propRadius);
        EditorGUILayout.PropertyField(propDamage);
        EditorGUILayout.PropertyField(propColor);
        if(serializedObject.ApplyModifiedProperties()){
            // update color
        }
    }
}

ContextMenu 和 MenuItem

ContextMenu

Custom context menu

MenuItem

Menu item example

Editor Window

标签:教程,Color,void,transform,笔记,Unity,position,public
From: https://www.cnblogs.com/dewxin/p/17406067.html

相关文章

  • Unity中Button的调色
    Unity的Button功能在游戏开发中是非常常用的,今天笔者来简单介绍一下Button的调色,以便使其更美观。首先,是之前笔者遇到过的一个问题,在我们新建了一个Button之后,它会像我下面的开始游戏按钮一样,背景呈现出白色,与我们游戏开始菜单的颜色很不符合。 我们可以在Inspector窗口找到No......
  • unity学习日志4(麦扣老师3DRPG项目学习)
    新的APIPhysics.OverLapSphere(位置(Vector3),半径(Float))函数功能:在该位置生成一个球体,返回一个数组(类型为collider),数组存储所有与球体接触和在球体内的拥有碰撞检测的物体链接直达:https://docs.unity.cn/cn/2021.3/ScriptReference/Physics.OverlapSphere.html项目中......
  • [PHP](MD5、sha1)比较漏洞-笔记
    PhP(MD5、sha1)比较漏洞(弱比较、强比较、强碰撞)弱比较md5和sha1弱比较都是利用php解析哈希值以“0E”开头的特性,结果都为0符合参数1的字符串值和参数2的字符串值不相等,但md5值相等。如:240610708,aabg7XSs,aabC9RqS,s878926199a这四段字符串MD5编码后结果分别对应240610708:0E462097......
  • Unity普通项目升级为URP通用渲染管线
    @TOC<hrstyle="border:solid;width:100px;height:1px;"color=#000000size=1">前言我的unity版本是2019.4如果是新建的unity项目,直接创建URP案例工程就可以了<hrstyle="border:solid;width:100px;height:1px;"color=#000000size=1">如果想从普通的工程升级成UR......
  • 06人月神话阅读笔记
    第11章未雨绸缪不变只是愿望,变化才是永恒。——SWIFT。普遍的做法是,选择一种方法,试试看;如果失败了,没关系,再试试别的。不管怎么样,重要的是先去尝试。——富兰克林D.罗斯福。11.1化学工程师很早就认识到,在实验室可以进行的反应过程,并不能在工厂中一步实现。一个被称为“实验性......
  • 002 Python 学习笔记
    #通过:f"内容{变量}"的格式,实现快速格式化,如下示例print("-----------字符串快速格式化------------")name="sunwugang"age=18print(f"mynameis{name},ageis{age}")print("mynameis{},ageis{}".format(name,age)) ......
  • 05人月神话阅读笔记
    第9章削足适履9.1程序有多大?除了运行时间以外,它所占据的空间也是主要开销。当系统设计者认为对用户而言,常驻程序内存的形式比加法器、磁盘等更加有用时,他会将硬件实现中的一部分移到内存上。相反的,其他的做法是非常不负责任的。由于规模是软件系统产品用户成本中如此大的一个......
  • 04人月神话阅读笔记
    第7章为什么巴比伦塔会失败?7.1巴比伦塔项目的失败是因为缺乏交流,以及交流的结果——组织。他们无法相互交谈,从而无法合作。当合作无法进行时,工作陷入了停顿。7.2因为左手不知道右手在做什么,所以进度灾难、功能的不合理和系统缺陷纷纷出现。随着工作的进行,许多小组慢慢地修改自......
  • 01人月神话阅读笔记
    第1章焦油坑1.1编程系统产品(ProgrammingSystemsProduct)开发的工作量是供个人使用的、独立开发的构件程序的九倍。我估计软件构件产品化引起了3倍工作量,将软件构件整合成完整系统所需要的设计、集成和测试又强加了3倍的工作量,这些高成本的构件在根本上是相互独立的。1.2编程......
  • 02人月神话阅读笔记
    第3章外科手术队伍3.1同样有两年经验而且在受到同样的培训的情况下,优秀的专业程序员的工作效率是较差程序员的十倍。(Sackman、Erikson和Grand)需要协作沟通的人员的数量影响着开发成本,因为成本的主要组成部分是相互的沟通和交流,以及更正沟通不当所引起的不良结果(系统调试)。这一......