类和继承
在Godot引擎中,GDScript
是一种类似于 Python 的脚本语言,专门用于游戏开发。类和继承是面向对象编程的核心概念,掌握这些概念对于编写高效、可维护的代码至关重要。本节将详细介绍如何在 GDScript
中使用类和继承,包括类的定义、属性和方法的使用,以及如何通过继承来扩展类的功能。
类的定义
在 GDScript
中,类通过 extends
关键字来继承自一个基类。如果一个类没有明确继承自任何基类,它将默认继承自 Object
类。以下是一个简单的类定义示例:
# 定义一个简单的类
extends Object
# 类的属性
var health: int
var strength: int
var agility: int
# 类的构造函数
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
# 类的方法
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
属性
类的属性用于存储类的状态信息。在 GDScript
中,属性可以通过 var
关键字来定义,并且可以指定其类型。例如:
var health: int
var strength: int
var agility: int
方法
类的方法是定义在类中的函数,用于实现类的行为。在 GDScript
中,方法通过 func
关键字来定义。例如:
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
构造函数
构造函数用于在创建类的实例时初始化其属性。在 GDScript
中,构造函数的名称是 _init
,并且它可以接受参数。例如:
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
继承
继承允许我们创建一个新的类,该类继承自一个现有的类,从而重用现有类的属性和方法,并可以添加新的属性和方法或修改继承的方法。在 GDScript
中,继承通过 extends
关键字来实现。
单继承
以下是一个简单的单继承示例,Player
类继承自 Character
类:
# 定义基类 Character
extends Object
var health: int
var strength: int
var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
# 定义子类 Player 继承自 Character
extends Character
var name: String
var level: int
func _init(name: String, level: int, health: int, strength: int, agility: int):
# 调用基类的构造函数
super._init(health, strength, agility)
self.name = name
self.level = level
func level_up() -> void:
self.level += 1
self.health += 10
self.strength += 5
self.agility += 3
print(f"{self.name} leveled up to level {self.level}!")
func die() -> void:
# 重写基类的 die 方法
print(f"{self.name} has fallen in battle!")
多继承
GDScript
支持单继承,但不直接支持多继承。然而,我们可以通过组合的方式来实现类似多继承的效果。以下是一个示例,Player
类通过组合 Character
和 PlayerStats
类来实现多个类的功能:
# 定义 Character 类
extends Object
var health: int
var strength: int
var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
# 定义 PlayerStats 类
extends Object
var name: String
var level: int
func _init(name: String, level: int):
self.name = name
self.level = level
func level_up() -> void:
self.level += 1
print(f"{self.name} leveled up to level {self.level}!")
# 定义 Player 类,通过组合 Character 和 PlayerStats 类
extends Object
var character: Character
var player_stats: PlayerStats
func _init(name: String, level: int, health: int, strength: int, agility: int):
self.character = Character.new(health, strength, agility)
self.player_stats = PlayerStats.new(name, level)
func attack(target) -> void:
self.character.attack(target)
func take_damage(amount: int) -> void:
self.character.take_damage(amount)
func level_up() -> void:
self.player_stats.level_up()
self.character.health += 10
self.character.strength += 5
self.character.agility += 3
func die() -> void:
print(f"{self.player_stats.name} has fallen in battle!")
重写方法
在继承中,子类可以重写基类的方法。重写方法允许子类提供不同的实现,从而改变或扩展基类的行为。以下是一个示例,Player
类重写了 Character
类的 die
方法:
# 基类 Character
extends Object
var health: int
var strength: int
var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
# 子类 Player
extends Character
var name: String
var level: int
func _init(name: String, level: int, health: int, strength: int, agility: int):
# 调用基类的构造函数
super._init(health, strength, agility)
self.name = name
self.level = level
func level_up() -> void:
self.level += 1
self.health += 10
self.strength += 5
self.agility += 3
print(f"{self.name} leveled up to level {self.level}!")
func die() -> void:
# 重写基类的 die 方法
print(f"{self.name} has fallen in battle!")
调用基类方法
在重写方法时,有时我们希望在子类的方法中调用基类的方法。这可以通过 super
关键字来实现。以下是一个示例,Player
类的 take_damage
方法在调用基类的 take_damage
方法后添加了额外的逻辑:
# 基类 Character
extends Object
var health: int
var strength: int
var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
# 子类 Player
extends Character
var name: String
var level: int
var shield: int = 0
func _init(name: String, level: int, health: int, strength: int, agility: int):
# 调用基类的构造函数
super._init(health, strength, agility)
self.name = name
self.level = level
func level_up() -> void:
self.level += 1
self.health += 10
self.strength += 5
self.agility += 3
print(f"{self.name} leveled up to level {self.level}!")
func take_damage(amount: int) -> void:
# 使用盾牌减少伤害
if self.shield > 0:
self.shield -= amount
if self.shield <= 0:
amount = amount - self.shield
self.shield = 0
# 调用基类的 take_damage 方法
super.take_damage(amount)
func die() -> void:
# 重写基类的 die 方法
print(f"{self.name} has fallen in battle!")
类的可见性
在 GDScript
中,类的属性和方法默认是公开的(public)。这意味着它们可以在任何地方被访问和修改。然而,有时我们希望限制某些属性和方法的访问范围,以提高代码的安全性和可维护性。GDScript
提供了 @export
注解来实现这一目的。
@export 注解
@export
注解用于将类的属性暴露给 Godot 编辑器,使其可以在编辑器中被修改。以下是一个示例:
# 基类 Character
extends Object
@export var health: int
@export var strength: int
@export var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
私有属性和方法
GDScript
没有严格的私有属性和方法的概念,但可以通过命名约定来表示。通常,私有属性和方法的名称以单个下划线 _
开头。以下是一个示例:
# 基类 Character
extends Object
var health: int
var strength: int
var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
# 私有方法
func _heal(amount: int) -> void:
self.health += amount
print(f"Healed for {amount} points. Current health: {self.health}")
受保护属性和方法
受保护属性和方法通常用于子类访问,但不希望在外部被访问。在 GDScript
中,受保护属性和方法的名称以两个下划线 __
开头。以下是一个示例:
# 基类 Character
extends Object
var health: int
var strength: int
var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
# 受保护方法
func __heal(amount: int) -> void:
self.health += amount
print(f"Healed for {amount} points. Current health: {self.health}")
# 子类 Player
extends Character
var name: String
var level: int
func _init(name: String, level: int, health: int, strength: int, agility: int):
# 调用基类的构造函数
super._init(health, strength, agility)
self.name = name
self.level = level
func level_up() -> void:
self.level += 1
self.health += 10
self.strength += 5
self.agility += 3
print(f"{self.name} leveled up to level {self.level}!")
func take_damage(amount: int) -> void:
# 使用盾牌减少伤害
if self.shield > 0:
self.shield -= amount
if self.shield <= 0:
amount = amount - self.shield
self.shield = 0
# 调用基类的 take_damage 方法
super.take_damage(amount)
func die() -> void:
# 重写基类的 die 方法
print(f"{self.name} has fallen in battle!")
# 子类调用受保护方法
func use_potion(potion: Potion) -> void:
self.__heal(potion.amount)
类的实例化
类的实例化是指创建类的具体对象。在 GDScript
中,类的实例化通过 new
关键字来实现。以下是一个示例:
# 基类 Character
extends Object
var health: int
var strength: int
var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
# 子类 Player
extends Character
var name: String
var level: int
func _init(name: String, level: int, health: int, strength: int, agility: int):
# 调用基类的构造函数
super._init(health, strength, agility)
self.name = name
self.level = level
func level_up() -> void:
self.level += 1
self.health += 10
self.strength += 5
self.agility += 3
print(f"{self.name} leveled up to level {self.level}!")
func die() -> void:
# 重写基类的 die 方法
print(f"{self.name} has fallen in battle!")
# 实例化 Player 类
var player = Player.new("Hero", 1, 100, 20, 15)
# 调用 Player 类的方法
player.attack(enemy)
player.level_up()
player.take_damage(50)
类的多态
多态是指子类可以替换基类的实例,并且子类可以提供不同的实现。在 GDScript
中,多态通过继承和方法重写来实现。以下是一个示例:
# 基类 Character
extends Object
var health: int
var strength: int
var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
# 子类 Player
extends Character
var name: String
var level: int
func _init(name: String, level: int, health: int, strength: int, agility: int):
# 调用基类的构造函数
super._init(health, strength, agility)
self.name = name
self.level = level
func level_up() -> void:
self.level += 1
self.health += 10
self.strength += 5
self.agility += 3
print(f"{self.name} leveled up to level {self.level}!")
func die() -> void:
# 重写基类的 die 方法
print(f"{self.name} has fallen in battle!")
# 子类 Enemy
extends Character
var name: String
func _init(name: String, health: int, strength: int, agility: int):
# 调用基类的构造函数
super._init(health, strength, agility)
self.name = name
func die() -> void:
# 重写基类的 die 方法
print(f"{self.name} is defeated!")
# 多态示例
var characters: Array[Character] = []
characters.append(Player.new("Hero", 1, 100, 20, 15))
characters.append(Enemy.new("Monster", 80, 15, 10))
for character in characters:
character.attack(character)
character.take_damage(30)
if character.health <= 0:
character.die()
类的静态方法
静态方法是指不依赖于类的实例属性的方法。在 GDScript
中,静态方法通过 @static
注解来定义。以下是一个示例:
# 基类 Character
extends Object
var health: int
var strength当然,以下是续写的内容,包括对前文的重复以便更好地阅读理解:
```gdscript
# 基类 Character
extends Object
var health: int
var strength: int
var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
静态方法
静态方法是指不依赖于类的实例属性的方法。在 GDScript
中,静态方法通过 @static
注解来定义。静态方法可以直接通过类名调用,而不需要创建类的实例。以下是一个示例:
# 基类 Character
extends Object
var health: int
var strength: int
var agility: int
@static
func create_character(health: int, strength: int, agility: int) -> Character:
return Character.new(health, strength, agility)
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
在这个示例中,create_character
是一个静态方法,用于创建 Character
类的实例。静态方法可以通过类名直接调用,例如:
var character = Character.create_character(100, 20, 15)
静态属性
静态属性是指属于类而不是类的实例的属性。在 GDScript
中,静态属性通常通过类名来访问。以下是一个示例:
# 基类 Character
extends Object
var health: int
var strength: int
var agility: int
@static var total_characters: int = 0
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
# 每创建一个实例,静态属性 total_characters 增加 1
Character.total_characters += 1
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
在这个示例中,total_characters
是一个静态属性,用于记录创建的 Character
实例的总数。每当创建一个新的 Character
实例时,total_characters
会增加 1。静态属性可以通过类名直接访问,例如:
print(f"Total characters: {Character.total_characters}")
类的抽象方法
抽象方法是指在基类中定义但没有实现的方法,子类必须实现这些方法。GDScript
本身没有直接支持抽象方法的概念,但可以通过定义一个空的虚拟方法来实现类似的效果。以下是一个示例:
# 基类 Character
extends Object
var health: int
var strength: int
var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
# 定义一个虚拟的攻击方法
func special_attack(target) -> void:
pass
# 子类 Player
extends Character
var name: String
var level: int
func _init(name: String, level: int, health: int, strength: int, agility: int):
# 调用基类的构造函数
super._init(health, strength, agility)
self.name = name
self.level = level
func level_up() -> void:
self.level += 1
self.health += 10
self.strength += 5
self.agility += 3
print(f"{self.name} leveled up to level {self.level}!")
func die() -> void:
# 重写基类的 die 方法
print(f"{self.name} has fallen in battle!")
# 实现基类的虚拟方法
func special_attack(target) -> void:
print(f"{self.name} uses a special attack!")
target.take_damage(self.strength * 2)
在这个示例中,Character
类定义了一个虚拟的 special_attack
方法。Player
类继承自 Character
并实现了这个方法。如果子类没有实现这个方法,调用它时将不会有任何效果。
类的接口
GDScript
没有直接支持接口的概念,但可以通过定义一个基类并在子类中实现其方法来模拟接口。以下是一个示例:
# 定义一个基类 Character
extends Object
var health: int
var strength: int
var agility: int
func _init(health: int, strength: int, agility: int):
self.health = health
self.strength = strength
self.agility = agility
func attack(target) -> void:
target.take_damage(self.strength)
func take_damage(amount: int) -> void:
self.health -= amount
if self.health <= 0:
self.die()
func die() -> void:
print("I am dead!")
# 定义一个虚拟的方法,模拟接口
func perform_special_action() -> void:
pass
# 子类 Player
extends Character
var name: String
var level: int
func _init(name: String, level: int, health: int, strength: int, agility: int):
# 调用基类的构造函数
super._init(health, strength, agility)
self.name = name
self.level = level
func level_up() -> void:
self.level += 1
self.health += 10
self.strength += 5
self.agility += 3
print(f"{self.name} leveled up to level {self.level}!")
func die() -> void:
# 重写基类的 die 方法
print(f"{self.name} has fallen in battle!")
# 实现基类的虚拟方法
func perform_special_action() -> void:
print(f"{self.name} performs a special action: Use a potion!")
self.health += 20
# 子类 Enemy
extends Character
var name: String
func _init(name: String, health: int, strength: int, agility: int):
# 调用基类的构造函数
super._init(health, strength, agility)
self.name = name
func die() -> void:
# 重写基类的 die 方法
print(f"{self.name} is defeated!")
# 实现基类的虚拟方法
func perform_special_action() -> void:
print(f"{self.name} performs a special action: Roar!")
在这个示例中,Character
类定义了一个虚拟方法 perform_special_action
,而 Player
和 Enemy
子类分别实现了这个方法。这样,我们可以通过 Character
类的引用调用 perform_special_action
方法,而具体的行为将由子类决定。
# 多态示例
var characters: Array[Character] = []
characters.append(Player.new("Hero", 1, 100, 20, 15))
characters.append(Enemy.new("Monster", 80, 15, 10))
for character in characters:
character.attack(character)
character.take_damage(30)
character.perform_special_action()
if character.health <= 0:
character.die()
总结
-
类的定义:使用
extends
关键字来继承基类,类的属性和方法分别用var
和func
关键字定义。 -
继承:通过
extends
关键字实现单继承,使用super
关键字调用基类的方法。 -
重写方法:子类可以重写基类的方法以提供不同的实现。
-
类的可见性:默认属性和方法是公开的,可以通过
@export
注解暴露给 Godot 编辑器,私有属性和方法以单个下划线_
开头,受保护属性和方法以两个下划线__
开头。 -
静态方法和属性:使用
@static
注解定义静态方法,静态属性通过类名访问。 -
抽象方法:通过定义虚拟方法来模拟接口,子类必须实现这些方法。
-
多态:子类可以替换基类的实例,并提供不同的实现。
通过这些概念,可以编写更加模块化和可维护的代码,从而提高游戏开发的效率和质量。希望这些内容能帮助你在 GDScript
中更好地使用类和继承。