在獨立遊戲開發中,角色系統是核心玩法邏輯的載體。本文分享一套基於 HGE (Haaf's Game Engine) 2D引擎的 RPG 角色模塊完整設計,採用抽象基類 + 具體派生類 + 管理器單例的經典三層架構,展示 C++ 面向對象思想在遊戲實體管理中的落地實踐。
class FGRoleObject {
public:
// 引擎綁定(靜態單例模式)
static void Init(HGE* m_hge) { hge = m_hge; }
// 純虛函數:強制派生類實現邏輯與渲染
virtual void Logic() = 0;
virtual void Render() = 0;
// 通用屬性 Getter(內聯優化)
float GetX() { return m_x; }
float GetY() { return m_y; }
int GetID() { return m_ID; }
// ... HP, MP, ATK, DEF, SPD 等屬性獲取器 ...
// 碰撞體係:軸對齊包圍盒 (AABB)
void SetRect(float ltx, float lty, float rdx, float rdy)
{ m_rect.Set(ltx, lty, rdx, rdy); }
bool Intersect(FGRoleObject& role)
{ return m_rect.Intersect(&role.m_rect); }
// 屬性批量設置 / 狀態機切換
void SetAttribute(/* 18個屬性參數 */);
void SetMode(ActionMode _mode) { m_actionMode = _mode; }
protected:
// 構造函數受保護:禁止直接實例化基類
FGRoleObject(short _id, CampType _camp, const char* _name, float _x, float _y);
virtual ~FGRoleObject();
// 靜態共享資源
static HGE* hge;
static float Money; // 全局金幣
static float PlayTime; // 全局遊戲時間
// 實例數據成員
CampType m_camp; // 陣營:PLAYER / ENEMY / NEUTRAL
char m_name[256]; // 角色名
short m_id; // 唯一ID
float m_x, m_y; // 世界座標
hgeRect m_rect; // 碰撞盒
// ... 戰鬥屬性、經驗值、移動速度、朝向、狀態枚舉 ...
};
設計亮點:
class FGMyRole : public FGRoleObject {
public:
// 複雜構造:基類參數 + 動畫參數 + UI參數 + 移動速度
FGMyRole(short id, CampType camp, char* name, float x, float y,
float tx, float ty, float tw, float th, float fps,
int nframe, HTEXTURE tex, short uiIndex, float speed);
virtual ~FGMyRole();
// 核心幀邏輯:輸入處理 + 狀態機驅動 + 動畫更新
void Logic() override;
void Render() override { spr[m_direction]->Render(m_x, m_y); }
// 裝備系統:固定5槽位數組
void InitEquips();
ItemObject* Equips[5]; // 武器、防具、鞋、飾品x2
private:
// HGE 動畫資源:8方向各一組
hgeAnimation* spr[8];
float m_fps; int m_nframe;
float m_tx, m_ty, m_tw, m_th; // 材質圖集坐標/尺寸
short m_uiPortraitIndex; // UI頭像索引
};
void FGMyRole::Logic() {
// 1. 當前方向動畫幀更新
spr[m_direction]->Update(hge->Timer_GetDelta());
// 2. 狀態機分發
switch (m_actionMode) {
case MOVE: HandleMoveInput(); break; // WASD 八方向移動 + 斜向速度修正
case WAIT: HandleWaitInput(); break; // 偵測按鍵切換到 MOVE
case ATTACK: HandleAttack(); break; // 播放攻擊動畫,幀結束自動回 WAIT
case HIT: /* 被擊硬直 */ break;
case DEAD: /* 死亡處理 */ break;
}
}
void FGMyRole::HandleMoveInput() {
bool moved = false;
// W 上
if (hge->Input_GetKeyState(HGEK_W)) {
if (hge->Input_GetKeyState(HGEK_D)) { m_x += m_speed; m_y -= m_speed; m_dir = RIGHT_UP; moved = true; }
else if (hge->Input_GetKeyState(HGEK_A)) { m_x -= m_speed; m_y -= m_speed; m_dir = LEFT_UP; moved = true; }
else { m_y -= m_speed; m_dir = UP; moved = true; }
}
// S 下 / D 右 / A 左 ... 類似處理
// 無輸入時:幀歸零、切換待機貼圖、狀態轉 WAIT
if (!moved) { spr[m_dir]->SetFrame(0); SetMode(WAIT); }
// Ctrl 攻擊鍵:切換 ATTACK 狀態、重置動畫幀、提高播放速度
if (hge->Input_GetKeyState(HGEK_CTRL)) { SetMode(ATTACK); /* 設置攻擊貼圖與速度 */ }
}
實現細節:
void FGMyRole::InitEquips() {
for (int i = 0; i < 5; ++i) Equips[i] = nullptr;
switch (m_ID) {
case 0: // 主角
Equips[0] = new Weapon(/* 參數 */); // 武器槽
Equips[1] = new Armor(/* 參數 */); // 防具槽
Equips[2] = new Armor(/* 參數 */); // 鞋子槽
Equips[3] = new Accessory(/* 參數 */); // 飾品1
Equips[4] = new Accessory(/* 參數 */); // 飾品2
break;
case 1: // 同伴
Equips[0] = new Weapon(/* 參數 */);
break;
}
}
固定長度數組 `ItemObject* Equips[5]` 映射固定槽位(武器/防具/鞋/飾品×2),運行期通過多態調用裝備屬性加成。擴展性可改為 `std::vector` 或 `std::map
class FGRoleManager {
public:
// 單例訪問點:首次調用綁定 HGE 並初始化基類靜態指針
static FGRoleManager* Instance(HGE* hge) {
if (!m_instance) {
this->hge = hge;
FGRoleObject::Init(hge);
m_instance = new FGRoleManager();
}
return m_instance;
}
// 統一幀更新:遍歷列表調用多態 Logic/Render
void Logic() { for (auto obj : m_list) obj->Logic(); }
void Render() { for (auto obj : m_list) obj->Render(); }
// 生命週期管理
void AddRole(FGRoleObject& obj) {
m_list.push_front(&obj);
m_idList.push_back(obj.GetID());
}
void DeleteRole(FGRoleObject& obj) { m_list.remove(&obj); }
// 查詢接口
FGRoleObject* GetRole(short id); // O(n) 線性查找
short GetMyRoleCount(); // 陣營統計
short GetEnemyCount();
private:
FGRoleManager() = default;
~FGRoleManager() = default;
std::list<FGRoleObject*> m_list; // 基類指針容器:多態存儲
std::list<short> m_idList; // ID 索引備份
static FGRoleManager* m_instance;
static HGE* hge;
static FGRoleObject* s_currentRole; // 當前選中角色
};
架構優勢:
這套架構在小型 RPG 專案中已驗證穩定高效,清晰分層讓邏輯擴展與調試更加從容。完整代碼已開源於個人倉庫,歡迎交流指正。