本文的地图配置是针对游戏服务器中的场景和副本用到的地图相关配置。
设计上:
地图配置类型有
1)地图进入需求配置
2)地图阻挡配置(地图编辑器编辑 :地图阻挡 。 安全区域和普通区域)
3)地图区域配置(跳转点、地图特殊区域类型、复活区域、特殊怪出现区域)
4)副本地图怪物配置(npc出生信息)
5)普通地图的怪物配置(npc出生信息)
6)功能性npc配置(功能型npc出生信息)
场景对象继承场地地图基类,初始化时会读取所有的场景地图配置。
地图配置初始化如下:
bool scene_map_base::init(nXMLParser& nxml)
{ if(!map_base::init(nxml)) { return false; } //1)地图进入需求配置 if(!nxml.getNodeAttrStr("xmlname", xmlFileName, MAX_NAME_LEN)//地图配置文件名字 || !nxml.getNodeAttrInt("checklevel", &this->enter_level)//进入等级 || !nxml.getNodeAttrInt("needmoney", &this->enter_money))//进入金钱 { assert(false && "加载地图大小失败."); return false; } g_log->debug("name:%s,level:%u,money:%u",this->name.c_str(), this->enter_level, this->enter_money); //2)地图阻挡(地图编辑器编辑 :地图阻挡 。 安全区域和普通区域) if(!load_map_block()) { assert(false && "加载阻挡失败."); return false; } //3)地图区域配置文件(跳转点、地图区域类型、复活区域、特殊怪出现区域) if(!loadMapConfig()) { assert(false && "加载地图配置文件失败."); return false; } if(((maptype & SceneMapType_Demon) || (maptype & SceneMapType_Castle) || (maptype & SceneMapType_Fort) || (maptype & SceneMapType_Activity_1))) { //4)副本地图怪物配置文件(npc出生信息) if (!loadMapSummonNpcConfig()) { assert(false && "加载副本地图NPC配置文件失败."); return false; } debug_log("map id = %u, maptype = %u", this->id, this->maptype); } else { //5)普通地图的怪物配置文件(npc出生信息) if(!loadMapMonsterConfig())//加载怪物配置文件 { assert(false && "加载怪物配置文件失败."); return false; } } //6)功能性npc配置文件(功能型npc出生信息) if(!loadFuncNpcConfig()) { assert(false && "加载地图配置文件失败."); return false; } return true; }
地图分为安全区域(不可战斗区域)和普通区域(可战斗区域)。
这两个区域都是含有阻挡和非阻挡的格子。加载地图阻挡时会读取地图编辑器生成的配置文件数据到内存的地图数据数组。
阻挡配置文件数据包含:
地图宽高、地图id、名字
普通区域(阻挡和可行走区域)
安全区域(阻挡和可行走区域)
bool scene_map_base::load_map_block() { char tempid[32]; nGraphAlgo::int2str(this->id,tempid); std::string temp_file = ""; temp_file = temp_file + g_xml_config.get_string("Global.Scene.Map") + tempid + "_mapS.xml"; stMapData hdr; nXMLParser xml; if(!xml.loadFile(temp_file.c_str())) { g_log->error("该地图没有阻挡配置文件: %s",temp_file.c_str()); return false; } std::string mapName; xml.setCurNode("res", "config"); if(!xml.isNoneNode()) { xml.getNodeAttrInt("mapId", &hdr.mapid); xml.getNodeAttrStr("mapName",mapName, MAX_NAME_LEN); xml.getNodeAttrInt("colNum", &hdr.width); xml.getNodeAttrInt("rowNum", &hdr.height); } else { g_log->error("找不到res->config配置选项"); return false; } if (!hdr.width || !hdr.height) { error_log("地图[%s,%u]配置宽高错误[%u,%u]",mapName.c_str(),this->id,hdr.width,hdr.height); return false; } //设置地图宽高数据 this->width = hdr.width; this->height = hdr.height; size_t total_size = hdr.width * hdr.height; _block_data.resize(total_size); //普通区域 uint32 check_low = 0; xml.setCurNode("res", "wayLayer", "row"); while(!xml.isNoneNode()) { std::string tile; xml.getNodeAttrStr("mark", tile, 1024); for(uint16 i = 0; i < this->width; ++i) { uint32 index = check_low * this->width + i; if((tile[i] - 48) == 1)//此处为1才是阻挡,是0是可行走区域 { ///可行走区域 _block_data[index].dwFlags = 1; } } check_low++; xml.setNextNode(); } if(check_low != this->height) { g_log->error("行走区域配置的行数不够:%u,%u", check_low, this->height); return false; } //安全区域配置 check_low = 0; xml.setCurNode("res", "safeLayer", "row"); while(!xml.isNoneNode()) { std::string tile; xml.getNodeAttrStr("mark", tile, 1024); for(uint16 i = 0; i < this->width; ++i) { uint32 index = check_low * this->width + i; if((tile[i] - 48) == 3) { ///可行走区域 _block_data[index].dwFlags |= eNewBlockSafe; } } check_low++; xml.setNextNode(); } if(check_low != this->height) { g_log->error("安全区域配置的行数不够:%u,%u", check_low, this->height); return false; } return true; }
第一行是地图具体信息。第三行开始是图档标识(1是阻挡,0是可行走)。
<config mapId="1" mapName="勇者大陆" mapWidth="19712" mapHeight="13824" tileWidth="256" tileHeight="256" cellWidth="32" cellHeight="32" rowNum="432" colNum="616"/>
<wayLayer name="障碍标记层">
<row mark="111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" index="0"/>
。。。。。
</wayLayer>
<safeLayer name="安全区域标记层">
<row mark="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" index="0"/>
。。。
</safeLayer>
<pends>
<item id="82714130" x="10685" y="9483" layer="0" interval="0" key="role/pend/fire/10001"/>
<item id="12420115" x="9794" y="9413" layer="0" interval="0" key="role/pend/safe/10033"/>
<item id="82714135" x="11027" y="9297" layer="0" interval="0" key="role/pend/fire/10001"/>
<item id="82714649" x="12610" y="7139" layer="0" interval="0" key="role/pend/candle/10002"/>
<item id="12420125" x="9868" y="9455" layer="0" interval="0" key="role/pend/safe/10033"/>
<item id="12420129" x="9906" y="9475" layer="0" interval="0" key="role/pend/safe/10033"/>
<item id="12420133" x="9945" y="9496" layer="0" interval="0" key="role/pend/safe/10033"/>
怪物id、出生地xy和随机范围、怪死亡后重生的时间间隔、NPC移动路径、NPC的朝向
bool scene_map::loadMapMonsterConfig() { std::string fullXmlName = ""; char tempid[32]; nGraphAlgo::int2str(this->id,tempid); fullXmlName = fullXmlName + g_xml_config.get_string("Global.Scene.Map") + tempid + "_monster.xml"; nXMLParser xml; if(!xml.loadFile(fullXmlName.c_str())) { g_log->error("该地图没有怪物配置文件: %s",fullXmlName.c_str()); return true; } xml.setCurNode("root", "Npc", "npcborn"); while(! xml.isNoneNode()) { NpcMapData *npcMapData = new NpcMapData(); //默认都不需要回收 npcMapData->birthData.needRecycle = false; npcMapData->birthData.needDelete = false; xml.getNodeAttrInt("id", &npcMapData->birthData.npcID); xml.getNodeAttrInt("num", &npcMapData->num); xml.getNodeAttrInt("x", &npcMapData->birthData.pos_x); xml.getNodeAttrInt("y", &npcMapData->birthData.pos_y); xml.getNodeAttrInt("width", &npcMapData->birthData.width); xml.getNodeAttrInt("height", &npcMapData->birthData.height); xml.getNodeAttrInt("interval", &npcMapData->birthData.interval); xml.getNodeAttrInt("path", &npcMapData->birthData.pathID, 0); xml.getNodeAttrInt("dir", &npcMapData->birthData.direct, _DIR_RANDOM); if(!inm.addNpcMapData(npcMapData)) { g_log->error("地图【%s,%u】怪物出生数据出错:%u",this->name, this->id,npcMapData->birthData.npcID); assert(0); } xml.setNextNode(); } return true; }
地图怪物配置
地图怪物配置(id、出生地xy和随机范围、怪死亡后重生的时间间隔、NPC移动路径、NPC的朝向)
<root>
<Npc>
<npcborn aid="102211590" name="巨人" id="206" x="79" y="258" num="1" width="5" height="5" interval="10" path="2" dir="8"/>
<npcborn aid="2188298" name="" id="195" x="127" y="149" num="1" width="5" height="5" interval="10" path="2" dir="8"/>
<npcborn aid="102211598" name="巨人" id="206" x="97" y="274" num="1" width="5" height="5" interval="10" path="2" dir="8"/>
<npcborn aid="2189341" name="" id="202" x="429" y="334" num="1" width="5" height="5" interval="3" path="2" dir="8"/>
<npcborn aid="21882913" name="" id="195" x="140" y="151" num="1" width="5" height="5" interval="10" path="2" dir="8"/>
<npcborn aid="21882917" name="" id="195" x="153" y="174" num="1" width="5" height="5" interval="10" path="2" dir="8"/>
......
怪物id、出生地xy和随机范围、怪死亡后重生的时间间隔、NPC移动路径、npc波次、NPC的朝向
bool scene_map::loadMapSummonNpcConfig() { std::string fullXmlName = ""; fullXmlName = fullXmlName + g_xml_config.get_string("Global.Scene.Map") + ((const char*)this->xmlFileName) + "_server_npc.xml"; nXMLParser xml; if(!xml.loadFile(fullXmlName.c_str())) { g_log->debug("该地图没有召唤NPC配置文件: %s",fullXmlName.c_str()); return true; } xml.setCurNode("Map", "Npc", "npcborn"); while(! xml.isNoneNode()) { NpcMapData *npcMapData = new NpcMapData(); //默认都不需要回收 npcMapData->birthData.needRecycle = false; npcMapData->birthData.needDelete = false; xml.getNodeAttrInt("id", &npcMapData->birthData.npcID); xml.getNodeAttrInt("num", &npcMapData->num); xml.getNodeAttrInt("x", &npcMapData->birthData.pos_x); xml.getNodeAttrInt("y", &npcMapData->birthData.pos_y); xml.getNodeAttrInt("width", &npcMapData->birthData.width); xml.getNodeAttrInt("height", &npcMapData->birthData.height); xml.getNodeAttrInt("interval", &npcMapData->birthData.interval); xml.getNodeAttrInt("path", &npcMapData->birthData.pathID, 0); xml.getNodeAttrInt("dir", &npcMapData->birthData.direct, _DIR_RANDOM); xml.getNodeAttrInt("lifetime", &npcMapData->birthData.lifetime, 0); xml.getNodeAttrInt("wave", &npcMapData->birthData.wave, 1); summoninm.addNpcMapData(npcMapData); xml.setNextNode(); } return true; }
(1)召唤npc文件配置
<Map>
<Npc>
<npcborn aid="111310202" name="" id="833" x="130" y="56" num="1" width="5" height="5" interval="10" path="2" wave="1" dir="8"/>
<npcborn aid="111310204" name="" id="834" x="137" y="57" num="1" width="5" height="5" interval="10" path="2" wave="1" dir="8"/>
<npcborn aid="111310207" name="" id="833" x="145" y="55" num="1" width="5" height="5" interval="10" path="2" wave="1" dir="8"/>
<npcborn aid="111310380" name="" id="833" x="127" y="84" num="1" width="5" height="5" interval="10" path="2" wave="1" dir="8"/>
......