AOI主要有九宫格、灯塔和十字链表的算法实现。本文阐述十字链表的实现
本文是二维地图,将地图内的对象按照坐标值,从小到大分在x轴和y轴两个链表上。
如果是三维地图,则还需要维护多一个z轴的链表
啥子都不说,上代码
AOI视野范围的节点类
import math
class CEntity(object):
def __init__(self,cid,x,y,distance):
self.cid = cid
self.x = x
self.y = y
self.radius = distance
def __str__(self):
return "[%s:(%s-%s)]" % (self.cid,self.x,self.y)
AOI的场景类
class CScene(object):
def __init__(self):
self.m_mapEntity = {}
self.m_listXentity = []
self.m_listYentity = []
def add(self,cid,x,y,distance = 2):
pentity = self.m_mapEntity.get(cid,None)
if pentity:
return
pentity = CEntity(cid,x,y,distance)
self.m_mapEntity[cid] = pentity
indexx = len(self.m_listXentity)
for i in range(len(self.m_listXentity)):
if x < self.m_listXentity[i].x:
indexx = i
break
indexy = len(self.m_listYentity)
for j in range(len(self.m_listYentity)):
if y < self.m_listYentity[j].y:
indexy = j
break
self.m_listXentity.insert(indexx,pentity)
self.m_listYentity.insert(indexy,pentity)
entitymap = self.getRangeSet(pentity)
print("add entity {0}".format(pentity))
for entity in entitymap.values():
print("send {0} entermsg to {1}".format(pentity,entity)) #通知entity,pentity进入视野范围
print("send {0} entermsg to {1}".format(entity,pentity)) #通知pentity,entity进入视野范围
def move(self,cid,x,y):
pentity = self.m_mapEntity.get(cid,None)
if not pentity:
return
oldEntityMap = self.getRangeSet(pentity)
self.updateEntityPosition(pentity,x,y)
newEntityMap = self.getRangeSet(pentity)
for cid,entity in oldEntityMap.items():
if newEntityMap.get(cid,None) != None:
print("send {0} movemsg to {1}".format(pentity,entity))
print("send {0} movemsg to {1}".format(entity,pentity))
else:
print("send {0} leavemsg to {1}".format(pentity,entity))
print("send {0} leavemsg to {1}".format(entity,pentity))
pass
for cid,entity in newEntityMap.items():
if oldEntityMap.get(cid,None) == None:
print("send {0} entermsg to {1}".format(pentity,entity))
print("send {0} entermsg to {1}".format(entity,pentity))
def leave(self,cid):
pentity = self.m_mapEntity.get(cid,None)
if not pentity:
return
print("leave entity {0}".format(pentity))
leaveEntityMap = self.getRangeSet(pentity)
for _,entity in leaveEntityMap.items():
print("send {0} leavemsg to {1}".format(pentity,entity))
self.m_listXentity.remove(pentity)
self.m_listYentity.remove(pentity)
self.m_mapEntity.pop(cid,None)
pentity = None
def getRangeSet(self,pentity):
if pentity == None:
return
x_map = {}
xpos = self.m_listXentity.index(pentity)
for pos in range(xpos - 1,-1,-1):
curentity = self.m_listXentity[pos]
if math.fabs(curentity.x - pentity.x) <= pentity.radius:
x_map[curentity.cid] = curentity
else:
break
for pos in range(xpos + 1,len(self.m_listXentity),1):
curentity = self.m_listXentity[pos]
if math.fabs(curentity.x - pentity.x) <= pentity.radius:
x_map[curentity.cid] = curentity
else:
break
pEntityMap = {}
ypos = self.m_listYentity.index(pentity)
for pos in range(ypos - 1,-1,-1):
curentity = self.m_listYentity[pos]
if math.fabs(curentity.y - pentity.y) <= pentity.radius:
if x_map.get(curentity.cid,None) != None:
pEntityMap[curentity.cid] = curentity
else:
break
for pos in range(ypos + 1,len(self.m_listYentity),1):
curentity = self.m_listYentity[pos]
if math.fabs(curentity.y - pentity.y) <= pentity.radius:
if x_map.get(curentity.cid,None) != None:
pEntityMap[curentity.cid] = curentity
else:
break
return pEntityMap
def updateEntityPosition(self,pentity,x,y):
oldx = pentity.x
oldy = pentity.y
pentity.x = x
pentity.y = y
xpos = self.m_listXentity.index(pentity)
if oldx <= x:
startindex = xpos + 1
endindex = len(self.m_listXentity)
step = 1
else:
startindex = xpos - 1
endindex = 0
step = -1
indexx = xpos
for i in range(startindex,endindex,step):
if x >= self.m_listXentity[i].x:
indexx = i
else:
break
ypos = self.m_listYentity.index(pentity)
if oldy <= y:
startindex = ypos + 1
endindex = len(self.m_listYentity)
step = 1
else:
startindex = ypos - 1
endindex = 0
step = -1
indexy = ypos
for i in range(startindex,endindex,step):
if y >= self.m_listYentity[i].y:
indexy = i
else:
break
self.m_listXentity.remove(pentity)
self.m_listYentity.remove(pentity)
self.m_listXentity.insert(indexx,pentity)
self.m_listYentity.insert(indexy,pentity)
pass
def dump(self):
print("xlist:")
for entity in self.m_listXentity:
print(entity)
print("ylist:")
for entity in self.m_listYentity:
print(entity)
def main():
scene = CScene()
print("步骤一:添加5个对象")
scene.add(1,1,5)
scene.add(2,2,2)
scene.add(3,3,1)
scene.add(4,5,3)
scene.add(5,6,6)
print("步骤二:添加一个新的对象6到坐标(3,3)")
scene.add(6,3,3)
# scene.dump()
print("步骤三:把对象6移动到坐标(4,4)")
scene.move(6,4,4)
# scene.dump()
print("步骤四:移除这6个对象")
for i in range(7):
scene.leave(i)
pass
if __name__ == "__main__":
main()
add:添加节点。。
move:节点移动
leave:节点离开
getRangeSet(self,pentity):获取指定对象的AOI对象列表 返回一个map
updateEntityPosition(self,pentity,x,y) 更新对象CEntity到坐标(x,y)
运行结果如下:
附带分析: