Menu
CoddyTech

游戏角色系统

本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 107 课,共 107 课。

challenge icon

挑战

简单

让我们构建一个游戏角色系统,结合结构体嵌入、接口和策略模式,打造灵活的战斗机制!你将设计不同类型的角色,让他们以独特的方式攻击和防御,还能在运行时切换技能。

你将把代码分到 5 个文件中:

  • character.go:定义基础 Character 结构体,其中包含通用字段:Name(字符串)、Health(整数)和 MaxHealth(整数)。添加 NewCharacter 构造函数以及以下方法:
    • IsAlive() bool:如果生命值大于 0,则返回 true
    • Status() 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] damage

    IceSpell 结构体包含 Power(整数)。它的 Use() 返回:Casts ice shard for [Power] damage

    PowerStrike 结构体包含 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] damage
    • defend [amount]:应用伤害,并输出 [Name] takes [actual] damage
    • setability [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 在线编译器