Menu
CoddyTech

Phát triển nhân vật trong game

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

challenge icon

Thử thách

Dễ

Hãy xây dựng hệ thống Phát triển nhân vật trong trò chơi, nơi các lớp nhân vật khác nhau chiến đấu bằng những kỹ năng riêng biệt. Bạn sẽ tạo một hệ thống phân cấp nhân vật cùng hệ thống kinh nghiệm và lên cấp, qua đó minh họa cách tính kế thừa, giao diện, trait và tính đóng gói phối hợp với nhau trong bối cảnh trò chơi.

Bạn sẽ sắp xếp code trong sáu tệp:

  • Ability.php: Định nghĩa giao diện Ability với hai phương thức: getName(): string và use(Character $target): string. Giao diện này đảm bảo mọi kỹ năng đều có thể được sử dụng theo cách nhất quán, bất kể hiệu ứng cụ thể của chúng.
  • CombatLogger.php: Tạo trait CombatLogger cung cấp phương thức logAction(string $action): string. Phương thức này sẽ trả về chuỗi hành động với tiền tố "[COMBAT] ". Các nhân vật sẽ dùng trait này để định dạng thông báo chiến đấu nhất quán.
  • Character.php: Bao gồm các tệp Ability và CombatLogger. Tạo lớp trừu tượng Character sử dụng trait CombatLogger. Dùng cú pháp khởi tạo thuộc tính trong hàm khởi tạo cho name (string, public readonly). Thêm các thuộc tính private cho health (int, bắt đầu từ 100), level (int, bắt đầu từ 1) và experience (int, bắt đầu từ 0). Triển khai:
    • getHealth(): int và getLevel(): int: các phương thức getter cho thuộc tính private
    • takeDamage(int $amount): void: giảm máu nhưng không bao giờ xuống dưới 0
    • heal(int $amount): void: tăng máu nhưng không bao giờ vượt quá 100
    • gainExperience(int $xp): string: cộng XP; nếu kinh nghiệm đạt 100 trở lên thì lên cấp (đặt lại XP thành 0, tăng cấp, hồi 20 HP), trả về "[name] leveled up to [level]!"; nếu không thì trả về "[name] gained [xp] XP"
    • isAlive(): bool: trả về true nếu máu lớn hơn 0
    • abstract getSpecialAbility(): Ability: mỗi loại nhân vật cung cấp kỹ năng riêng
    • attack(Character $target): string: gây 10 sát thương lên mục tiêu, dùng logAction() để trả về "[COMBAT] [name] attacks [target name] for 10 damage"
  • Warrior.php: Bao gồm tệp Character. Tạo lớp Warrior kế thừa Character. Triển khai getSpecialAbility() để trả về một lớp ẩn danh triển khai Ability:
    • getName() trả về "Shield Bash"
    • use() gây 25 sát thương và trả về "[COMBAT] Shield Bash hits [target name] for 25 damage!" (dùng logAction() của trait)
  • Mage.php: Bao gồm tệp Character. Tạo lớp Mage kế thừa Character. Triển khai getSpecialAbility() để trả về một lớp ẩn danh triển khai Ability:
    • getName() trả về "Fireball"
    • use() gây 35 sát thương và trả về "[COMBAT] Fireball engulfs [target name] for 35 damage!"
  • main.php: Bao gồm các tệp Warrior và Mage. Bạn sẽ nhận hai đầu vào: dữ liệu nhân vật (JSON) và một chuỗi các hành động chiến đấu (JSON).

    Định dạng JSON của nhân vật:

    [{"type": "warrior", "name": "Thorin"}, {"type": "mage", "name": "Gandalf"}]

    Định dạng JSON của hành động:

    [{"action": "attack", "attacker": 0, "target": 1}, {"action": "special", "attacker": 1, "target": 0}, {"action": "xp", "character": 0, "amount": 100}, {"action": "status", "character": 0}]

    Tạo tất cả nhân vật và lưu vào một mảng (chỉ số 0, 1, v.v.). Xử lý từng hành động:

    • "attack": Nhân vật ở chỉ số attacker tấn công mục tiêu; in ra kết quả
    • "special": Nhân vật dùng kỹ năng đặc biệt lên mục tiêu; in ra kết quả
    • "xp": Nhân vật nhận kinh nghiệm; in ra kết quả
    • "heal": Nhân vật hồi máu theo lượng đã cho; in ra "[name] healed to [health] HP"
    • "status": In ra "[name] - Level [level], HP: [health], Alive: [Yes/No]"

    In mỗi kết quả trên một dòng mới.

Hệ thống này minh họa cách lớp trừu tượng định nghĩa hành vi chung của nhân vật, giao diện đảm bảo các kỹ năng tuân theo một giao diện nhất quán, trait cung cấp chức năng ghi nhật ký có thể tái sử dụng, còn lớp ẩn danh tạo các cách triển khai kỹ năng riêng mà không làm codebase của bạn rối thêm với những lớp chỉ dùng một lần.

Tự làm thử

<?php
require_once 'Warrior.php';
require_once 'Mage.php';

// Đọc đầu vào
$charactersJson = trim(fgets(STDIN));
$actionsJson = trim(fgets(STDIN));

// Phân tích cú pháp đầu vào JSON
$charactersData = (array)json_decode($charactersJson, true);
$actionsData = (array)json_decode($actionsJson, true);

// TODO: Tạo mảng để lưu các đối tượng nhân vật
$characters = [];

// TODO: Duyệt $charactersData và tạo các đối tượng Warrior hoặc Mage
// dựa trên trường "type", rồi lưu chúng vào mảng $characters

// TODO: Xử lý từng hành động trong $actionsData
// Xử lý các loại hành động sau:
// - "attack": Nhân vật tại chỉ số kẻ tấn công tấn công mục tiêu; in ra kết quả
// - "special": Nhân vật dùng getSpecialAbility()->use() lên mục tiêu; in ra kết quả
// - "xp": Nhân vật nhận kinh nghiệm; in ra kết quả
// - "heal": Nhân vật hồi máu; in ra "[name] healed to [health] HP"
// - "status": In ra "[name] - Level [level], HP: [health], Alive: [Yes/No]"

?>

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 PHP online