注: 这是在Godot4.0中总结出的内容,并且语言是C#。

特别的,下面有的特性和C#关系比较大。

基本特性

在Godot中,为某个节点编写特别的代码时,需要为节点新建脚本,或引用已有脚本。

引用脚本时,填入脚本路径即可,相当于是复用代码了。

新建脚本时,一般做法是新建一个自定义类型,并且这个类型继承自原有节点的类型。

其实,你也可以不继承自原有节点的那个类型。下面的各小节文章均是针对这个情况的。

在选择继承的目标时,你可以选择的范围有:(最常规的)继承自原有类型、继承自自定义类型、继承自"中间"类型。

继承自自定义类型

继承自自定义类型,一般来说是为了满足这个需求的:

用户为某节点编写了一个自定义了类型后,在新的情境下,需要扩充这个类型,让它有新的功能,并且旧的不能变。

此时,程序员一般第一个想到的就是继承。Godot顺理成章地允许用户这样进行继承。

想要进行这样的继承时,为节点新建脚本时必须先选择已有类型,若直接填入想继承的类型,Godot不允许这样做。

创建完成后,用户可以打开脚本文件,手动修改继承类型。

虽然看上去就像作弊,但是Godot方面认可这种做法,也有人建议在建立脚本时,"继承"内容框可以选择自定义类型进行继承,但是Godot的开发者们暂时并没有这么做。

需要注意的是,这种做法对此脚本附加到的节点有要求。

  • 首先,你在编写一系列一层一层继承起来的类时,你建立起来的最底层的那个自定义类肯定是继承自"原生类型"的

    • "原生类型"就是新建节点时,你能在面板里选到的类型(没错,这里找不到你自定义的类型,它们100%是Godot内置的!)
  • 要将这一系列自定义类中的任何一个附加到一个目标节点时,这个目标节点在编辑器中体现出来的类型必须能够兼容自定义类最底层的"原生类型",和它相同,或是继承自它,都可以。

    (注:如果不兼容,运行时会报错。)

继承自"中间"类型

读了上面的内容时,你也许会意识到,其实任何一个自定义类型底层的"原生类型",可以是它附加的节点的继承树中大于等于Node的类型之中的任何一个类型。

我称这种做法为"继承自中间类型"。

事实上,也许需要调整一下观念。大家一开始似乎会认为,节点附加了一个自定义类型后,节点就是自定义类型的实例了。

实际情况似乎要稍微割裂一点。因为继承自中间类型后,你会发现,这个自定义类型无法完全描述这个节点。请看接下来的例子。

这里,我给一个精灵Sprite2d挂上这样一个自定义类型,它继承自Node。

public partial class TNode : Node
{
}

我想知道,这个节点从C#继承体系的角度看,还是不是"精灵(Sprite2D)"了

让我们在根节点中写一点代码,试图了解该节点的类型。

public partial class AskForClass : Node2D
{
public override void _Ready()
{
var t1node = GetNode("TNode"); //方法1 打印继承树
Type tobj;
tobj = t1node.GetType();
while (tobj != null)
{
GD.Print(tobj.Name);
tobj = tobj.BaseType;
}
GD.Print("以上是继承树。"); //方法2 类型转换。转换失败的话就是null。
var t2node = t1node as Sprite2D;
GD.Print(t2node); //方法3 我发现Godot有一个IsClass()方法
GD.Print("is sprite2D class?" + t1node.IsClass("Sprite2D")); }
}

(小提示:Godot的_Ready()函数被执行顺序是先子节点,再父节点,这样嵌套的,所以父节点访问子节点总是万无一失的,当然,这个示例就算顺序不是这样也不存在问题)

上面用了3种方式试图确认我们的t1node有没有Sprite2D的成分,以及Sprite2D的成分通过哪种方式能查到,猜猜看?

结论是:

TNode
Node
GodotObject
Object
以上是继承树。
null
is sprite2D class?True

除非使用Godot提供的函数IsClass(), 光靠C#的继承体系,查不到Sprite2D的成分, 而且实例无法转换成Sprite2D类型,这样就不能以C#通常的方式操作Sprite2D特有的函数和变量了。

在C#内,虽然它"放弃了作为Sprite2D的身份",但我们的节点在运行时仍然做着一个"精灵"会做的事情,比如我在编辑器里对它的位置和旋转进行了变更,这些变更都没有丢失。

