Godot 学习笔记(2):信号深入讲解

文章目录

  • 前言
  • 相关链接
  • 环境
  • 信号
    • 简单项目搭建
    • 默认的信号
      • 先在label里面预制接收函数
      • 添加信号
    • 自定义无参数信号
      • 为了做区分,我们在label新增一个函数
    • 自定义带参数信号
      • Button代码
      • label代码
      • 连接信号
    • 自定义复杂参数信号
      • 自定义GodotObject类
      • Button
      • Label
      • 连接信号
    • 父传子
      • Callable,信号回调
        • Button
        • Lable
        • 连接信号
        • 参数个数不对的异常问题
        • 解决异常方法
    • 手动连接信号
    • 信号等待
    • Node注入,取代信号
      • 基于Action的信号模拟
        • Button
  • 总结

前言

这里我们深入学习一下Godot的信号。对于数据流的控制一直是前端最重要的内容。

相关链接

Godot Engine 4.2 简体中文文档

环境

  • visual studio 2022
  • .net core 8.0
  • godot.net 4.2.1
  • window 10

信号

信号就是传输数据的一种方式,信号是单向数据流,信号默认是从下往上传递数据的。即子传父

简单项目搭建

在这里插入图片描述

默认的信号

信号的发出和接收是需要配合的,有点像【发布订阅】模式。信号的发布是带有参数的。这里Button是发布者,Lable是订阅者。

在这里插入图片描述

我这里建议先在订阅者一方先新建函数,再链接信号。因为Godot在gdscript中是可以自动新建代码的,但是在.net 中需要我们手动新建代码。

先在label里面预制接收函数

using Godot;
using System;

public partial class Label : Godot.Label
{
	private int num = 0;
	// Called when the node enters the scene tree for the first time.
	public override void _Ready()
	{
		this.Text = "修改";
	}

	// Called every frame. 'delta' is the elapsed time since the previous frame.
	public override void _Process(double delta)
	{
	}

	/// <summary>
	/// 接受按钮点击
	/// </summary>
	public void RecevieButtonDown()
	{
		this.Text = $"{num}";
		num++;
	}
	
}

添加信号

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

自定义无参数信号

我们在Button的代码里面添加信号

using Godot;
using System;

public partial class Button : Godot.Button
{
    // Called when the node enters the scene tree for the first time.
    /// <summary>
    /// 添加自定义信号
    /// </summary>
    [Signal]
    public delegate void MyButtonClickEventHandler();
    public override void _Ready()
    {
        //在按钮按下时添加信号发送
        this.ButtonDown += () => EmitSignal(nameof(MyButtonClick));
    }

    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
    {

    }



}

在这里插入图片描述

为了做区分,我们在label新增一个函数

	/// <summary>
	/// 为了做区分,我们新增一个函数
	/// </summary>
	public void RecevieButtonDown2()
	{
		GD.Print("我是自定义无参信号");
        this.Text = $"{num}";
        num++;
    }

在这里插入图片描述
在这里插入图片描述

自定义带参数信号

这边比较复杂,需要了解C# 的delegate。

C#中委托(delegate)与事件(event)的快速理解

不理解的话那就先凑合着用好了。

Button代码

using Godot;
using System;

public partial class Button : Godot.Button
{
    // Called when the node enters the scene tree for the first time.
    /// <summary>
    /// 添加自定义信号
    /// </summary>
    [Signal]
    public delegate void MyButtonClickEventHandler();

    private int num = 0;

    /// <summary>
    /// 添加带参数型号
    /// </summary>
    [Signal]
    public delegate void AddNumberEventHandler(int number);
    public override void _Ready()
    {
        //我们给AddNumber添加功能,delegate只能添加或者删除函数,有点类似于触发器。
        //每次调用的时候,num自动++
        AddNumber += (item) => num++;
        //在按钮按下时添加信号发送
        this.ButtonDown += () =>
        {
            EmitSignal(nameof(MyButtonClick));
            //触发按钮信号
            EmitSignal(nameof(AddNumber),num);
        };
    }

    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
    {

    }



}

label代码

using Godot;
using System;

public partial class Label : Godot.Label
{
	private int num = 0;
	// Called when the node enters the scene tree for the first time.
	public override void _Ready()
	{
		this.Text = "修改";
	}

	// Called every frame. 'delta' is the elapsed time since the previous frame.
	public override void _Process(double delta)
	{
	}

