学习笔记 —— 入门Godot C#开发 —— 信号篇

关于

我是一个Godot初学者,了解Godot的一些基础知识但苦于不懂C#,故写此博文来记录自己的学习过程。
如有错误,还请指正。


什么是信号?

信号 signal 是Godot提供的用于解耦节点与节点的方法。它是观察者模式的一种良好实现。

本文仅涉及使用C#与Godot的信号系统交互的过程。使用GDScript与信号交互的过程,请参考官方文档

如何定义信号?

C#使用“委托” delegate来处理信号连接。例如,

using Godot;
using System;

public partial class LearnSignal : Node
{
	[Signal] public delegate void TestSignalEventHandler();
}

在我们的例子里,我们在类LearnSignal中,定义了一个名为TestSignal的信号。
可能有些难以理解。但,在那之前,不如让我们来了解了解什么是“委托”吧。


那什么是Delegate?

在C#中,委托(delegate)是一种类型,它安全地封装了一个方法的引用。它类似于函数指针,但更安全和灵活。委托可以指向一个或多个方法,并且可以在运行时动态地更改指向的方法。这使得委托成为实现回调和事件监听等设计模式的理想选择。

在Godot里,信号系统在C#中的实现,也是基于上述delegate机制。

如何定义Delegate?

委托通过delegate关键字定义,后面跟着返回类型、委托名称以及参数列表(如果有的话)。
例如,

using Godot;
using System;

// 学习 C# 中“委托”机制。此为单委托
public partial class LearnSignal : Node
{
    // 定义一个委托,名为MyDelegate,接受参数string message,返回值void
    public delegate void MyDelegate(string message);

    // 打印消息。这是用于被委托调用的方法
    public void ShowMessage(string msg)
    {
        GD.Print("ShowMessage: " + msg);
    }

    // 接下来,在_Ready()方法中,我们创建一个委托实例,并将ShowMessage方法作为参数传递给它。
    public override void _Ready()
    {
        // 创建委托实例
        MyDelegate myDelegate = new MyDelegate(ShowMessage);

        // 调用委托实例
        myDelegate("Hello World");
    }
}

// 学习多委托机制
public partial class LearnSignal : Node
{
    // 写几个函数来调用吧
    void Func1(string msg)
    {
        GD.Print("调用了Func1" + msg);
    }
    void Func2(string msg)
    {
        GD.Print("调用了Func2" + msg);
    }
    void Func3(string msg)
    {
        GD.Print("调用了Func3" + msg);
    }
    void Func4(string msg)
    {
        GD.Print("调用了Func4" + msg);
    }


    // 我们可以在EnterTree里定义多个委托实例,然后制作“组合委托”
    public override void _EnterTree()
    {
        MyDelegate delegate1 = Func1;
        MyDelegate delegate2 = Func2;
		MyDelegate delegate3 = Func3;
		MyDelegate delegate4 = Func4; // 创建了四个委托实例

        // 组合它们吧。我们定义的委托可以被简单的“+”操作符组合
		// 使用 + - 操作符可以添加或者移除委托。
		MyDelegate multidelegate = delegate1 + delegate2 + delegate3 + delegate4;


		// 另一种定义方式是:
		multidelegate = new MyDelegate(Func1);
		multidelegate += Func2;
		multidelegate += Func3;
		multidelegate += Func4;
		
        // 调用它吧,这将同时调用Func1,Func2,Func3,Func4
		// 顺带一提,委托是引用传递的,所以你可以在任何地方添加或者移除委托
        multidelegate("Delegate Hello World");
    }
}

总而言之,你可以把委托当成一个盒子。你可以直接把满足条件的函数塞进去,在想要的时候调用委托就会执行这盒子里的所有函数。

  • 你可以为一个委托添加多个函数——你甚至可以多次把一个函数连接到委托。
  • 你所加入的函数会按“先进入先调用”的顺序被调用。
  • 你可以使用-来断开函数和委托的连接。这会优先移除委托链链尾的函数,也就是后进入委托链的方法。
  • 尝试移除委托中一个不存在的函数不会有任何异常和错误发生,你可以放心使用-操作符。

有了委托,我们就可以去了解Godot的信号机制了。


开始了解Godot的信号吧

Godot的C#信号基于Delegate,但多了一些限制:

  • 你的委托必须以"EventHandler"结尾。
  • 你的委托接受的参数必须与Variant兼容,或继承自GodotObject

举个例子:我们定义了简单的信号,但它好像并不能运行。
在这里插入图片描述

这是为什么呢?答案很明显,我们定义的委托/信号,没有以“EventHandler”结尾!让我们修改一下代码:

在这里插入图片描述

看,我们的引擎正确识别了我们的信号。

我们定义了MyDelegateEventHandler, 引擎正确的将我们的信号的名称识别为了“MyDelegate”,也识别了它接受的参数。

连接信号至函数

信号本质上就是Delegate。

不过,为了让Godot满意,我们最好用Godot推荐的办法来连接信号。

using Godot;
using System;