个人推测,此时需要使用GetIndexed()SetIndexed()等方法来操作那些无法直接访问的东西。

这个实例和实际存在于Godot运行时的节点竟然有这样的不同。

以后也许时不时需要想起来这样一件事——C#实例和Godot内的节点只是连在一起,有一个映射的关系罢了,并不100%是那个节点本身。

节点除了有C#能提供给它的函数和字段外,还可以拥有相当突出的Godot赋予它的不同类型的功能,即各种类型的"原生节点"的各种各样的功能。

阅读文档后,大概可以这样理解,Godot运行时维护的节点身上的函数和变量的表现更符合动态语言的特征,而不是静态类型语言。

不论是C#脚本、Godot脚本、还是"原生类型"的节点,行为都是将自己的各种功能附加或覆盖到了节点身上。

Godot is very dynamic. An object's script, and therefore its properties, methods and signals, can be changed at run-time. Because of this, there can be occasions where, for example, a property required by a method may not exist. To prevent run-time errors, see methods such as set, get, call, has_method, has_signal, etc. Note that these methods are much slower than direct references.

Godot很是动态。对象的脚本及其属性、方法和信号可以在运行时更改。因此,在某些情况下,例如,方法所需的属性可能不存在。若要防止运行时错误,请参阅设置、获取、调用、has_method、has_signal等方法。请注意,这些方法比直接引用慢得多。

继承自中间类型后可能遇到的坑

上述特性可能会引发一个问题,当你需要用C#找到场景中的所有某一原生类型的节点时,从"中间"继承的节点被获取后,由于一些身份被放弃了,有可能被漏掉!

也就是说,在上面的案例中,想找Sprite2D时,用下面的方法,挂载了TNode脚本的精灵将被跳过,尽管它这么大一个放在屏幕上。

List<Sprite2D> lst = new List<Sprite2D>();
var children = FindChildren("*");
foreach (var chi in children)
{
if (chi is Sprite2D sp)
{
lst.Add(sp);
GD.Print("这个是精灵" + chi.Name);
}
else
{
GD.Print("这个不是精灵" + chi.Name);
}
}

我没有测试GDScript,不知道是否情况会不同。也许它支持多重继承?

综上所述,个人建议尽量避免附加脚本时从中间继承

实在有这样的需求,要么避免一个类型的每一个身份都需要被C#直接操作,要么用IsClass()配合GetIndexed()SetIndexed()等方法处理该对象。

参考:

https://godotengine.org/qa/141137/best-way-to-add-a-node-that-extends-a-custom-class

https://docs.godotengine.org/en/latest/classes/class_object.html

Godot的几个附加脚本和进行继承时比较特别的特性的更多相关文章

  1. [译]Godot系列教程四 - 编写脚本

    编写脚本(Scripting) 简介 关于无需编程即可创建视频游戏的那些工具的谈论有很多.不用学习编程知识对很多独立开发者来说就是一个梦想.这种需求 - 游戏开发者.甚至在很多公司内部,希望对游戏流程 ...

  2. bash 脚本编程 利用 “=” 赋值时,左右不能留空格

    对脚本变量用“=”赋值时, "=" 左右不能留有空格,否则会提示错误. 比如以下例子: #!/bin/bash BEGIN_TIME = `date +%H:%M:%S` ./a. ...

  3. set_include_path — 设置 include_path 配置选项为当前脚本设置 include_path 运行时的配置选项。

    说明 string set_include_path ( string $new_include_path ) 为当前脚本设置 include_path 运行时的配置选项. 参数 new_includ ...

  4. 游戏编程之Unity常用脚本类的继承关系

    前言学习Unity开发引擎的初学者会接触大量的脚本类,而这些类之间的关系往往容易被忽略.本文对Unity引擎开发中的一些常用类及其关系进行了简单的归纳总结. 博文首发地址:http://tieba.b ...

  5. VS2013 删除"附加依赖项"中“继承的值”

    经过好几次尝试,都无法在VS2013中直接删除“继承的值”,于是另辟蹊径,找到了一种解决方法. 相对而言,在 VS2010 中干这件事会容易一点,或者说,成功率更高一点,于是,我的思路就是再装一个 V ...

  6. shell脚本里面相互调用时路径不要用pwd获取

    shellA调用shellB,如果shellB 里面需要使用路径作为变量,去寻找其它文件.那么要注意,不用pwd,其返回的是系统中用户当前所在位置的路径,也就是shellA的路径,这样就错了.应该用d ...

  7. jenkins运行脚本生成HTML报告时遇到的问题

    1.jenkins生成HTML报告 1)安装插件:HTML Publisher plugin 2)系统管理->插件管理->安装HTMLHTML Publisher plugin 2.job ...

  8. 在arcgis使用python脚本进行字段计算时是如何解决中文问题的

    来自:https://www.jb51.net/article/73561.htm 一.引言 在arcgis打开一个图层的属性表,可以对属性表的某个字段进行计算,但是在平常一般都是使用arcgis提供 ...

  9. 在arcgis使用python脚本进行字段计算时对中文的处理方案

    一.引言 在arcgis打开一个图层的属性表,可以对属性表的某个字段进行计算,但是在平常一般都是使用arcgis提供的字段计算器的界面进行傻瓜式的简答的赋值操作,并没有使用到脚本对字段值进行逻辑的操作 ...

  10. 初学shell,为了练习sed,写了个简单的批量修改文件名的脚本,后来执行时发现系统竟然自带有一个rename命令,顺便也记下了

    1 #!/bin/bash   2 <<Comment   3 批量修改文件名的脚本   4 2015/10/24   5 webber   6 Comment   7 ARGS=2   ...