	/// <summary>
	/// 接受按钮点击
	/// </summary>
	public void RecevieButtonDown()
	{
		this.Text = $"{num}";
		num++;
	}
	/// <summary>
	/// 为了做区分,我们新增一个函数
	/// </summary>
	public void RecevieButtonDown2()
	{
		GD.Print("我是自定义无参信号");
		this.Text = $"{num}";
		num++;
    }

	public void AddNumber(int number)
	{
		this.Text = $"{number}";
		GD.Print($"我是代参数信号,num:[{number}]");
	}

}

连接信号

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

自定义复杂参数信号

在这里插入图片描述

GD0202: The parameter of the delegate signature of the signal is not supported¶

在这里插入图片描述

想要了解更多差异,需要看这个文章。

Godot Engine 4.2 简体中文文档 编写脚本 C#/.NET

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

自定义GodotObject类

using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpSimpleTest.models
{
    public partial class Student:GodotObject
    {
        public string Name = "小王";

        public int Age = 5;
        public Student() { }
    }
}

Button

using CSharpSimpleTest.models;
using Godot;
using System;

public partial class Button : Godot.Button
{
    // Called when the node enters the scene tree for the first time.
    /// <summary>
    /// 添加自定义信号
    /// </summary>
    [Signal]
    public delegate void MyButtonClickEventHandler();

    private int num = 0;

    /// <summary>
    /// 添加带参数型号
    /// </summary>
    [Signal]
    public delegate void AddNumberEventHandler(int number);


    private Student student = new Student() { Name = "小王",Age = 24};


    [Signal]
    public delegate void StudentEventHandler(Student student);  



    public override void _Ready()
    {
        //我们给AddNumber添加功能,delegate只能添加或者删除函数,有点类似于触发器。
        //每次调用的时候,num自动++
        AddNumber += (item) => num++;
        //在按钮按下时添加信号发送
        this.ButtonDown += () =>
        {
            EmitSignal(nameof(MyButtonClick));
            //触发按钮信号
            EmitSignal(nameof(AddNumber),num);
            //触发Student信号
            EmitSignal(nameof(Student),student);
        };
    }

    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
    {

    }



}

Label

在这里插入图片描述

using CSharpSimpleTest.models;
using Godot;
using System;

public partial class Label : Godot.Label
{
	private int num = 0;
	// Called when the node enters the scene tree for the first time.
	public override void _Ready()
	{
		this.Text = "修改";
	}

	// Called every frame. 'delta' is the elapsed time since the previous frame.
	public override void _Process(double delta)
	{
	}

	/// <summary>
	/// 接受按钮点击
	/// </summary>
	public void RecevieButtonDown()
	{
		this.Text = $"{num}";
		num++;
	}
	/// <summary>
	/// 为了做区分,我们新增一个函数
	/// </summary>
	public void RecevieButtonDown2()
	{
		GD.Print("我是自定义无参信号");
		this.Text = $"{num}";
		num++;
    }

	public void AddNumber(int number)
	{
		this.Text = $"{number}";
		GD.Print($"我是代参数信号,num:[{number}]");
	}

	/// <summary>
	/// 自定义复杂参数
	/// </summary>
	/// <param name="student"></param>
	public void ReviceStudent(Student student)
	{
		this.Text = $"student:Name[{student.Name}],Age[{student.Age}]";
	}

}

连接信号

在这里插入图片描述

至于对于的显示逻辑,是基于C# Variant这个类

C# Variant

在这里插入图片描述

在这里插入图片描述

父传子

Callable,信号回调

[教程]Godot4 GDscript Callable类型和匿名函数(lambda)的使用

在这里插入图片描述

在这里插入图片描述

Button
using Godot;
using System;
using System.Diagnostics;

public partial class test_node : Node2D
{
    // Called when the node enters the scene tree for the first time.

    private Label _lable;

    private Button _button;

    private int num = 0;

    [Signal]
    public delegate int NumAddEventHandler();



    public override void _Ready()
    {
        _lable = this.GetNode<Label>("Label");

        _button = this.GetNode<Button>("Button");

        _lable.Text = "修改";

        _button.ButtonDown += _button_ButtonDown;
        NumAdd += () => num;
    }

    public void _button_ButtonDown()
    {
        _lable.Text = $"按下修改{num}";
        GD.Print($"按下修改{num}");
        num++;

    }

    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
    {
        
    }


}

