游戏角色系统
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 107 课,共 107 课。
挑战
简单让我们构建一个游戏角色系统,结合结构体嵌入、接口和策略模式,打造灵活的战斗机制!你将设计不同类型的角色,让他们以独特的方式攻击和防御,还能在运行时切换技能。
你将把代码分到 5 个文件中:
character.go:定义基础Character结构体,其中包含通用字段:Name(字符串)、Health(整数)和MaxHealth(整数)。添加NewCharacter构造函数以及以下方法:IsAlive() bool:如果生命值大于 0,则返回 trueStatus() string:返回以下格式:[Name]: [Health]/[MaxHealth] HP
interfaces.go:定义用于实现多态的战斗接口:Attacker接口,包含Attack() int:返回造成的伤害Defender接口,包含TakeDamage(damage int) int:应用伤害减免,并返回实际受到的伤害Ability接口,包含Use() string:返回技能效果的描述
characters.go:创建 3 种嵌入Character并实现Attacker和Defender接口的角色类型:Warrior:包含Strength(整数)和Armor(整数)字段。攻击时造成等同于 Strength 的伤害。TakeDamage 会按 Armor 减少受到的伤害(至少受到 1 点伤害)。Mage:包含Intelligence(整数)和MagicResist(整数)字段。攻击时造成等同于 Intelligence * 2 的伤害。TakeDamage 会按 MagicResist / 2 减少受到的伤害(至少受到 1 点伤害)。Archer:包含Agility(整数)和Evasion(整数)字段。攻击时造成等同于 Agility + 5 的伤害。TakeDamage:如果 Evasion > 10,则减少 25% 的伤害(使用整数除法),否则受到全部伤害(至少受到 1 点伤害)。为每种角色添加构造函数:
NewWarrior、NewMage、NewArcher。这些构造函数接收名称、生命值和对应类型的属性值。abilities.go:通过可切换的技能实现策略模式:FireSpell结构体包含Power(整数)。它的Use()返回:Casts fireball for [Power] damageIceSpell结构体包含Power(整数)。它的Use()返回:Casts ice shard for [Power] damagePowerStrike结构体包含Multiplier(整数)。它的Use()返回:Performs power strike at [Multiplier]x damage在 Mage 结构体中添加一个
ability字段(类型为Ability),并添加SetAbility(a Ability)和UseAbility() string方法。如果 ability 为 nil,UseAbility 会返回No ability set。main.go:创建角色并演示战斗机制。读取角色类型(
warrior、mage或archer),然后读取角色名称、生命值和对应类型的属性值。创建相应的角色。然后读取操作数量。每项操作为以下其中一种:
status:输出角色状态attack:输出[Name] attacks for [damage] damagedefend [amount]:应用伤害,并输出[Name] takes [actual] damagesetability [type] [power]:仅适用于法师,用于设置技能(type 为fire、ice或strike)useability:仅适用于法师,输出技能的 Use() 结果
输入内容如下:
- 角色类型、名称、生命值和对应类型的属性值
- 操作数量,然后每项操作各占一行
例如,给定以下输入:
warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100输出应为:
Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damage再例如,给定以下输入:
mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40输出应为:
Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damage再例如,给定以下输入:
archer
Legolas
90
18
15
4
status
attack
defend 24
status输出应为:
Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HP注意,结构体嵌入让每种角色类型都能共享 Character 的字段和方法,而接口则支持多态战斗,任何 Attacker 都能造成伤害,任何 Defender 都能承受伤害。策略模式则通过 Mage 可切换的技能发挥作用:同一个角色可以使用不同的法术,而无需更改其核心实现!
亲手试试
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// 读取角色类型
scanner.Scan()
charType := strings.TrimSpace(scanner.Text())
// 读取角色名称
scanner.Scan()
name := strings.TrimSpace(scanner.Text())
// 读取生命值
scanner.Scan()
health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// 读取第一个角色类型专属属性
scanner.Scan()
stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// 读取第二个角色类型专属属性
scanner.Scan()
stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// TODO:根据 charType 创建对应的角色
// 使用变量:charType、name、health、stat1、stat2
// 对于战士:stat1 = Strength,stat2 = Armor
// 对于法师:stat1 = Intelligence,stat2 = MagicResist
// 对于弓箭手:stat1 = Agility,stat2 = Evasion
// 读取动作数量
scanner.Scan()
numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// 处理每个动作
for i := 0; i < numActions; i++ {
scanner.Scan()
line := strings.TrimSpace(scanner.Text())
parts := strings.Fields(line)
action := parts[0]
// TODO:处理每种动作类型:
// - "status":输出角色的 Status()
// - "attack":输出 "[Name] attacks for [damage] damage"
// - "defend [amount]":计算伤害并输出 "[Name] takes [actual] damage"
// - "setability [type] [power]":为法师设置技能(fire、ice 或 strike)
// - "useability":输出法师的 UseAbility() 结果
_ = action // 实现时删除这一行
_ = fmt.Println // 实现时删除这一行
}
}
面向对象编程中的所有课
自己练习: Go 在线编译器