前段时间在做一个游戏的跨地图寻路,地图结构及寻路思路说明如下:
地图 表主要字段列表:
`m_id` int(11) unsigned NOT NULL auto_increment,
`m_name` varchar(32) NOT NULL default '' COMMENT '地图名称',
`m_type` tinyint(3) unsigned NOT NULL default '0' COMMENT '地图类型 0非场景地图 1野外 2普通 3家园 4帮会 5副本',
`m_grade` tinyint(3) unsigned NOT NULL default '0' COMMENT '地图分级 0场景地图 1场景区域点 2区域 3世界',
`g1_id` int(11) unsigned NOT NULL default '0' COMMENT '所属场景区域点ID',
`g2_id` int(11) unsigned NOT NULL default '0' COMMENT '所属区域ID',
`m_land` varchar(16) NOT NULL default '' COMMENT '着陆点x,y; 区域地图和区域点地图的m_land存在该区域的着陆地图节点,场景地图的m_land存放该场景的着陆坐标点x,y',
`m_link` varchar(64) NOT NULL default '' COMMENT '连接场景ID和传送点ID',
世界地图(m_grade=3)=>区域地图(m_grade=2)=>场景区域点(m_grade=1)=>场景地图(m_grade=0)
只有场景地图才属于实际地图,玩家可进入;其它地图只提供地图关联,不可进入;
设计寻路流程:
- 判断当前地图与目标地图是否是场景地图(m_grade=0),仅场景地图间可寻路;
- 判断两地图是否属于同一区域(g2_id),不属于同一区域则需要根据区域间的连接关系(m_link)进行寻路,寻路后则根据在区间内的路径终点取其着陆点(m_land)即区域点地图;
- 判断两地图是否属于同一区域点(g1_id),不属于同一区域则需要根据区域间的连接关系(m_link)进行寻路,寻路后则根据在区间内的路径终点取其着陆点(m_land)即区域地图;
- 根据区域间的连接关系(m_link)进行寻路,寻路结果如下:result('list':[(地图ID_1,传送点1),(地图ID_2,传送点2),(地图ID_3,传送点3),...],'land':着陆点[区域场景为坐标,其它为地图ID]);
- 在区域和区域点间寻路时,如果不能到达(无连接关系或关系断开),则会返回直接到达的数据,且传送点为0,如:result('list':[(起始点,0),(终点,0)],‘land’:终点的着陆点)
- 在场景内着陆坐标为0则,由自动选择可移动的坐标点 @param int current 当前场景 @param int target 目标场景 @return: dict result{'err':int,'path':dict} err:成功=0, 失败:地图不存在=1,地图不可到达=2,地图区域没关连=3,地图区域点没关连=4,地图没有关连=5,地图数据不完整=6
以上是我们游戏的跨地图寻路设置思路,在游戏初始化时,会将所有地图以节点方式来存储,每个节点的子节点即是当前地图的连接场景ID,寻路时将在节点间计算最短路径
以下代码为python实现的节点间寻路算法,代码中包含了必要的注释和例子,在节点间寻路完闭后可将节点列表的值 与地图ID作相应处理,即可得到地图的路径、着陆点、坐标等信息,具体依具需求而定
#coding:utf-8
#多地图间最短路径之节点算法
import copy
'''
#树形节点查询算法
节点,存放子节点
@author:yangxs yang.net618@gmail.com
'''
class Node(object):
def __init__(self,id,subnode):
self.id = id
if len(subnode)>0:
self.__subnode = subnode
else:
self.__subnode = None
def getSubnode(self):
return self.__subnode
'''
树形结构,查询节点间最短节点数的路径
'''
class Tree(object):
def __init__(self,nodes):
#所有节点列表 {node_id:objNode}
self.__nodes = nodes
#已经查询过的节点列表
self.__readyNodes = []
self.__found = {}
'''
查询当前节点到目标节点的最短路径
@param int current 当前(起始)节点
@param int target 目标节点
@return list result 节点路径列表,按起始节点到目标节点的顺序排列
'''
def search(self,current,target):
if current == target:
result = [current]
else:
result = []
'''确定当前点节点与目标节点是否存在'''
if self.__nodes.has_key(current) and self.__nodes.has_key(target):
'''
检查当前节点的子节点中是否存在目标节点
1、目标节点属于当前节点的子节点,则直接返回[current,target];
2、目标节点不属于当前节点的子节点,则在子节点的子节点中继续循环查找目标节点,并记录最短节点数的路径列表;
'''
subnodes = self.__nodes[current].getSubnode()
if subnodes != None:
if target in subnodes:
result = [current,target]
else:
self.__readyNodes.append(current)
self.__find(current,subnodes,target)
if self.__found.has_key(current):
result = self.__found[current]
result.insert(0,current)
'''重置已经找到的路径信息'''
self.__found = {}
self.__readyNodes = []
return result
'''
在父节点的子节点列表中循环查找目标节点,并记录最短节点列表
@param int parent 父节点
@param list nodes 子节点列表
@param int target 目标节点
@return: null
'''
def __find(self,parent,nodes,target):
for node in nodes:
'''节点是否已经查询过'''
if node in self.__readyNodes:
if self.__found.has_key(node):
result = [node] + copy.deepcopy(self.__found[node])
self.__founded(parent,result)
else:
if self.__nodes.has_key(node):
subnodes = self.__nodes[node].getSubnode()
if subnodes:
self.__readyNodes.append(node)
if target in subnodes:
self.__founded(node,[target])
self.__founded(parent,[node,target])
else:
self.__find(node,subnodes,target)
if self.__found.has_key(node):
result = [node] + copy.deepcopy(self.__found[node])
self.__founded(parent,result)
'''
更新已经查找到的路径信息,记录最短路径列表
'''
def __founded(self,parent,result):
if self.__found.has_key(parent):
if len(result)<len(self.__found[parent]):
self.__found[parent] = result
else:
self.__found[parent] = result
map = {1:[2],
2:[1,3],
3:[2,9,11],
4:[5,6],
5:[4,7],
6:[4,15],
7:[5,10],
8:[16],
9:[3,12],
10:[7,11,16],
11:[3,10,17],
12:[9,13],
13:[12,14],
14:[13,15],
15:[14,6],
16:[10,8],
17:[11,18],
18:[17]}
print map
nodes = {}
for id,subnode in map.iteritems():
nodes[id] = Node(id,subnode)
tree = Tree(nodes)
result = tree.search(2,16)
print 'result:%s' %result
# result: [2, 3, 11, 10, 16]
result = tree.search(3,18)
print 'result:%s' %result
# result: [3, 11, 17, 18]
result = tree.search(1,18)
print 'result:%s' %result
# result: [1, 2, 3, 11, 17, 18]
此算法改进后可用于公交站点最短寻路算法,暂时无时间修改;
本人能力有限,代码中存在很多改进地方,望各位多指正