游戏算法-AOI十字链表入门简单版python

AOI十字链表python简单实现,仅用作学习,理解基本原理

AOI一般是C++去实现,本文章python写仅用来入门了解

场景内维护两个实体链表

 list_x和list_y

所有实体根据x坐标大小排序,在list_x上
所有实体根据y坐标大小排序,在list_y上
 

实体位置改变时候,需要改变实体在list_x,list_x的位置

实体的AOI范围, 根据AOI半径 在list_x,list_x上遍历即可

十字链表发在计算AOI范围内对象时,计算量非常小,且跟空间大小无关,因此省去大量无效的遍历过程。十字链表法也有一些需要注意的地方,因为在两条链表中都必须按照大小顺序进行排列,因此对象在不停的移动,会带来比较大量的计算。

 python版本:2.7

# coding: utf-8


class Entity(object):
	def __init__(self, eid, x, y):
		self.id = eid

		self.x = x
		self.y = y
		self.radius = 5  # AOI半径

	def __str__(self):
		return "<{0},{1}-{2}>".format(self.id, self.x, self.y)
	
	def enter(self, pobj):
		print("{0} enter {1} view".format(pobj, self))

	def leave(self, pobj):
		print("{0} leave {1} view".format(pobj, self))

	def move(self, pobj):
		print("{0} move in {1} view".format(pobj, self))


class Scene(object):
	def __init__(self):
		self.map_entity = {}
		self.list_x = []  # [实例ID] 实际应用链表 + 跳跃表
		self.list_y = []

	def __str__(self):
		return "<list_x: {0}, list_y: {1}, entities: {2}>".format(self.list_x, self.list_y, self.map_entity.keys())

	def add_by_entity(self, entity):
		# 添加一个新的对象
		self.map_entity[entity.id] = entity

		pos = len(self.list_x)
		for i, eid in enumerate(self.list_x):
			x = self.map_entity[eid].x
			if entity.x < x:
				pos = i
				break

		self.list_x.insert(pos, entity.id)

		pos = len(self.list_y)
		for j, eid in enumerate(self.list_y):
			y = self.map_entity[eid].y
			if entity.y < y:
				pos = j
				break

		self.list_y.insert(pos, entity.id)
		return entity

	def remove(self, eid):
		entity = self.map_entity.pop(eid)
		for i, eid in enumerate(self.list_x):
			if eid == entity.id:
				del self.list_x[i]
				break

		for j, eid in enumerate(self.list_y):
			if eid == entity.id:
				del self.list_y[j]
				break

	def get_range_entities(self, entity):
		# 获取指定对象的AOI对象列表
		ret_set = set([])
		for ob_id in self.list_x:
			ob = self.map_entity[ob_id]
			if abs(ob.x - entity.x) <= entity.radius:
				if ob != entity:
					ret_set.add(ob)

		for ob_id in self.list_y:
			ob = self.map_entity[ob_id]
			if abs(ob.y - entity.y) <= entity.radius:
				if ob not in ret_set and entity != ob:
					ret_set.add(ob)
			else:
				break

		return list(ret_set)
	
	def update_pos(self, eid, x, y):
		# 更新对象eid对象的坐标为(x, y)
		if eid not in self.map_entity:
			# 首次进入
			entity = Entity(eid, x, y)
			entity = self.add_by_entity(entity)
			entities = self.get_range_entities(entity)
			for ob in entities:
				ob.enter(entity)
		else:
			entity = self.map_entity[eid]
			if x < 0 or y < 0:
				# 坐标小于0时为离开场景
				obs = self.get_range_entities(entity)
				self.remove(eid)
				for ob in obs:
					ob.leave(entity)
			else:
				old_entities = self.get_range_entities(entity)
				self.remove(eid)
				entity.x = x
				entity.y = y
				self.add_by_entity(entity)
				new_entities = self.get_range_entities(entity)
				# enter处理
				enter_obs = [ob for ob in new_entities if ob not in old_entities]
				for ob in enter_obs:
					ob.enter(entity)
				# leave处理
				leave_obs = [ob for ob in old_entities if ob not in new_entities]
				for ob in leave_obs:
					ob.leave(entity)
				# move处理
				move_obs = [ob for ob in old_entities if ob in new_entities]
				for ob in move_obs:
					ob.move(entity)        
			

def test():
	scene = Scene()

	scene.update_pos(1, 1, 2)
	scene.update_pos(2, 5, 9)
	scene.update_pos(3, 10, 10)
	pobj1 = scene.map_entity[1]
	pobj2 = scene.map_entity[2]
	pobj3 = scene.map_entity[3]

	print scene

	print("\nscene.update_pos(1, 7, 7), pobj1.range: ")
	scene.update_pos(1, 7, 7)
	for ob in scene.get_range_entities(pobj1):
		print(ob)

	print("\nscene.update_pos(2, 1, 2)), pobj2.range:")
	scene.update_pos(2, 1, 2)
	for ob in scene.get_range_entities(pobj2):
		print(ob)

	print("\nscene.update_pos(3, 5, 5), pobj3.range: ")
	scene.update_pos(3, 5, 5)
	for ob in scene.get_range_entities(pobj3):
		print(ob)


test()

运行结果:
<2,5-9> enter <1,1-2> view
<3,10-10> enter <2,5-9> view
<list_x: [1, 2, 3], list_y: [1, 2, 3], entities: [1,2,3]>


scene.update_pos(1, 7, 7), pobj1.range:
<1,7-7> enter <3,10-10> view
<1,7-7> move in <2,5-9> view
<3,10-10>
<2,5-9>


scene.update_pos(2, 1, 2)), pobj2.range:
<2,1-2> leave <3,10-10> view
<2,1-2> move in <1,7-7> view
<1,7-7>


scene.update_pos(3, 5, 5), pobj3.range:
<3,5-5> enter <2,1-2> view
<3,5-5> move in <1,7-7> view
<1,7-7>
<2,1-2>

规范版本应该双向列表维护,实体数量较多时候,应该用跳跃表去维护,类的设计如下

 参考:https://www.cnblogs.com/rond/p/6114919.html

class Entity(object):
	def __init__(self):
		self.id = 0
		self.x = 0
		self.y = 0
		
		self.x_prev = None  # 双向列表
		self.x_next = None
		
		self.y_prev = None
		self.y_next = None
	
	
class Scene(object):
	def __init__(self):
		self.list_x_head = None
		self.list_x_tail = None
		
		self.list_y_head = None
		self.list_y_tail = None

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值