不修改引擎代码前提下替代dynamic_cast判断HitActor类型的方案
不使用dynamic_cast判断命中Actor类型的实现方案
前提:完全不修改引擎代码,仅在业务层实现判断逻辑。
方案1:使用typeid运算符(依赖RTTI)
利用C++ RTTI特性的typeid运算符,可以直接获取多态对象的动态类型,和Person类的类型做对比即可完成判断。由于引擎提供的Actor类存在虚析构函数,属于多态类型,可直接使用该方案。
实现代码
#include <typeinfo> // 替换原有dynamic_cast判断逻辑 Actor* hitActor = hr.GetHitActor(); if (hitActor != nullptr && typeid(*hitActor) == typeid(Person)) { // 类型确认后可安全使用static_cast转换 Person* HitPlayer = static_cast<Person*>(hitActor); cout << "Player hit!" << endl; } else { cout << "Person not hit!" << endl; }
优缺点
- 优点:实现简单,无额外维护成本,和
dynamic_cast逻辑接近 - 缺点:依赖RTTI开启(部分游戏引擎默认关闭RTTI);仅能精确匹配类型,如果存在继承自
Person的子类,该方案会将子类实例判定为非Person类型,无法匹配多态继承场景。
方案2:全局实例注册表(不依赖RTTI)
如果引擎关闭了RTTI,可在业务层维护一个全局的存活Person实例指针集合,创建Person实例时注册到集合,销毁时从集合移除,判断时直接查询命中的Actor指针是否在集合中即可。
实现代码
#include <unordered_set> // 业务层全局注册表,存储所有存活的Person实例指针 std::unordered_set<Actor*> g_alivePersonSet; // 封装Person创建逻辑,自动注册 Person* CreatePerson(const std::string& name) { Person* person = new Person(name); g_alivePersonSet.insert(person); return person; } // 封装Person销毁逻辑,自动注销 void DestroyPerson(Person* person) { g_alivePersonSet.erase(person); delete person; } // 命中判断逻辑 Actor* hitActor = hr.GetHitActor(); if (hitActor != nullptr && g_alivePersonSet.count(hitActor)) { Person* HitPlayer = static_cast<Person*>(hitActor); cout << "Player hit!" << endl; } else { cout << "Person not hit!" << endl; } // main函数中创建实例改用封装接口: Person* player1 = CreatePerson("Bob"); Person* player2 = CreatePerson("Phil"); // 销毁实例改用封装接口: DestroyPerson(player1); DestroyPerson(player2);
优缺点
- 优点:完全不依赖RTTI,查询性能远高于
dynamic_cast和typeid,适合高频调用的光线追踪命中判断场景;支持Person派生类的判断,只要子类实例创建时注册到集合即可。 - 缺点:需要确保所有
Person实例的创建和销毁都走封装接口,若引擎内部会自行创建Person实例,该方案无法捕获到这部分实例,会出现判断误差;需要额外维护实例生命周期,操作不当可能出现野指针误判。
方案选择建议
- 如果项目开启了RTTI,且
Person类没有自定义派生类,优先使用typeid方案 - 如果项目关闭了RTTI,且所有
Person实例都由业务层代码创建,优先使用全局注册表方案 - 无特殊限制的情况下,原生
dynamic_cast本身就是最安全的多态类型判断方案,支持继承匹配,无需额外维护逻辑,不需要刻意替换。
内容的提问来源于stack exchange,提问作者mpro34
相关产品推荐
相关产品推荐

