Funcode坦克大战:C++类继承与多态实战解析
1. 游戏架构设计
在Funcode坦克大战项目中,面向对象编程(OOP)的三大特性——封装、继承和多态得到了充分体现。我们先来看核心类的UML设计:
class CWeapon { // 基础属性 int m_iDir; float m_fSpeedX, m_fSpeedY; int m_iHp; // 虚函数接口 virtual void OnMove(); virtual void OnFire(); virtual void OnSpriteColSprite(); }; class CTankPlayer : public CWeapon { // 玩家特有属性 int key; // 重写虚函数 void OnMove(int iKey, bool bPress) override; void OnFire() override; }; class CTankEnemy : public CWeapon { // AI特有属性 float m_fChangeDirTime; // 新增追踪功能 void trackMove(); };这种架构设计具有以下优势:
- 扩展性:新增坦克类型只需继承基类
- 维护性:公共逻辑集中在基类
- 灵活性:通过虚函数实现差异化行为
2. 多态实现机制
多态在项目中主要通过两种方式实现:
2.1 虚函数重写
以子弹碰撞处理为例,基类定义虚函数:
virtual void OnSpriteColSprite(CWeapon* pSprite);派生类实现各自逻辑:
// 玩家子弹碰撞处理 void CBullet::OnSpriteColSprite(CWeapon* pSprite) { if(pSprite->GetOwner() == ENEMY) { pSprite->SetHp(0); // 直接消灭敌人 } } // 敌人子弹碰撞处理 void CBullet::OnSpriteColSprite(CWeapon* pSprite) { if(pSprite->GetOwner() == PLAYER) { pSprite->SetHp(pSprite->GetHp()-1); // 逐步减少血量 } }2.2 状态模式变体
通过状态变量控制行为变化:
// 在游戏主循环中 if(trackStatus == 1) { m_vWeapon[i]->trackMove(); // 追踪模式 } else { m_vWeapon[i]->OnMove(); // 普通移动 }3. 核心功能实现对比
3.1 追踪功能实现
| 实现方式 | 代码示例 | 优点 | 缺点 |
|---|---|---|---|
| 纯虚函数 | virtual void trackMove()=0; | 强制子类实现 | 需要修改所有派生类 |
| 状态变量 | if(trackStatus) trackMove(); | 动态切换 | 增加条件判断 |
追踪算法核心代码:
void CTankEnemy::trackMove() { float dx = playerX - thisX; float dy = playerY - thisY; if(abs(dx) > abs(dy)) { SetDir(dx > 0 ? RIGHT : LEFT); } else { SetDir(dy > 0 ? DOWN : UP); } // 更新速度 SetSpriteLinearVelocity(GetSpeedX(), GetSpeedY()); }3.2 穿墙功能实现
通过碰撞检测标志位控制:
void CTankPlayer::OnSpriteColSprite(CWeapon* pSprite) { if(strstr(pSprite->GetName(),"wall")) { if(g_GameMain.crossStatus == 1) return; // 穿墙模式忽略碰撞 // 正常碰撞处理 SetSpeed(0, 0); } }4. 特殊能力系统设计
10种特殊能力通过状态机模式实现:
class CGameMain { public: // 状态变量 int invincibleStatus; // 无敌 int crossStatus; // 穿墙 int trackStatus; // 追踪 // 状态切换方法 void ToggleInvincible() { invincibleStatus = !invincibleStatus; m_pTankPlayer->SetHp(invincibleStatus ? 10000 : 2); } };能力触发时序图:
- 玩家按下数字键选择能力
- 按下K键执行
handle(status) - 修改对应状态变量
- 游戏主循环检测状态执行相应逻辑
5. 性能优化技巧
5.1 对象池管理
使用vector存储游戏对象,避免频繁创建销毁:
vector<CWeapon*> m_vWeapon; // 添加子弹 void AddBullet(int iDir, float fPosX, float fPosY, int iOwner) { CBullet* pBullet = new CBullet(name); m_vWeapon.push_back(pBullet); } // 销毁对象 void DeleteWeaponByName(const char* szName) { for(auto it = m_vWeapon.begin(); it != m_vWeapon.end(); ) { if(strcmp((*it)->GetName(), szName) == 0) { delete *it; it = m_vWeapon.erase(it); } else { ++it; } } }5.2 碰撞检测优化
通过分层检测减少计算量:
- 先检测包围盒重叠
- 再检测精确碰撞
- 最后处理碰撞响应
void OnSpriteColSprite(const char *szSrcName, const char *szTarName) { // 快速排除 if(!ShouldCollide(szSrcName, szTarName)) return; // 精确检测 if(CheckPixelCollision(szSrcName, szTarName)) { // 处理碰撞 HandleCollision(szSrcName, szTarName); } }6. 设计模式应用
6.1 状态模式
将坦克的10种特殊能力抽象为状态:
class TankState { public: virtual void Move(CTank* tank) = 0; virtual void Fire(CTank* tank) = 0; }; class NormalState : public TankState { void Move(CTank* tank) override { // 正常移动逻辑 } }; class InvincibleState : public TankState { void Move(CTank* tank) override { // 无敌状态移动逻辑 } };6.2 观察者模式
实现游戏事件通知:
class GameEventNotifier { vector<IGameObserver*> observers; public: void AddObserver(IGameObserver* obs) { observers.push_back(obs); } void Notify(EventType type) { for(auto obs : observers) { obs->OnEvent(type); } } };7. 开发经验分享
在实际开发中,有几个关键点需要特别注意:
- 资源管理:音效文件路径处理要兼容不同平台
- 碰撞精度:调整碰撞体大小确保游戏体验
- 状态同步:多个状态同时激活时的优先级处理
- AI难度:通过调整追踪算法的参数平衡游戏性
一个常见的坑是虚函数忘记加override关键字,这会导致难以发现的逻辑错误。建议开启编译器的-Wsuggest-override警告选项。