Menu
CoddyTech

Phát triển nhân vật trò chơi

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

Trong thử thách này, bạn sẽ xây dựng một hệ thống Phát triển nhân vật trong trò chơi để thể hiện sức mạnh của OOP trong việc mô hình hóa các đối tượng phức tạp, có tính tương tác.

Thiết kế hệ thống phân cấp nhân vật, trong đó lớp cơ sở Character xác định các thuộc tính chung như máu, tên và cấp độ. Các lớp chuyên biệt như Warrior, Mage và Archer mở rộng lớp cơ sở này với những khả năng và phong cách chiến đấu riêng.

Interface rất phù hợp trong trường hợp này: Attackable có thể định nghĩa các phương thức chiến đấu, còn Healable xử lý các khả năng hồi phục. Mage có thể triển khai cả hai, trong khi Warrior chỉ tập trung vào tấn công.

Bạn có thể cân nhắc dùng mẫu Strategy cho các cách tấn công khác nhau, để nhân vật linh hoạt chuyển đổi phong cách chiến đấu. Mẫu State có thể quản lý các trạng thái của nhân vật như bình thường, trúng độc hoặc choáng, vốn ảnh hưởng đến hành vi.

Hãy nghĩ về tính hợp thành (composition) trong các hệ thống trang bị, nơi nhân vật “có” vũ khí và áo giáp thay vì kế thừa từ chúng. Dùng kiểu liệt kê (enum) cho lớp nhân vật hoặc loại sát thương, đồng thời áp dụng tính đóng gói (encapsulation) để bảo vệ các chỉ số khỏi những thay đổi không hợp lệ.

challenge icon

Thử thách

Dễ

Hãy cùng xây dựng hệ thống phát triển nhân vật trong game, thể hiện sức mạnh của lập trình hướng đối tượng (OOP) trong việc mô hình hóa các thực thể game phức tạp, có tính tương tác! Bạn sẽ tạo một hệ thống phân cấp nhân vật với nhiều phong cách chiến đấu, khả năng đặc biệt và một hệ thống trạng thái ảnh hưởng đến cách nhân vật hành xử.

Bạn sẽ tổ chức code thành bảy tệp:

  • DamageType.java: Tạo một enum đại diện cho các loại sát thương khác nhau trong game. Bao gồm các giá trị PHYSICAL, MAGICAL và RANGED. Thêm một trường riêng tư description (String), một hàm khởi tạo gán giá trị cho trường này và một phương thức getter. Sát thương vật lý có mô tả "brute force", sát thương phép thuật có mô tả "arcane energy", còn sát thương tầm xa có mô tả "precision strike".
  • Attackable.java: Tạo một interface định nghĩa các khả năng chiến đấu. Khai báo phương thức attack(Character target) trả về một String mô tả đòn tấn công và phương thức getDamageType() trả về một DamageType.
  • Healable.java: Tạo một interface cho các khả năng hồi phục. Khai báo phương thức heal(int amount) trả về một String mô tả hành động hồi máu.
  • Character.java: Tạo một lớp cơ sở trừu tượng cho tất cả nhân vật trong game. Bao gồm các trường riêng tư name (String), health (int), maxHealth (int) và level (int). Hàm khởi tạo nhận name, maxHealth và level, đồng thời ban đầu đặt health bằng maxHealth. Thêm các phương thức getter cho tất cả các trường. Thêm phương thức protected setHealth(int health) để đảm bảo health luôn nằm trong khoảng từ 0 đến maxHealth. Tạo phương thức takeDamage(int damage) để giảm health và trả về "[name] takes [damage] damage! Health: [health]/[maxHealth]". Thêm phương thức isAlive() trả về true nếu health lớn hơn 0. Khai báo phương thức trừu tượng getCharacterClass() trả về một String.
  • Warrior.java: Tạo một lớp kế thừa Character và triển khai Attackable. Thêm trường riêng tư strength (int). Hàm khởi tạo nhận name, maxHealth, level và strength. Triển khai getCharacterClass() để trả về "Warrior". Triển khai getDamageType() để trả về PHYSICAL. Triển khai attack(Character target) để gây sát thương bằng strength cộng với level, gọi phương thức takeDamage của mục tiêu và trả về "[name] strikes with [damageType description] for [damage] damage!", theo sau là ký tự xuống dòng và kết quả từ takeDamage.
  • Mage.java: Tạo một lớp kế thừa Character và triển khai cả Attackable lẫn Healable. Thêm các trường riêng tư intelligence (int) và mana (int). Hàm khởi tạo nhận name, maxHealth, level, intelligence và mana. Triển khai getCharacterClass() để trả về "Mage". Triển khai getDamageType() để trả về MAGICAL. Triển khai attack(Character target) để gây sát thương bằng intelligence nhân với 2, theo cùng định dạng như Warrior. Triển khai heal(int amount) để hồi máu (dùng setHealth) và trả về "[name] channels healing magic! Health: [health]/[maxHealth]".
  • Main.java: Hãy đưa hệ thống game của bạn vào hoạt động! Bạn sẽ nhận một chuỗi lệnh phân tách bằng dấu phẩy:
    • CREATE_WARRIOR:name:maxHealth:level:strength
    • CREATE_MAGE:name:maxHealth:level:intelligence:mana
    • ATTACK:attackerName:targetName
    • HEAL:mageName:amount
    • STATUS:characterName

    Duy trì một tập hợp các nhân vật. Xử lý từng lệnh và in ra:

    • CREATE_WARRIOR/CREATE_MAGE: [name] the [characterClass] joins the battle!
    • ATTACK: In ra kết quả tấn công (String do phương thức attack trả về)
    • HEAL: In ra kết quả hồi máu hoặc [name] cannot heal nếu nhân vật không phải Mage
    • STATUS: [name] ([characterClass]) - Level [level] - Health: [health]/[maxHealth] - [Alive/Defeated]

    Sau khi xử lý tất cả lệnh, in ra --- Battle Summary ---, rồi in trạng thái của từng nhân vật.

