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

news2024/11/25 5:45:48

文章目录

  • 前言
  • 相关链接
  • 环境
  • 信号
    • 简单项目搭建
    • 默认的信号
      • 先在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 回调来代替信号。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国内超好用且免费的 AI 写作工具有哪些?

目前&#xff0c;市面上这类AI工具实在是太多了&#xff0c;比如依托于清华大学开发的智谱清言&#xff0c;或亦是百度的文心一言&#xff0c;还是阿里云的通义千问&#xff0c;这些AI工具在功能是类似的&#xff0c;但是依托于大模型的不同&#xff0c;可能回答的结果迥然不同…

基于springboot+vue的在线互动学习网站

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

(C语言)memcpy函数详解与模拟实现

目录 1. memcpy函数详解 情况一&#xff1a; 情况二&#xff1a; 情况三&#xff1a; 情况四&#xff1a; 情况五&#xff1a; 2. memcpy模拟实现 2.1 重叠情况的讨论&#xff1a; 2.2 memcpy函数超出我们的预期 1. memcpy函数详解 头文件<string.h> 这个函数和…

canoe 以太网配置

canoe vn5620 以太网配置 首先电脑连接上VN5620&#xff0c;配置了License才能进行端口设置 从开始菜单中打开Vector Hardware Configuration 进入到配置页面&#xff0c;会出现VN5620&#xff0c;选择进行右击&#xff0c;点击第一个选项Ethernet device configuration ca…

Open World Object Detection in the Era of Foundation Models

Open World Object Detection in the Era of Foundation Models 摘要介绍相关工作开放词汇物体检测开放世界目标检测类无关的目标检测3.真实世界目标检测基准3.1 数据集细节3.2 基准架构3.3 什么是一个未知对象4. 利用基准模型用于开放世界目标检测4.1 背景4.2 属性生成4.3 属性…

SD3刚发布不久,最新的SD3-Turbo就来了:只需要4步就能超过MidjourneyV6!

StabilityAI刚刚发布了SD3-Turbo的论文&#xff1a;Fast High-Resolution Image Synthesis with Latent Adversarial Diffusion Distillation。 SD3-Turbo采用了一种新的蒸馏方法&#xff1a;Latent Adversarial Diffusion Distillation (LADD)&#xff0c;与基于像素的ADD&am…

elment-ui el-tabs组件 每次点击后 created方法都会执行2次

先看错误的 日志打印: 错误的代码如下: 正确的日志打印: 正确的代码如下: 前言: 在element-ui的tabs组件中,我们发现每次切换页面,所有的子组件都会重新渲染一次。当子页面需要发送数据请求并且子页面过多时,这样会过多的占用网络资源。这里我们可以使用 v-if 来进行…

HarmonyOS NEXT应用开发之左右拖动切换图片效果案例

介绍 本示例使用滑动手势监听&#xff0c;实时调整左右两侧内容显示区域大小和效果。通过绑定gesture事件中的PanGesture平移手势&#xff0c;实时获取拖动距离。当拖动时&#xff0c;实时地调节左右两个Image组件的宽度&#xff0c;从而成功实现左右拖动切换图片效果的功能。…

python接口自动化测试数据和代码分离解析

common中存放的是整个项目中公共使用的封装方法 从工程目录上可以看到区分 datas中专门存放测试数据(yml文件) cases中专门集中存放测试用例 ... 数据分离的第一步先找到工程项目路径 1 2 3 4 5 6 7 8 9 10 11 12 # -*- encoding: utf-8 -*- """ __Software…

通过docker容器安装zabbix6.4.12图文详解(监控服务器docker容器)

目录 一、相关环境及镜像二、zabbix-server服务端部署1.使用docker创建zabbix-server服务端(1). 创建专用于Zabbix组件容器的网络(2). 启动空的MySQL服务器实例(3). 启动Zabbix Java网关实例(4). 启动Zabbix服务器实例并将实例与创建的MySQL服务器实例链接(5). 启动Zabbix Web界…

深入理解Ubuntu22:探索Linux操作系统的功能与应用

一、linux &#xff08;一&#xff09;、安装 1、电脑可以安装双系统&#xff0c;即在一套硬件上只能同时运行一个操作系统&#xff0c;例&#xff1a;C盘安装win&#xff0c;D盘安装linux。 2、虚拟机 虚拟机需要硬件支持&#xff0c;并需开启VT-x. 如&#xff1a;Virtual…

华为OD机试真题-推荐多样性-2024年OD统一考试(C卷)

题目描述: 推荐多样性需要从多个列表中选择元素,一次性要返回N屏数据(窗口数量),每屏展示K个元素(窗口大小),选择策略: 1. 各个列表元素需要做穿插处理,即先从第一个列表中为每屏选择一个元素,再从第二个列表中为每屏选择一个元素,依次类推 2. 每个列表的元素尽量均…

②免费AI软件开发工具测评:通义灵码 VS 码上飞

前言 我又双叒叕来测评了&#xff01;上次给大家带来的是iFlyCode和CodeFlying两款产品的测评&#xff0c;受到了大家的一致好评~ 今天咱就继续来聊聊&#xff0c;这次我们选的的对象是通义灵码和码上飞&#xff0c;从名字上也能看到出来这两款产品一定是跟软件开发有关系的&…

【c++】c++背景(c++的前世今生)

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;c_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1. 什么是C 2. C发展史 3. C的重要性 3.1 语言的使用广泛度 3.2在工作邻域 1. 操作系统以及大型系统软件开发 2. 服务器端开发 3. …

模型部署——RKNN模型量化精度分析及混合量化提高精度

模型部署——RKNN模型量化精度分析及混合量化提高精度&#xff08;附代码&#xff09;-CSDN博客 3.1 量化精度分析流程 计算不同情况下&#xff0c;同一层网络输入值的余弦距离&#xff0c;来近似的查看每一层精度损失的情况。具体量化精度分析的流程如下&#xff1a; 3.2 量…

【好用】Star超36.8k,一个的免费通用数据库管理工具

关于数据库管理工具&#xff0c;大家可能都在用SQLyog、Navicat、MySQL-Front、SQL Studio、MySQL Workbench等等&#xff0c;这些管理工具不是不好用&#xff0c;就是要变魔术才可以用&#xff0c;今天 V 哥给大家推荐一个即好用&#xff0c;又免费的可视化通用数据库管理工具…

对JS文件进行压缩未通过,对WXML文件进行压缩未通过 问题解决

问题描述 在使用uniapp 开发微信小程序&#xff0c;进行上架发布时 代码质量栏 出现对JS文件进行压缩未通过&#xff0c;对WXML文件进行压缩未通过 问题。 虽然现实代码上传成功&#xff0c;但是作为一个合格的猿人&#xff0c;肯定是要解决的。那么如何解决呢&#xff1f; …

java-ssm-jsp基于javaweb的宠物猫狗商业系统

java-ssm-jsp基于javaweb的宠物猫狗商业系统 获取源码——》公主号&#xff1a;计算机专业毕设大全 获取源码——》公主号&#xff1a;计算机专业毕设大全

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

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

【Leetcode-73.矩阵置零】

题目&#xff1a; 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]示例 2&…