Lable
using CSharpSimpleTest.models;
using Godot;
using System;

public partial class Label : Godot.Label
{
    private int num = 0;
    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
        this.Text = "修改";
    }

    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
    {
    }

    /// <summary>
    /// 接受按钮点击
    /// </summary>
    public void RecevieButtonDown()
    {
        this.Text = $"{num}";
        num++;
    }
    /// <summary>
    /// 为了做区分,我们新增一个函数
    /// </summary>
    public void RecevieButtonDown2()
    {
        GD.Print("我是自定义无参信号");
        this.Text = $"{num}";
        num++;
    }

    public void AddNumber(int number)
    {
        this.Text = $"{number}";
        GD.Print($"我是代参数信号,num:[{number}]");
    }

    /// <summary>
    /// 自定义复杂参数
    /// </summary>
    /// <param name="student"></param>
    public void ReviceStudent(Student student)
    {
        this.Text = $"student:Name[{student.Name}],Age[{student.Age}]";
    }

    public void CallBackTest(Callable callable, Callable callable2)
    {
        callable.Call();
        callable2.Call(23);

    }

}

连接信号

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

参数个数不对的异常问题
public void CallBackTest(Callable callable, Callable callable2)
{
    try
    {
        callable.Call();
        //callable2.Call(23);

        //如果我们参数个数不对,也不会在C#中抛出异常,会在Godot中抛出异常
        callable2.Call();
    }
    catch (Exception e)
    {
        GD.Print("发送异常");
        GD.Print(e.ToString());
    }
    

}

在这里插入图片描述
在这里插入图片描述
这是个十分危险的使用,因为我们无法溯源对应的代码,也无法try catch找到异常的代码,因为这个代码是在C++中间运行的。

解决异常方法

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

手动连接信号

由于Godot 对C# 的支持不是很够,所以我们点击Go to Method的时候,是不能直接跳转到对应的代码的。
在这里插入图片描述

    private Timer timer;

    private Godot.Button btn;
    public override void _Ready()
    {
        //先获取信号
        btn = GetNode<Button>("../Button");
        //再手动接受信号
        btn.Connect("Student",new Callable(this,nameof(ReviceStudent)));
    }

详细可以看官方文档的最佳实践
在这里插入图片描述

信号等待

在这里插入图片描述

在这里插入图片描述

using CSharpSimpleTest.models;
using Godot;
using System;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;

public partial class Label : Godot.Label
{
    private int num = 0;
    // Called when the node enters the scene tree for the first time.

    private Timer timer;
    public override void _Ready()
    {
        //获取Timer
        timer = GetNode<Timer>("Timer");
        //启动Timer
        timer.Start();
        this.Text = "修改";
        WaitTimeout();
    }

    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
    {
    }

    /// <summary>
    /// 接受按钮点击
    /// </summary>
    public void RecevieButtonDown()
    {
        this.Text = $"{num}";
        num++;
    }
    /// <summary>
    /// 为了做区分,我们新增一个函数
    /// </summary>
    public void RecevieButtonDown2()
    {
        GD.Print("我是自定义无参信号");
        this.Text = $"{num}";
        num++;
    }

    public void AddNumber(int number)
    {
        this.Text = $"{number}";
        GD.Print($"我是代参数信号,num:[{number}]");
    }

    /// <summary>
    /// 自定义复杂参数
    /// </summary>
    /// <param name="student"></param>
    public void ReviceStudent(Student student)
    {
        this.Text = $"student:Name[{student.Name}],Age[{student.Age}]";
    }

    public void CallBackTest(Callable callable, Callable callable2)
    {
        callable.Call();

        //throw new Exception("error");
        //callable2.Call(23);

        //如果我们参数个数不对,也不会在C#中抛出异常,会在Godot中抛出异常

        callable2.Call();

    }


    public async Task WaitTimeout()
    {
        while (true)
        {
            await ToSignal(timer, Timer.SignalName.Timeout);
            GD.Print($"收到Timer信号,num[{num}]");
            this.Text = $"{num}";
            num++;
        }
        
    }

}

在这里插入图片描述

Node注入,取代信号

信号最大的问题就是:

  • 入参不固定
  • godot.net 对C# 支持力度不够
  • 编译报错在外部C++代码,Debug难度大
  • 不符合OOP的编程逻辑

比如我们在Button.cs中添加如下属性

    /// <summary>
    /// 新增的姓名
    /// </summary>
    public string MyName = "我是Button";