Bạn sẽ nhận một đầu vào: chuỗi lệnh được phân tách bằng dấu phẩy.

Ví dụ, với đầu vào CREATE_WARRIOR:Thorin:100:5:15,CREATE_MAGE:Gandalf:80:7:12:50,ATTACK:Thorin:Gandalf,HEAL:Gandalf:25,STATUS:Gandalf, đầu ra sẽ là:

Thorin the Warrior joins the battle!
Gandalf the Mage joins the battle!
Thorin strikes with brute force for 20 damage!
Gandalf takes 20 damage! Health: 60/80
Gandalf channels healing magic! Health: 80/80
Gandalf (Mage) - Level 7 - Health: 80/80 - Alive
--- Battle Summary ---
Thorin (Warrior) - Level 5 - Health: 100/100 - Alive
Gandalf (Mage) - Level 7 - Health: 80/80 - Alive

Thử thách này kết hợp tính kế thừa (hệ thống phân cấp Character), triển khai nhiều interface (Mage triển khai cả Attackable và Healable), enum có phương thức (DamageType), tính đa hình (xử lý thống nhất các kiểu nhân vật khác nhau) và tính đóng gói (quản lý health bằng protected) trong một hệ thống game hấp dẫn!

Tự làm thử

import java.util.Scanner;
import java.util.HashMap;
import java.util.Map;
import java.util.LinkedHashMap;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Dùng một tập hợp để lưu các nhân vật (LinkedHashMap giữ nguyên thứ tự thêm vào)
        // LinkedHashMap<String, Character> characters = new LinkedHashMap<>();
        
        // TODO: Tách input theo dấu phẩy để lấy từng lệnh
        // String[] commands = input.split(",");
        
        // TODO: Xử lý từng lệnh:
        // - CREATE_WARRIOR:name:maxHealth:level:strength
        //   In ra: "[name] the Warrior joins the battle!"
        // 
        // - CREATE_MAGE:name:maxHealth:level:intelligence:mana
        //   In ra: "[name] the Mage joins the battle!"
        // 
        // - ATTACK:attackerName:targetName
        //   In ra kết quả tấn công từ phương thức attack
        // 
        // - HEAL:mageName:amount
        //   In ra kết quả hồi máu, hoặc "[name] cannot heal" nếu không phải là Mage
        // 
        // - STATUS:characterName
        //   In ra: "[name] ([characterClass]) - Level [level] - Health: [health]/[maxHealth] - [Alive/Defeated]"
        
        // TODO: Sau khi xử lý tất cả các lệnh, in ra "--- Battle Summary ---"
        // Sau đó in trạng thái của từng nhân vật
    }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

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