GDScript 中的静态类型

在本指南中,您将学会:

  • 如何在 GDScript 中使用类型
  • 静态类型可以帮助您避免 bug 在何处以及如何使用此新语言功能完全取决于您: 您可以只在某些敏感的GDScript文件中使用它, 也可以在任何地方使用它, 或者像往常一样编写代码! 静态类型可用于变量, 常量, 函数, 参数, 和返回类型. 注解 从Godot 3.1开始, 可以使用类型化的GDScript.

    静态类型简介

    有了GDScript中的类型,Godot在编写代码时甚至可以检测到更多错误!它会在你工作时为你和你的团队提供更多信息. 因为当你调用方法时, 会显示出参数的类型. Item 节点, 然后再编写一个 Inventory . 要将项目添加到清单中, 使用代码的人员应始终将 Item 传递给 Inventory.add 方法. 使用类型, 您可以强制执行以下操作:
    1. # In 'Item.gd'.class_name Item# In 'Inventory.gd'.class_name Inventoryfunc add(reference: Item, amount: int = 1): var item = find_item(reference) if not item: item = _instance_item_from_db(reference) item.amount += amount
    警告系统 . 从版本3.1开始,Godot会在您编写代码时向您发出有关代码的警告: 引擎会识别代码中可能导致运行时出现问题的部分, 但您可以决定是否要保留代码. 稍后详细介绍. PlayerController 类的动态和静态类型补全选项之间的区别. 您之前可能已经将节点存储在变量中, 并输入了一个点, 但是没有代码自动补全建议: 这是因为动态代码. Godot无法知道您传递给函数的节点或值类型. 但是, 如果您明确地声明了类型, 则将从该节点类型获取所有公共方法和变量: 将来, 类型化的GDScript也将提高代码性能: 实时编译和其他编译器改进已经出现在路线图上! 总体而言, 类型化编程可为您提供更加结构化的体验. 它有助于防止错误并改善脚本的自我描述能力. 当您在团队中或长期项目中工作时, 这特别有用: 研究表明, 开发人员将大部分时间都花在阅读别人的代码或他们过去编写并忘记的脚本上. 代码越清晰, 越结构化, 理解得就越快, 前进的速度就越快.

    如何使用静态类型

    var health: int . 这将强制变量的类型保持不变:
    1. var damage: float = 10.5const MOVE_SPEED: float = 50.0
    如果您写冒号但省略写类型时,Godot将尝试推测类型:
    1. var life_points := 4var damage := 10.5var motion := Vector2()
    当前, 您可以使用三类…类型:
  1. 内置类型
  2. Object, Node, Area2D, Camera2D, 等)
  3. class_name feature to register types in the editor. 注解 您不需要为常量编写类型提示, 因为Godot会根据分配的值自动设置. 但是您仍然可以这样做, 以使代码的意图更加清晰.

    自定义变量类型

    您可以将任何类(包括您的自定义类)用作类型. 有两种在脚本中使用它们的方法. 第一种方法是将要用作类型的脚本预加载为常量:
    1. const Rifle = preload("res://player/weapons/Rifle.gd")var my_rifle: Rifle
    class_name 关键字. 对于上面的示例, 您的 Rifle.gd 看起来就像这样:
    1. extends Node2Dclass_name Rifle
    class_name ,Godot会在编辑器中注册一个全局 Rifle 类型, 您可以在任何地方使用它而无需将其预加载到常量中:
    1. var my_rifle: Rifle

    变量转换

    类型转换是类型语言的关键概念. 转换是将值从一种类型转换为另一种类型. extends Area2D. 您希望它与游戏角色, 即一个附带有一个名为 PlayerController 的脚本的 KinematicBody2D, 碰撞. 您可以使用 on_body_entered 信号来检测碰撞. 使用类型化的代码, 您检测到的物体(body)将是通用的 PhysicsBody2D, 而不是 _on_body_entered 回调上使用的 PlayerController. as 转换关键字检查这个 PhysicsBody2D 是否是您的游戏角色, 并再次使用冒号 : 来强制变量使用这种类型. 这会强制变量坚持使用 PlayerController 类型:
    1. func _on_body_entered(body: PhysicsBody2D) -> void: var player := body as PlayerController if not player: return player.damage()
    body 没有扩展为 PlayerController , 则 player 变量将被设置为 null . 我们可以用它来检查物体是否为游戏玩家角色. 多亏了类型转换, 我们还将获得 player 变量的代码自动补全功能. 注解 如果您尝试使用内置类型进行转换并失败, 则Godot将引发错误.

    安全行

    您也可以使用转换来确保安全行. 安全行是Godot 3.1中的新工具, 可以告诉您歧义的代码行何时是类型安全的. 由于您有时会混合类型化代码和动态代码, 有时如果指令在运行时触发错误,Godot可能没有足够的信息判断. $Timer 获取节点.GDScript支持 鸭子类型 <https://stackoverflow.com/a/4205163/8125343&gt; __, 所以即使您的计时器是 Timer 类型, 它也扩展了 NodeObject , 这两个类. 使用动态GDScript, 只要节点具有您需要调用的方法, 您也不必关心节点的类型. ($Timer as Timer) , ($Player as KinematicBody2D) 等.Godot将确保该类型有效, 如果是, 则行号在脚本编辑器的左侧变为绿色. 不安全代码 (第 7 行) vs 安全代码 (第 6 行和第 8 行) 注解 可以在编辑器设置中关闭安全行或更改其颜色.

    使用箭头 -> 定义函数的返回类型

    ->, 后跟返回类型:
    1. func _process(delta: float) -> void: pass
    void 表示函数不返回任何内容. 您可以使用任何类型, 如变量:
    1. func hit(damage: float) -> bool: health_points -= damage return health_points <= 0
    您还可以使用自己的节点作为返回类型:
    1. # 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

    静态还是动态:坚持一种风格

    强类型的 GDScript 和动态 GDScript 可以共存于同一项目,但还是建议选择其一,以确保代码库和同伴间的一致性。如果你们遵循相同的规范,那么大家都可以更轻松地协作,阅读和理解他人的代码也会更加迅速。 类型化的代码需要编写更多, 但是您将获得上面讨论的好处. 下面是内容一样的空脚本示例, 首先是使用动态风格:
    1. extends Nodefunc _ready(): passfunc _process(delta): pass
    其次是使用静态类型:
    1. extends Nodefunc _ready() -> void: passfunc _process(delta: float) -> void: pass
    body_entered 信号示例:
    1. func _on_Area2D_body_entered(body): pass
    以及具有类型提示的, 相同的回调:
    1. func _on_area_entered(area: CollisionObject2D) -> void: pass
    CollisionObject2D:
    1. func _on_area_entered(bullet: Bullet) -> void: if not bullet: return take_damage(bullet.damage)
    bullet 变量在这里保存任何继承了 CollisionObject2D 的节点类, 但我们需要确保它是我们的 Bullet , 即那个我们为项目创建的节点类型. 如果它是其他任何东西, 比如 Area2D , 或任何不扩展 Bullet 的节点, bullet 变量值将是 null .

    警告系统

    注解 GDScript 警告系统.

    无法指定类型的情况

    都会触发错误 . 您不能将枚举作为类型:
    1. enum MoveDirection {UP, DOWN, LEFT, RIGHT}var current_direction: MoveDirection
    您无法指定数组中单个成员的类型. 这会给您一个错误:
    1. var enemies: Array = [$Goblin: Enemy, $Zombie: Enemy]
    for 循环中强制指定类型, 因为 for 关键字循环中每个元素已经有不同的类型. 所以您 不能 指定类型:
    1. var names = ["John", "Marta", "Samantha", "Jimmy"]for name: String in names: pass
    两个脚本不能循环依赖:
    1. # Player.gdextends Area2Dclass_name Playervar rifle: Rifle
    1. # Rifle.gdextends Area2Dclass_name Riflevar player: Player

    总结

    类型化的GDScript是一个强大的工具. 从Godot 3.1版开始可用, 它可以帮助您编写更多结构化的代码, 避免常见错误, 以及创建可伸缩的系统. 将来, 由于即将进行的编译器优化, 静态类型也将为您带来不错的性能提升.