在这里插入图片描述
我们就可以在Lable中拿到这个属性

在这里插入图片描述
在这里插入图片描述

基于Action的信号模拟

Button

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
但是这样有个问题,Action需要初始化,不然会报空异常

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
当然,也可以使用event Action,因为event Action是不允许在外部重写的。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以event Action 是最优的写法,是最不会出现问题的。

在这里插入图片描述

总结

信号就是Godot中数据沟通方式。信号的出现就是为了将复杂的数据处理简单化为接口的形式。再加上Godot中的Sence,这个就有利于我们面向对象的编程习惯。

但是信号是没有参数的声明的,而且参数出现问题会外部抛出异常,所以我们最好就使用event Action 回调来代替信号。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/472062.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何查看zip文件的MD5码

目录 Windows macOS 和 Linux 要查看zip文件的MD5码&#xff0c;你可以使用不同的方法&#xff0c;具体取决于你使用的操作系统。以下是一些常见平台的指导&#xff1a; Windows 可以使用PowerShell来计算文件的MD5码。打开PowerShell&#xff0c;然后使用以下命令&#xf…

wireshark 使用实践

1、打开wireshark软件&#xff0c;选择网卡&#xff0c;开始抓包 2、打开浏览器&#xff0c;访问一个http网站&#xff1a;这里我用 【邵武市博物馆】明弘治十一年&#xff08;1498&#xff09;铜钟_文物资源_福建省文 测试&#xff0c;因为它是http的不是https&#xff0c;方…

基于深度学习的场景文本检测

CTPN 简介&#xff1a; 基于目标检测方法的文本检测模型&#xff0c;在Faster RCNN的基础上进行了改进&#xff0c;并结合双向LSTM增强了序列提取特征&#xff0c;通过anchor和gt的设计将文本检测任务转化为一连串小尺度文本框的检测。 解决问题&#xff1a; 文本长短不一&…

jenkins gradle 编译时jvm不足情况

gradle 编译时jvm不足情况 #开启线程守护&#xff0c;第一次编译时开线程&#xff0c;之后就不会再开了org.gradle.daemontrue#配置编译时的虚拟机大小org.gradle.jvmargs-Xmx2048m -XX:MaxPermSize512m -XX:HeapDumpOnOutOfMemoryError -Dfile.encodingUTF-8#开启并行编译&…

JL15-400/11过电流继电器 400A 一开一闭 380V 柜内安装JOSEF约瑟

系列型号 JL15-1.5/11电流继电器JL15-2.5/11电流继电器 JL15-5/11电流继电器JL15-10/11电流继电器 JL15-15/11电流继电器JL15-20/11电流继电器 JL15-30/11电流继电器JL15-40/11电流继电器 JL15-60/11电流继电器JL15-80/11电流继电器 JL15-100/11电流继电器JL15-150/11电流…

顺序表的动态分配基本操作

#include <stdio.h> #include <stdlib.h>// 顺序表存储空间动态分配 #define InitSize 10 // 顺序表初始长度 typedef int ElemType; // int类型重命名为ElemType&#xff0c;方便后续调整typedef struct { // 定义结构体ElemType *data; // 用静…

LeetCode 刷题 --- 快速幂

前言&#xff1a; 幂运算是一种常见的运算&#xff0c;求取a^n,最容易想到的方法便是通过循环逐个累乘&#xff0c;其复杂度为O(n)&#xff0c;这在很多时候是不够快的&#xff0c;所以我们需要一种算法来优化幂运算的过程。 快速幂&#xff0c;二进制取幂&#xff08;Binary…

Transformer的前世今生 day03(Word2Vec、如何使用在下游任务中)

前情回顾 由上一节&#xff0c;我们可以得到&#xff1a; 任何一个独热编码的词都可以通过Q矩阵得到一个词向量&#xff0c;而词向量有两个优点&#xff1a; 可以改变输入的维度&#xff08;原来是很大的独热编码&#xff0c;但是我们经过一个Q矩阵后&#xff0c;维度就可以控…

express+mysql+vue,从零搭建一个商城管理系统16--收货地址(全国省市县名称和code列表)

提示&#xff1a;学习express&#xff0c;搭建管理系统 文章目录 前言一、新建config/area.js二、新建models/address.js三、新建dao/address.js四、新建routes/address.js五、添加地址六、查询用户地址列表总结 前言 需求&#xff1a;主要学习express&#xff0c;所以先写serv…

