Menu
CoddyTech

Hệ thống nhân vật trong trò chơi

Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 107 trên 107.

challenge icon

Thử thách

Dễ

Hãy cùng xây dựng một hệ thống nhân vật trong trò chơi kết hợp nhúng struct, interface và mẫu Strategy để tạo ra cơ chế chiến đấu linh hoạt! Bạn sẽ thiết kế các kiểu nhân vật khác nhau, có thể tấn công và phòng thủ theo cách riêng, với các năng lực có thể thay đổi ngay khi chương trình đang chạy.

Bạn sẽ sắp xếp code trong năm tệp:

  • character.go: Định nghĩa struct Character cơ sở với các trường dùng chung: Name (string), Health (int) và MaxHealth (int). Thêm hàm khởi tạo NewCharacter và các phương thức:
    • IsAlive() bool: trả về true nếu máu lớn hơn 0
    • Status() string: trả về theo định dạng: [Name]: [Health]/[MaxHealth] HP
  • interfaces.go: Định nghĩa các interface chiến đấu cho phép đa hình:
    • Interface Attacker với Attack() int: trả về lượng sát thương gây ra
    • Interface Defender với TakeDamage(damage int) int: giảm sát thương và trả về lượng sát thương thực tế phải chịu
    • Interface Ability với Use() string: trả về mô tả hiệu ứng của năng lực
  • characters.go: Tạo ba kiểu nhân vật nhúng Character và triển khai cả Attacker lẫn Defender:

    Warrior: có trường Strength (int) và Armor (int). Attack trả về Strength làm sát thương. TakeDamage giảm sát thương nhận vào theo Armor (lượng sát thương phải chịu tối thiểu là 1).

    Mage: có các trường Intelligence (int) và MagicResist (int). Attack trả về Intelligence * 2 làm sát thương. TakeDamage giảm sát thương nhận vào theo MagicResist / 2 (lượng sát thương phải chịu tối thiểu là 1).

    Archer: có các trường Agility (int) và Evasion (int). Attack trả về Agility + 5 làm sát thương. TakeDamage: nếu Evasion > 10, giảm sát thương đi 25% (chia số nguyên), nếu không thì chịu toàn bộ sát thương (lượng sát thương phải chịu tối thiểu là 1).

    Thêm hàm khởi tạo cho từng kiểu: NewWarrior, NewMage, NewArcher, nhận tên, máu và các giá trị chỉ số riêng của từng kiểu.

  • abilities.go: Triển khai mẫu Strategy với các năng lực có thể thay đổi:

    Struct FireSpell với Power (int). Use() của struct này trả về: Casts fireball for [Power] damage

    Struct IceSpell với Power (int). Use() của struct này trả về: Casts ice shard for [Power] damage

    Struct PowerStrike với Multiplier (int). Use() của struct này trả về: Performs power strike at [Multiplier]x damage

    Thêm trường ability (kiểu Ability) vào struct Mage, cùng các phương thức SetAbility(a Ability) và UseAbility() string. UseAbility trả về No ability set nếu ability là nil.

  • main.go: Tạo nhân vật và minh họa cơ chế chiến đấu.

    Đọc kiểu nhân vật (warrior, mage hoặc archer), rồi đọc tên, máu và các chỉ số riêng của kiểu nhân vật đó. Tạo nhân vật tương ứng.

    Sau đó, đọc số lượng hành động. Mỗi hành động là một trong các mục sau:

    • status: in trạng thái của nhân vật
    • attack: in [Name] attacks for [damage] damage
    • defend [amount]: áp dụng sát thương và in [Name] takes [actual] damage
    • setability [type] [power]: chỉ dành cho mage, đặt năng lực (type là fire, ice hoặc strike)
    • useability: chỉ dành cho mage, in kết quả của Use() trong năng lực

Chương trình sẽ nhận các đầu vào sau:

  • Kiểu nhân vật, tên, máu và các chỉ số riêng của kiểu nhân vật đó
  • Số lượng hành động, sau đó là từng hành động trên một dòng riêng

Ví dụ, với đầu vào:

warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100

Đầu ra sẽ là:

Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damage

Với đầu vào:

mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40

Đầu ra sẽ là:

Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damage

Với đầu vào:

archer
Legolas
90
18
15
4
status
attack
defend 24
status

Đầu ra sẽ là:

Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HP

Hãy chú ý cách nhúng struct giúp mỗi kiểu nhân vật dùng chung các trường và phương thức cơ sở của Character, còn interface cho phép đa hình trong chiến đấu, nhờ đó bất kỳ Attacker nào cũng có thể gây sát thương và bất kỳ Defender nào cũng có thể nhận sát thương. Mẫu Strategy thể hiện rõ ưu điểm qua các năng lực có thể thay đổi của Mage: cùng một nhân vật có thể dùng các phép khác nhau mà không cần thay đổi phần triển khai cốt lõi!

Tự làm thử

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// Đọc loại nhân vật
	scanner.Scan()
	charType := strings.TrimSpace(scanner.Text())

	// Đọc tên nhân vật
	scanner.Scan()
	name := strings.TrimSpace(scanner.Text())

	// Đọc lượng máu
	scanner.Scan()
	health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Đọc chỉ số đầu tiên theo loại nhân vật
	scanner.Scan()
	stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Đọc chỉ số thứ hai theo loại nhân vật
	scanner.Scan()
	stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// TODO: Tạo nhân vật phù hợp dựa trên charType
	// Sử dụng các biến (variable): charType, name, health, stat1, stat2
	// Với warrior: stat1 = Strength, stat2 = Armor
	// Với mage: stat1 = Intelligence, stat2 = MagicResist
	// Với archer: stat1 = Agility, stat2 = Evasion

	// Đọc số lượng hành động
	scanner.Scan()
	numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Xử lý từng hành động
	for i := 0; i < numActions; i++ {
		scanner.Scan()
		line := strings.TrimSpace(scanner.Text())
		parts := strings.Fields(line)
		action := parts[0]

		// TODO: Xử lý từng loại hành động:
		// - "status": in ra Status() của nhân vật
		// - "attack": in ra "[Name] attacks for [damage] damage"
		// - "defend [amount]": áp dụng sát thương và in ra "[Name] takes [actual] damage"
		// - "setability [type] [power]": với pháp sư, thiết lập kỹ năng (fire, ice hoặc strike)
		// - "useability": với pháp sư, in ra kết quả của UseAbility()

		_ = action // Xóa dòng này khi triển khai
		_ = fmt.Println // Xóa dòng này khi triển khai
	}
}

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Go online