配置读取流程
配置读取的本质就是把xml文件中的数据读取到程序中定义的数据结构中去。
开始读取配置
首先要提醒的是,在读取的过程中,我们必须做严格的检查,对读取失败的情况给出易读的说明。通常情况,读取失败意味着进程终止。
当程序启动后,首先要做的事情就是加载配置,游戏中的代码硬编码配置较少,大部分都是通过配置文件来动态获取的。
配置之间也会有依赖关系,比如大部分的逻辑配置都依赖于物品配置,解决依赖的办法就是让物品配置先于逻辑配置加载。在World::InitGameWorld
方法中就是全部游戏配置的加载步骤,其调用顺序是:
ServerLogic::Start World::Start World::InitGameWorld ... LogicConfigManager::Init yourconfig::Init // 你自己要实现的配置
我们大部分的配置都属于逻辑配置,它们被统一放在了(LogicConfigManager)里面管理。
定义配置结构体
我们需要把配置表的内容抽象成代码中数据结构。我们的配置表就是一张行列二维表,因此用数组抽象是最合适的。
比如,有这样的配置结构:
cs | cs | cs | cs | cs |
---|---|---|---|---|
宠物id | 激活所需物品 | 攻击 | 防御 | 血量 |
id | active_item_id | gongji | fangyu | maxhp |
0 | 26000 | 100 | 100 | 100 |
1 | 26001 | 200 | 200 | 200 |
2 | 26002 | 300 | 300 | 300 |
前两行是说明,真正有效的内容从第三行开始。cs代表配置内容生成到客户端(c)和服务端(s),中文的说明是给策划看的。
我们可以用这样的数据结构来抽象这个配置:
struct PetConfigItem { PetConfigItem(): id(0), active_item_id(), gongji(0), fangyu(0), maxhp(0){} int id; // 宠物id ItemID active_item_id; // 激活所需物品 int gongji; // 攻击 int fangyu; // 防御 int maxhp; // 血量 }; static const int MAX_PET_COUNT = 8; PetConfigItem m_petconfig_list[MAX_PET_COUNT]; // 宠物配置
提供获取配置的接口
配置读取完毕后,必须提供一个接口访问配置内容。这个接口的返回值必须是一个const指针(或引用)。
比如要获取一个宠物配置项,可以这样编写接口:
inline bool IsValidPetID(int id) { // 保证是合法的、不会导致数组越界的id // ... } const PetConfigItem* GetPetConfigItem(int id) { if (IsValidPetID(id)) { return &m_petconfig_list[id]; } return nullptr; }
在实际编程中,建议把这样有关数组下标判断的工作放到一个简单的inline函数中去。因为你可能在许多地方要做这样的判断工作,而且一旦出现错误,后果非常严重。