文章来源
本文的作者:Nathan Lovato
原文地址在:http://gdquest.com/tutorial/game-design/godot/gdscript/typed-gdscript/
Godot 3.1的GDscript可选输入语法。你将将学习:
- 如何在GDscript中使用类型
- 静态类型能帮助你避免错误
静态类型可用于变量,常量,函数,参数和返回类型
为什么你要学习GDscript的类型定义?
在GDscript中给变量定义类型,Godot可以检测到更多错误!它会在你工作时为你和你的团队提供更多信息,因为当你调用方法时,参数的类型很明显。
如下代码所示,在类 Inventory中有add方法中reference:Item表示参数reference是Item类型,amount : int表示参数amount是int类型
"""in Item.gd"""
class_name Item
"""in Inventory.gd"""
class_name Inventory
func add(reference : Item, amount : int = 1):
var item = find_item(reference)
if not item:
item = _instance_item_from_db(reference)
item.amount += amount
在GDScript使用类型的另一个显着优点是新的警告系统。从版本3.1开始,Godot会在你编写代码时向你发出有关代码的警告:引擎会识别代码中可能导致运行时出现问题的部分,但你可以决定是否要保留代码。稍后详细介绍。
静态类型还为你提供了更好的代码完成选项。下面,你可以看到被调用类PlayerController的动态类型和静态类型自动完成的区别。
在你没有给body定义类型的时候,自动完成是没有提示的:
这是由于动态代码。Godot无法知道你传递给函数的节点或值类型。但是,如果你明确地编写类型,则将从节点获取所有公共方法和变量:
将来,GDScript类型还将提高代码性能:Just In Time编译和其他编译器改进已经在规划图上了!
总的来说,类型化编程给你提供了一种更结构化的体验。它有助于防止错误并改进脚本的自文档化方面。当你在团队或长期项目中工作时,这一点尤其有用:研究表明,开发人员将大部分时间花在阅读其他人的代码或过去编写的脚本上,而忘记了这些内容。代码越清晰,结构越结构化,理解起来就越快,前进的速度就越快。
如何在GODOT 3.1中使用静态类型?
要定义变量或常量的类型,请在变量名称后面加上一个冒号,后跟其类型。例如var health : int。这会强制变量的类型始终保持不变:
如果你写一个冒号,Godot会尝试推断类型,但你省略了类型:
目前你可以使用三种类型的......类型:
- 内置类型
- 核心类和节点(Object,Node,Area2D,Camera2D,等等)
- 你自己的自定义类。查看新的class_name功能以在编辑器中注册类型
自定义变量类型
你可以将任何类(包括自定义类)用作类型。有两种方法可以在脚本中使用它们。第一种方法是预加载要用作常量类型的脚本:
const Rifle = preload('res://player/weapons/Rifle.gd')
var my_rifle : Rifle
第二种方法是class_name在创建时使用关键字。对于上面的示例,Rifle.gd将如下所示:
extends Node2D
class_name Rifle
如果你使用class_name,Godot会在编辑器中全局注册Rifle类型,你可以在任何地方使用它而无需将其预加载到常量中:
var my_rifle : Rifle
变量转换
类型转换是类型化语言中的一个关键概念。我们从另一种类型的类型转换成另一种类型
想象一下你的游戏中的敌人,extends Area2D
类型。你想让它与Player
发生碰撞,一个extend kinematicBody2D
的PlayerController脚本。使用on_body_entered
信号来检测碰撞。使用类型化代码,你检测到的主体将是通用的PhysicsBody2D
,而不是PlayerController
的_on_body_entered
回调。
你可以PhysicsBody2D
使用as
cast关键字检查这是否是你的Player ,并:
再次使用冒号强制变量使用此类型。这会强制变量保持PlayerController
类型:
func _on_body_entered(body : PhysicsBody2D) -> void:
var player := body as PlayerController
if not player:
return
player.damage()
当我们处理自定义类型时,如果body
未扩展PlayerController
,则player
变量将设置为null
。我们可以用它来检查身body是否是玩家。通过这样的转换,使用player时可以获得PlayerController全部方法和变量的自动完成
安全线
你还可以使用转换来确认安全线。安全线是Godot 3.1中的一个新工具,用于告诉你什么时候不明确的代码行是类型安全的。由于你可以混合并匹配键入的和动态的代码,有时,Godot没有足够的信息来判断一条指令是否会在运行时触发错误。
你获得子节点时会发生这种情况。让我们以一个Timer为例:使用动态代码,你可以获取节点$Timer
。GDscript支持duck-typing,所以即使你的计时器是类型Timer,它extend自Node和Object。使用动态GDscript,你不需要关心节点的类型,只要它有需要调用的方法。
当你得到一个节点时,你可以使用强制转换告诉Godot你期望的类型:($Timer as Timer)
,($Player as KinematicBody2D)
等等。戈多将确保这种类型有效,如果是这样,行号将在脚本编辑器的左侧变为绿色。
使用箭头定义函数的返回类型 - >
要定义函数的返回类型,请->在声明后写一个破折号和一个右尖括号,然后是返回类型:
func _process(delta : float) -> void:
pass
该类型void
表示该函数不返回任何内容。你可以使用任何类型作为变量:
func hit(damage : float) -> bool:
health_points -= damage
return health_points <= 0
你还可以使用自己的节点作为返回类型:
"""Inventory.gd"""
"""Adds an item to the inventory and returns it"""
func add(reference : Item, amount : int) -> Item:
var item : Item = find_item(reference)
if not item:
item = ItemDatabase.get_instance(reference)
item.amount += amount
return item
类型化(typed)or DYNAMIC:坚持一种风格
类型化GDscript和动态GDscript可以在同一个项目中共存。但我建议,为了代码库的一致性,以及同行的一致性,应该坚持使用这两种风格。如果你遵循相同的指导原则,那么每个人都可以更容易地一起工作,并且可以更快地阅读和理解其他人的代码。
类型化代码需要更多的编写,但是你可以获得我们上面讨论的好处。下面是一个相同的空脚本示例,使用DYNAMIC样式:
extends Node
func _ready():
pass
func _process(delta):
pass
并使用静态类型:
extends Node
func _ready() -> void:
pass
func _process(delta : float) -> void:
pass
如你所见,你还可以将类型与引擎virtual methods一起使用。与任何方法一样,信号回调也可以使用类型。下面是动态样式的body_entered信号:
func _on_Area2D_body_entered(body):
pass
和相同的回调,类型提示:
func _on_area_entered(area : CollisionObject2D) -> void:
pass
你可以自由替换,例如PhysicsBody2D,使用你自己的类型,自动转换参数:
func _on_area_entered(bullet : Bullet) -> void:
if not bullet:
return
take_damage(bullet.damage)
该bullet变量可以持有任何CollisionObject2D,但我们要确保这是我们的Bullet,我们为我们的项目创建了一个节点。如果它是其他任何东西,比如一个Area2D或者没有extend 的任何节点Bullet,那么bullet变量就是null。
警告系统
警告系统补充了类型化的GDscript。它可以帮助你避免在开发过程中难以发现的错误,并可能导致运行时错误。
你可以在项目设置中配置警告GDscript:
你可以在脚本编辑器的状态栏中找到活动GDscript文件的警告列表。以下示例有3个警告:
要忽略一个文件中的特定警告,请插入表单的特殊注释#warning-ignore:warning-id
,或单击警告说明右侧的忽略链接。Godot将在相应的行上方添加注释,代码将不再触发相应的警告:
警告不会阻止游戏运行,但如果你愿意,可以将它们变成错误。这样,除非你修复所有警告,否则你的游戏将无法编译。前往GDscript
项目设置的部分以打开此选项。这是与前一个示例相同的文件,并在启用错误时显示警告:
无法指定类型的情况
为了总结这个介绍,我们将介绍一些不能使用类型提示的情况。以下所有示例都会触发错误。
你不能将Enums用作类型
enum MoveDirection { UP, DOWN, LEFT, RIGHT }
var current_direction : MoveDirection
你无法指定数组中单个成员的类型。这会给你一个错误:
var enemies : Array = [$Goblin : Enemy, $Zombie : Enemy]
你不能强制在for
循环中分配类型,因为for
关键字循环已经具有不同类型的每个元素。所以你不能写:
var names ['John', 'Marta', 'Samantha', 'Jimmy']
for name : String in names:
pass
两个脚本不能以循环方式相互依赖:
"""Player.gd""" extends Area2D class_name Player var rifle : Rifle
"""Rifle.gd""" extends Area2D class_name Rifle var player : Player
概要类型的情况
GDscript类型化是一个强大的工具。使用Godot 3.1,它已经可以帮助你编写更多结构化代码,帮助你避免常见错误,并创建可扩展系统。将来,由于即将进行的编译器优化,静态类型也将为你带来不错的性能提升。
这功能很好,在间接引用的情况下终于可以有代码提示了
那个.......,自定义类和autoload有什么区别,3.06能用吗?