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ệ.
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,MAGICALvà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ứcattack(Character target)trả về một String mô tả đòn tấn công và phương thứcgetDamageType()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ứcheal(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 protectedsetHealth(int health)để đảm bảo health luôn nằm trong khoảng từ 0 đến maxHealth. Tạo phương thứctakeDamage(int damage)để giảm health và trả về"[name] takes [damage] damage! Health: [health]/[maxHealth]". Thêm phương thứcisAlive()trả về true nếu health lớn hơn 0. Khai báo phương thức trừu tượnggetCharacterClass()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 khaigetCharacterClass()để trả về"Warrior". Triển khaigetDamageType()để trả vềPHYSICAL. Triển khaiattack(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 khaigetCharacterClass()để trả về"Mage". Triển khaigetDamageType()để trả vềMAGICAL. Triển khaiattack(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 khaiheal(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:strengthCREATE_MAGE:name:maxHealth:level:intelligence:manaATTACK:attackerName:targetNameHEAL:mageName:amountSTATUS: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 healnế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 - AliveThử 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
}
}
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
1Kiến thức cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcTrường (thuộc tính)Phương thức khởi tạoNạp chồng phương thức khởi tạoÔn tập: Máy tính đơn giản4Kế thừa
Kế thừa cơ bản (extends)Từ khóa superGhi đè phương thức (@Override)Chuỗi gọi hàm khởi tạoLớp ObjectKế thừa đơn và đa cấpVì sao không hỗ trợ kế thừa nhiều lớpÔn tập: Phân cấp lớp Employee7Phương thức đặc biệt và lớp Object
Phương thức toString()equals() và hashCode()Phương thức clone()compareTo() và ComparableGiao diện ComparatorÔn tập: sắp xếp tùy chỉnh2Bộ điều chỉnh quyền truy cập và đóng gói
Tổng quan về các mức truy cậpPhương thức getter và setterẨn thông tinTừ khóa finalÔn tập: Trình quản lý tài khoản ngân hàng5Tính đa hình
Kiến thức cơ bản về nạp chồng phương thứcGhi đè phương thức (thời gian chạy)Chuyển kiểu lên và chuyển kiểu xuốngToán tử instanceofLớp và phương thức trừu tượngÔn tập: máy tính hình học8Khái niệm OOP nâng cao
Kết hợp và kế thừaKết tập và hợp thànhLớp bên trong, lớp lồng nhau và lớp ẩn danhKiểu enum và phương thức enumBản ghi (Java 16+)Lớp sealed (Java 17+)11Mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu BuilderMẫu ObserverMẫu Strategy14Thử thách cuối cùng
Nền tảng học trực tuyếnHệ thống ngân hàngPhát triển nhân vật trò chơiDịch vụ cho thuê phương tiện3Thuộc tính lớp và thành viên tĩnh
Biến của instance và biến tĩnhPhương thức tĩnhKhối tĩnhHằng số (static final)Ôn tập: bộ đếm và tiện ích6Giao diện và lớp trừu tượng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnPhương thức mặc định và tĩnh trong giao diệnLớp trừu tượng và giao diệnGiao diện hàmÔn tập: hệ thống thanh toán9Kiểu tổng quát
Giới thiệu về kiểu tổng quátLớp tổng quátPhương thức tổng quátTham số kiểu có giới hạnKý tự đại diện (?, extends, super)Ôn tập: vùng chứa tổng quát12Mẫu thiết kế, phần 2
Mẫu CommandMẫu AdapterMẫu DecoratorMẫu Template MethodMẫu StateMẫu CompositeMẫu IteratorTự luyện tập: Trình biên dịch Java online