ANOMALY TRANSFORMER: TIME SERIES ANOMALY DETECTION WITH ASSOCIATION DISCREPANCY

论文题目&#xff1a; ANOMALY TRANSFORMER: TIME SERIES ANOMALY DETECTION WITH ASSOCIATION DISCREPANCY 发表会议&#xff1a;ICLR 2022 论文地址&#xff1a;https://openreview.net/pdf?idLzQQ89U1qm_ 论文代码&#xff1a;https://github.com/thuml/Anomaly-Transforme…

七、Java中SpringBoot组件集成接入【Minio文件服务器】

七、Java中SpringBoot组件集成接入【Minio文件服务器】 1.Minio介绍2.搭建Minio服务2.1Windows部署2.2Linux部署2.3docker部署 3.Minio可视化操作4.SpringBoot接入Minio1.添加maven依赖2.yaml配置文件3.配置类4.工具类5.控制类 5.常见问题6.其他参考文章 1.Minio介绍 对象存储…

设计模式深度解析:适配器模式与桥接模式-灵活应对变化的两种设计策略大比拼

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 适配器模式与桥接模式-灵活应对变化的两种设计策略大比拼 探索设计模式的魅力&#xff1a;深入了…

Day16-【Java SE进阶】IO流(二):字符流、缓冲流、转换流、打印流、数据流、序列化流、IO框架

一. 字符流 字节流&#xff1a;适合复制文件等&#xff0c;不适合读写文本文件 字符流&#xff1a;适合读写文本文件内容 1. FileReader(文件字符输入流) 作用:以内存为基准&#xff0c;可以把文件中的数据以字符的形式读入到内存中去。读取单个字符&#xff0c;性能比较差&a…

【位运算】【 数学】【 哈希映射】2857. 统计距离为 k 的点对

本文涉及知识点 位运算 数学 哈希映射 LeetCode 2857. 统计距离为 k 的点对 给你一个 二维 整数数组 coordinates 和一个整数 k &#xff0c;其中 coordinates[i] [xi, yi] 是第 i 个点在二维平面里的坐标。 我们定义两个点 (x1, y1) 和 (x2, y2) 的 距离 为 (x1 XOR x2) …

PMP备考心得 | 策略与技巧大揭秘

1.理解考试大纲&#xff1a;首先&#xff0c;你需要熟悉PMP考试的内容和结构。PMI官网提供了详细的考试大纲&#xff0c;包括项目管理的五个过程组&#xff08;启动、规划、执行、监控、收尾&#xff09;和十个知识领域&#xff1b; 2.制定学习计划&#xff1a;根据个人的时间…

【C语言】基本语法知识C语言函数操作符详解

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;C语言_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.基本语法 1.1 代码解释 1.1.1 main()主函数 1.1.2 int 1.1.3 { } 1.1.4 printf()库函数 1.1.5 stdio.h头文件 1.2 C语言的…

突破边界:Web3开启数字化社会的新纪元

引言 随着科技的不断进步和数字化社会的发展&#xff0c;Web3正逐渐成为了人们关注的焦点。作为新一代互联网的演进形态&#xff0c;Web3具有突破传统边界、实现去中心化的特点&#xff0c;被认为将开启数字化社会的新纪元。本文将深入探讨Web3的概念、特点、应用场景&#xf…

VueUse常见方法使用

npm i vueuse/core 1、useDebounceFn 节流防抖 import { useDebounceFn } from vueuse/core<button type"button" class"search" click"query">查询</button>// 查询 获取table数据const query2 async () > {try {const res …

MySQL 多表查询强化练习

环境准备 create table dept(id int PRIMARY KEY,dname VARCHAR(50),loc VARCHAR(50) ); insert into dept values (10,研发部,北京), (20,学工部, 上海), (30,销售部,广州 ), (40,财务部,深圳);create table job(id int PRIMARY KEY,jname VARCHAR(20),descripition VARCHAR(…

latex如何对一段文本进行加粗

在LaTeX中&#xff0c;你可以使用\textbf{}命令来对一段文本进行加粗。例如&#xff1a; \textbf{这是要加粗的文本}这将会把"这是要加粗的文本"这段文字显示为加粗。 如果你想要对整段文本进行加粗&#xff0c;你可以将整段文本都放在\textbf{}命令中&#xff0c;…