组合与继承
本课属于 Coddy GDScript 学习旅程的“面向对象编程”单元。第 40 课,共 65 课。
类可以通过两种方式复用代码。继承(extends)表示一个类是一种其他类:Mage 是一种 Player。组合表示一个类拥有一个组成部分:它会在成员变量中存储另一个对象,并把工作交给它:
# motor.gd
class_name Motor
extends RefCounted
func start() -> String:
return "motor running"
# car.gd
class_name Car
extends RefCounted
var motor := Motor.new()
func start() -> String:
return "Car starts, " + motor.start()Car.new().start() 会返回 Car starts, motor running。汽车并不知道电机如何工作:它会调用 motor.start()。把调用交给这样的部件来处理,称为委托。(这个类名为 Motor,因为 Engine 已经是 Godot 中的一个类。)
当各个部分会发生变化或组合时,组合就很有用。使用继承时,行走的角色和飞行的角色是两个子类,而一只开始飞行的蝙蝠无法更改自己的类。使用组合时,角色包含一个移动部分,切换它只需一次赋值:
# character.gd
class_name Character
extends RefCounted
var char_name: String
var movement: Movement
func _init(new_name: String, new_movement: Movement) -> void:
char_name = new_name
movement = new_movement
func move() -> String:
return char_name + " " + movement.move()
# main.gd
var bat := Character.new("Bat", Walk.new())
print(bat.move()) # Bat 走路
bat.movement = Fly.new()
print(bat.move()) # Bat 飞行这里,Walk 和 Fly 扩展了一个小型的 Movement 类:这两种技术可以协同工作。GDScript 类只能 extend 一个类,因此组合也是让一个对象通过持有多个部分来获得多种能力的方式。
对于始终不变的真实“是一种”关系,使用继承。对于类“拥有”某个东西的情况,或者需要在游戏运行时替换组成部分的情况,使用组合。
挑战
简单骑士不会继承武器:骑士拥有武器,而且可以更换武器。请构建这两个类。
Weapon存储weapon_name和damage,它们都由构造函数设置:Weapon.new("Sword", 7)。Knight存储knight_name(由构造函数提供)和一件武器,初始值为null。equip(new_weapon)会替换武器,drop()会将武器设回null。attack()会使用骑士的名字、武器的名字和伤害值,返回Ada attacks with Sword for 7 damage;如果没有武器,则返回Ada has no weapon。
提供的代码会读取骑士的名字和后续命令的数量(equip Sword 7、drop、attack),然后运行这些命令。对于 Ada 和 attack、equip Sword 7、attack、equip Axe 12、attack、drop、attack,输出为:
Ada has no weapon
Equipped Sword
Ada attacks with Sword for 7 damage
Equipped Axe
Ada attacks with Axe for 12 damage
Dropped the weapon
Ada has no weapon请将代码写在 weapon.gd 和 knight.gd 中。main.gd 存放了提供的输入/输出代码,不能编辑。
亲手试试
extends Node
func _ready():
# 提供的输入和输出代码:保持原样。
var knight := Knight.new(OS.read_string_from_stdin())
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
if parts[0] == "equip":
knight.equip(Weapon.new(parts[1], int(parts[2])))
print("Equipped ", parts[1])
elif parts[0] == "drop":
knight.drop()
print("Dropped the weapon")
else:
print(knight.attack())
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: GDScript 在线编译器