鸭子类型
本课属于 Coddy GDScript 学习旅程的“面向对象编程”单元。第 22 课,共 65 课。
鸭子类型关注对象能做什么,而不是它属于哪个类。只要它有你调用的方法,这次调用就能正常工作。这个名称源自一句俗语:“如果它游起来像鸭子,叫起来也像鸭子,那它就是鸭子。”
这两个类没有共同的父类,但它们都有 swim() 和 quack():
# duck.gd
class_name Duck
extends RefCounted
func swim() -> String:
return "Duck swimming"
func quack() -> String:
return "Quack!"# robot.gd
class_name Robot
extends RefCounted
func swim() -> String:
return "Robot swimming with propellers"
func quack() -> String:
return "Robot sound: BEEP BEEP!"不带类型的参数可以接受任何对象。函数会调用这些方法,每个对象都会运行自己的方法:
func make_it_swim_and_quack(thing) -> void:
print(thing.swim())
print(thing.quack())make_it_swim_and_quack(Duck.new())
make_it_swim_and_quack(Robot.new())Duck swimming
Quack!
Robot swimming with propellers
Robot sound: BEEP BEEP!重写需要一个共同的父类,鸭子类型则不需要。参数的类型标注为 thing: Duck 时,传入 Robot 会导致解析错误。
如果对象缺少该方法,调用就会因错误而停止函数运行,例如 Invalid call. Nonexistent function 'quack'。先用 has_method() 检查,该方法接受方法名作为 String:
var things := [Duck.new(), Rock.new()]
for thing in things:
if thing.has_method("quack"):
print(thing.quack())
else:
print("This one cannot quack")Quack!
This one cannot quack价格:使用类型时,Godot 会在程序运行前发现错误的方法名;使用鸭子类型时,只有运行到那一行才会发现。对于属于无关类但共享少数方法的对象,可以使用鸭子类型。
挑战
简单爆炸会对附近所有能受到伤害的对象造成伤害。这些对象没有共同的父类。player.gd 和 rock.gd 已完成:Player 有 take_damage(amount) 和 status(),Rock 只有 status()。
- 在
crate.gd中,为Crate添加一个初始值为false的broken变量、一个在伤害值达到或超过10时摧毁箱子的take_damage(amount)方法,以及一个返回Crate: broken或Crate: intact的status()。 - 在
explosion.gd中,编写静态函数hit_all(things, amount)。它会对数组中拥有take_damage(amount)方法的每个对象调用该方法,并返回命中的对象数量。
提供的代码会读取一行对象名称(player、crate 或 rock),然后读取后续爆炸的数量以及每次爆炸的伤害值。它会打印每次爆炸命中的对象数量,然后打印每个对象的状态。输入 player crate rock,并设置两次爆炸的伤害值分别为 5 和 20 时,输出如下:
Explosion 1 hit 2 objects
Explosion 2 hit 2 objects
Player: 75 HP
Crate: broken
Rock: solid你的代码写在 crate.gd 和 explosion.gd 中。main.gd 保存了提供的输入/输出代码,不能编辑。
亲手试试
extends Node
func _ready():
# 提供的输入/输出代码:保持原样
var things := []
for word in OS.read_string_from_stdin().split(" "):
if word == "player":
things.append(Player.new())
elif word == "crate":
things.append(Crate.new())
else:
things.append(Rock.new())
var count := int(OS.read_string_from_stdin())
for i in count:
var hits := Explosion.hit_all(things, int(OS.read_string_from_stdin()))
print("Explosion %d hit %d objects" % [i + 1, hits])
for thing in things:
print(thing.status())
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: GDScript 在线编译器