Unity Visual Scripting(可视化脚本) 自定义节点 踩坑教程

前言

最近开始尝试做一些游戏DEMO,对于包含大量对话、3D场景各类触发等需要大量非程序配置的部分,通过传统配置表会非常复杂且不易读,以往会选择使用lua等脚本语言由策划配置,这次尝试使用可视化脚本配置,看看是否可以更方便直观的完成配置。

整个游戏使用可视化脚本开发不太现实,期望是底层和主要游戏逻辑使用C#编写,提供高级接口供可视化脚本调用。这样配置简单,逻辑上也比较可控。

比如玩家走进一个区域弹出对话框,可视化脚本中只需要监听玩家走进区域,并直接调用播放对话:

这么做肯定少不了编写大量的自定义事件和节点,但是除了官方教程外,网络上几乎没有其他关于Visual Scripting 自定义节点的教程,果不其然踩了不少坑。于是写一篇教程式的文章完善这方面的空白。

这篇文章是建立在你已经熟读官方教程的前提下编写的。

项目结构

创建一个EventHooks.cs/EventNames.cs 文件,存放所有的自定义事件名:

c# 复制代码
public static class EventHooks
{
    // Bolt
    public const string Custom = nameof(Custom);

    // Global
    public const string OnGUI = nameof(OnGUI);
}

创建一个动态调整参数数量的节点

Visual Scripting并不通过反射属性来构建节点,而是通过执行Definition方法逻辑来决定节点的构建形式。通过为节点增加一个count参数,就可以根据count值动态改变输出端口数量。

c# 复制代码
public class MultiOutput : Unit
{
	[SerializeAs(nameof(count))]
	private int _count = 2;
	[DoNotSerialize]
	public ControlInput enter { get; private set; }
	[DoNotSerialize]
	[Inspectable, InspectorLabel("Count"), UnitHeaderInspectable("Count")]
	public int count
	{
		get => _count;
		set => _count = Mathf.Clamp(value, 1, 10);
	}
	[DoNotSerialize]
	public ReadOnlyCollection<ControlOutput> multiOutputs { get; private set; }

	protected override void Definition()
	{
		enter = ControlInputCoroutine(nameof(enter), flow => multiOutputs[0]);
		var _multiOutputs = new List<ControlOutput>();
		multiOutputs = _multiOutputs.AsReadOnly();
		for (var i = 0; i < count; i++)
		{
			var output = ControlOutput(i.ToString());
			Succession(enter, output);
			_multiOutputs.Add(output);
		}
	}
}

EventUnit需要TArgs泛型用于参数类型,但我不想传参数怎么办

泛型类型使用EmptyEventArgs即可。

全局事件节点

官方教程中的EventUnit示例就是全局广播的,官方也提供了封装好的全局事件类GlobalEventUnit:

c# 复制代码
public class OnGlobalEvent : GlobalEventUnit<EmptyEventArgs>
{
    protected override string hookName => EventNames.OnGlobalEvent;
}

指定Target的事件节点

如果想要仅触发某个gameObject上的某一个事件,则需要使用封装好的GameObjectEventUnit:

c# 复制代码
public class OnObjectEvent : GameObjectEventUnit<EmptyEventArgs>
{
    public override Type MessageListenerType => null;
    protected override string hookName => EventNames.OnObjectEvent;
}

运行时动态添加脚本监听的事件节点

实现MessageListener类(这个类会在运行时挂载到Graph所在的物体上):

c# 复制代码
public class UnityOnPlayerEnterColliderMessageListener : MessageListener
{
        private void OnTriggerEnter(Collider other)
        {
                if (Game.Instance.player.GetComponent<Collider>() == other)
                {
                        EventBus.Trigger(EventNames.OnPlayerEnterCollider, gameObject);
                }
        }
}

然后在GameObjectEventUnit中指定MessageListenerType:

c# 复制代码
public class OnPlayerEnterCollider : GameObjectEventUnit<EmptyEventArgs>
    {
        public override Type MessageListenerType => typeof(UnityOnPlayerEnterColliderMessageListener);
        protected override string hookName => EventNames.OnPlayerEnterCollider;
    }

为什么调用了EventBus.Trigger但是没有触发对应的事件

事件名、target、参数必须和定义严格匹配才会执行事件回调。比如:

没有使用GameObjectEventUnit,调用带target的EventBus.Trigger(EventNames.SomeEvent, gameObject)不会有任何事件触发

GameObjectEventUnit<int>指定了参数类型是int,调用不带参数的EventBus.Trigger(EventNames.SomeEvent, gameObject)不会有任何事件触发

自定义事件节点只有放在Events分类下时才会显示

c# custom UnitCategory for events

放在其他分类下,不会在Fuzzy Finder中显示(但是可以搜索到并使用)

(除了这个帖子没有任何其他地方提到这个事情......查了好久

给自定义节点更换Icon

找到你想使用的图标对应的节点:

使用TypeIcon注解:

kotlin 复制代码
[TypeIcon(typeof(SelectUnit))]
public class MyEvent : Unit

Icon只能使用官方图标库中的图标

参考资料

本文内容主要通过学习[email protected]/Runtime代码编写。

相关推荐
Thomas游戏开发4 天前
Unity3D光照层级与动态切换指南
前端框架·unity3d·游戏开发
Thomas游戏开发15 天前
Unity3D 崩溃分析工具的集成与优化
前端框架·unity3d·游戏开发
Thomas游戏开发19 天前
Unity3D网格简化与LOD技术详解
前端框架·unity3d·游戏开发
Thomas_YXQ20 天前
Unity3D 图形渲染(Graphics & Rendering)详解
开发语言·unity·图形渲染·unity3d·shader
Thomas游戏开发22 天前
Unity3D 图形渲染(Graphics & Rendering)详解
前端·unity3d·游戏开发
Thomas游戏开发22 天前
Unity3D 光栅化 vs 光线追踪:技术详解
前端框架·unity3d·游戏开发
Thomas游戏开发23 天前
Unity3D 多线程与协程优化详解
前端框架·unity3d·游戏开发
Thomas_YXQ1 个月前
Unity3D Cinemachine 高级应用详解
数码相机·unity·面试·职场和发展·unity3d·游戏开发
Thomas_YXQ1 个月前
Unity3D 战斗系统架构与设计详解
开发语言·unity·架构·系统架构·unity3d