// 学习 C# 与 Godot 信号的对接
public partial class LearnSignal : Node
{
	[Signal]
	public delegate void MyDelegateEventHandler(string message);
	
	// 定义3个用于被信号连接的函数
	void Func1(string msg) {
		GD.Print("Func 1 Message:" + msg);
	}
	void Func2(string msg) {
		GD.Print("Func 2 Message:" + msg);
	}
	void Func3(string msg) {
		GD.Print("Func 3 Message:" + msg);
	}
	
	public override void _Ready(){
		MyDelegate += Func1; // 连接信号MyDelegate到Func1
		MyDelegate += Func2; // 连接信号MyDelegate到Func2
		MyDelegate += Func3; // 连接信号MyDelegate到Func3
		
		EmitSignal(nameof(MyDelegate), "Hello World!"); // 发射信号
	}
}

有注意到吗?我们没有显式声明“MyDelegate”这个变量,但我们能直接使用它!

这得益于我们先前使用的[Signal]修饰。在这种语境下,C#会自动为我们生成一个名为“MyDelegate”的Event变量。

这也是为什么要有“EventHandler”的限制的原因之一。

只有遵守规则,才能让戈多的“魔法”正常工作。

等待信号 (await)

如果要用await等待我们刚定义的信号,使用以下方法:

await ToSignal(this, SignalName.MyDelegate);	

请不要搞混 SignalName.MyDelegate 和 MyDelegate。ToSignal方法用于制作Awaiter供await方法使用。详情还请见文档。

发射信号

发射信号可以用EmitSignal方法来使用。

EmitSignal(nameof(MyDelegate), "Hello World!"); // 发射信号
EmitSignal("MyDelegate", "Hello World!"); // 这么写也行,但是不推荐

我们输入MyDelegate,这个刚刚生成的Event的名称,以及为信号提供的参数,使用EmitSignal就可以释放该信号了。很简单。

Invoke 不能被用来触发与 Godot 信号绑定的事件,这与其他C#事件不同。(尽管我不知道这是什么意思…)

还有更多…

动态创建信号、异步Await、诸如此类,还有很多本文没提及的内容。我会在后续的文章里补充。

今天的学习到此为止吧。重点了解了C#中委托机制的用法——它作为观察者模式的一个实现,相当的灵活且可靠。后续我大概还会继续探索它的其他用法吧。

本文是我作为初学者的学习笔记。如有错误,还请指正。

### Godot 游戏引擎入门知识 Godot 是一款开源的跨平台游戏引擎,以其易学易用的特点受到广泛欢迎。无论是初学者还是有一定经验的游戏开发者,都可以通过学习 Godot 来提升自己的开发能力。 #### 一、Godot 的优势 Godot 引擎具备许多显著的优点,使其成为新手的理想选择。它不仅提供了直观的界面和强大的功能集,还拥有活跃的社区支持[^3]。以下是其主要特点: - **开放源码**:Godot 完全免费且开源,允许用户自由修改和分发。 - **跨平台支持**:能够轻松导出到 Windows、Linux、macOS、Android 和 iOS 平台。 - **可视编辑器**:内置场景编辑器让开发者可以通过拖拽操作快速构建复杂的游戏结构。 - **脚本语言灵活性**:除了默认的 GDScript 外,还可以使用 C# 或其他第三方插件进行开发。 #### 二、Godot 新手入门指南 针对刚接触 Godot开发者,可以从以下几个方面入手: 1. **安装与配置** 首先下载最新稳定版的 Godot 编辑器,并熟悉基本的操作流程。官方文档中有详尽的安装说明可供查阅[^1]。 2. **理解核心概念** 学习节点(Node)的概念及其在场景树(Scene Tree)中的作用至关重要。每一个游戏对象都由多个节点组成,它们共同协作完成特定功能[^3]。 3. **编写简单程序** 尝试创建一个小项目练习基础技能,比如移动角色或者检测碰撞事件。这里有一段简单的 GDScript 示例用于控制物体沿 X 轴平移: ```gdscript extends Node2D var speed = 200 func _process(delta): position.x += speed * delta ``` 4. **深入研究资源管理** 探索如何导入图片、音频文件等外部资产,并学会调整属性以适应不同设备上的表现效果[^5]。 5. **实践案例分析** 参考现成的例子可以帮助更快地上手实际应用。例如,《老程序员的数字游戏开发笔记》系列文章展示了完整的 3D 游戏制作过程,其中涉及物理模拟、动画处理等多个高级话题[^4]。 6. **利用在线资料** 不要忽视来自世界各地的学习材料价值。像《学习Godot引擎——阿拉伯语教程》这样的本地化内容能为母语非英语用户提供极大便利;同时也可以关注英文论坛和技术博客获取前沿资讯[^2]。 7. **参与交流活动** 加入相关 QQ 群组或是 Discord 频道,在与其他爱好者的互动过程中解决问题并分享心得体验。 --- ### 总结 综上所述,进入 Godot 生态圈并不困难,只需循序渐进地掌握必要的理论知识并通过不断尝试积累实战经验即可逐步成长为一名合格甚至优秀的独立游戏制作者!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值