随机推荐

  1. 修改host文件

    host文件路径 Windows 系统:C:\Windows\System32\drivers\etc\hosts Linux 系统:/etc/hosts Mac(苹果电脑)系统:/etc/hosts ...

  2. new 的原理是什么?通过 new 的方式创建对象和通过字面量 创建有什么区别?

    涉及面试题: new 的原理是什么?通过 new 的方式创建对象和通过字面量 创建有什么区别? 在调用 new 的过程中会发生四件事情 新生成了一个对象: 链接到原型: 绑定 this : 返回新对象 ...

  3. 实现和CSS一样的easing动画?直接看Mozilla、Chromium源码!

    前言 在上一篇丝滑的贝塞尔曲线:从数学原理到应用介绍贝塞尔曲线实现动画时给自己留了一个坑,实现的动画效果和CSS的transition-timing-function: cubic-bezier差别较 ...

  4. 迁移学习《Efficient and Robust Pseudo-Labeling for Unsupervised Domain Adaptation》

    论文信息 论文标题:Efficient and Robust Pseudo-Labeling for Unsupervised Domain Adaptation论文作者:Hochang Rhee.N ...

  5. 五月二十七日jdbc,算法以及数据库

    1.ResultSetMetaData接口主要获得结果集.例如:结果集字段数量和名字通过ResultSet的getMetaData()方法获得对应对象 public class app17_20 { ...

  6. 【转载】使用IntelliJ IDEA 14和Maven创建java web项目

    安装Maven 下载安装 去maven官网下载最新版(传送门  密码: gfi1) 解压到安装目录. 配置 右键桌面的计算机图标,属性–>高级系统设置–>环境变量,添加M2_HOME的环境 ...

  7. Go For Web:一篇文章带你用 Go 搭建一个最简单的 Web 服务、了解 Golang 运行 web 的原理

    前言: 本文作为解决如何通过 Golang 来编写 Web 应用这个问题的前瞻,对 Golang 中的 Web 基础部分进行一个简单的介绍.目前 Go 拥有成熟的 Http 处理包,所以我们去编写一个 ...

  8. Springboot集成MongoDB存储文件、读取文件

    一.前言和开发环境及配置 可以转载,但请注明出处. 之前自己写的SpringBoot整合MongoDB的聚合查询操作,感兴趣的可以点击查阅. https://www.cnblogs.com/zaoyu ...

  9. Go语言网络编程: 模拟实现DNS服务器

    环境: 两台虚拟机,不限系统 写在前面 DNS服务器是干什么的?DNS服务器(Domain Name Server,域名服务器)是进行域名和与之相对应的IP地址进行转换的服务器,保存了一张域名和与之相 ...

  10. JDK8到JDK17有哪些吸引人的新特性?

    作者:京东零售 刘一达 前言 2006年之后SUN公司决定将JDK进行开源,从此成立了OpenJDK组织进行JDK代码管理.任何人都可以获取该源码,并通过源码构建一个发行版发布到网络上.但是需要一个组 ...