使用引擎多少会需要自己搓一些工具
Godot给出的方案是通过插件来扩展引擎的功能
但受制于脚本的功能性,通过C#脚本来编写插件时会遇到一些问题
(或许通过GDExtension扩展会好一点)
本文采用C#格式的API
判断脚本类型
一般来说,插件通过GodotObject传递原始对象
理论上我们可以通过GodotObject.GetClass()获取对象
但由于脚本添加的自定义类型本质上是在GodotObject上附加了这个脚本
附加的脚本不会在Godot的ClassDB中添加新的元数据
也就是说没有改变这个GodotObject的实际类型
这会导致GodotObject.GetClass()返回的是基类类型
比如说下面的代码
[GlobalClass]
[Tool]
public partial class TestCamera : Camera3D
{
// ...
}
那么在插件上下文中,通过GetClass()返回的是Camera3D的类型
这会导致在制作查看器插件或者Gizmo插件时,无法通过这种方式对类型的判断
进而使得查看器插件的_CanHandle与Gizmo插件的_HasGizmo方法不是很好写
还是有办法解决这个问题的
通过比较脚本类型即可
以_CanHandle为例
public partial class TestCameraInspector : EditorInspectorPlugin
{
// ...
public override bool _CanHandle(GodotObject o)
{
var script = GD.Load<Script>("path of script");
return script == o.GetScript().AsGodotObject();
}
// ...
}
这样就可以判断脚本类型了
但如果说要扩展脚本功能,那么这个方法就不能判断了
毕竟相当于附加的脚本变了
这种情况可能直接判断基类型或者通过添加元数据判断更好
与脚本对象交互
由于上面的问题,导致GodotObject没法直接转换为需要的脚本问题
只能直接转换为继承基类型
比如上面的TestCamera,在插件给的GodotObject只能强制转型到Camera3D
那插件怎么传递数据到脚本对象呢
只能通过动态调用的方法实现
在GodotObject中,动态调用相关的主要有下面的方法
Get()可以获取某个属性值Set()可以设定某个属性值Call()可以调用某个方法 还有其他的一些查询方法与衍生方法
动态调用是可以调用脚本方法的,只要脚本在生存期内
在编写插件的场景中,也就是脚本要能在编辑器中运行,记得给脚本带[Tool]特性即可
下面举个例子
[GlobalClass]
[Tool]
public partial class TestCamera : Camera3D
{
public void TestMethod(int i)
{
GD.Print(i);
}
}
public partial class TestCameraInspector : EditorInspectorPlugin
{
// ...
// 这里给TestCamera栏添加一个按钮来触发这个TestMethod
public override void _ParseCategory(GodotObject o, string category)
{
if (category != "TestCamera") return;
var buttonTest = new Button()
{
Text = "Test"
};
buttonTest.ButtonUp += () => {o.Call("TestMethod", 233)};
AddCustomControl(buttonTest);
}
// ...
}
隐藏某个属性
有时候我们需要序列化一个属性,但希望只通过脚本内部管理
Godot本身没有这样的设置,[Export]默认是会把这个属性放在编辑器里的
这时候可以通过检查器插件来实现
[GlobalClass]
[Tool]
public partial class TestCamera : Camera3D
{
[Export]
public int HideProperty{ get;set; }
}
public partial class TestCameraInspector : EditorInspectorPlugin
{
// ...
public override bool _ParseProperty(
GodotObject @object, Variant.Type type, string name, PropertyHint hintType, string hintString,
PropertyUsageFlags usageFlags, bool wide)
{
return name == "HideProperty";
}
// ...
}
这样就实现了对HideProperty的隐藏
原理是_ParseProperty同返回值判断是否替换原有的控件
如果返回为true,则说明需要替换
但这里没有定义新的控件,那么就替换为空了