游戏算法-AOI九宫格python实现

将空间按照一定的方法进行分割,例如根据AOI范围的大小将整个游戏世界切分为固定大小的格子。当游戏物体位于场景的时候,根据坐标将它放入特定的格子中。

例如玩家1在位置7中,如果游戏内的AOI的范围为1个格子。当我们需要获取这个玩家周围的AOI对象时,只需要遍历7周围9个里面的对象即可。

实体的事件:

  • 进入场景enter:进入一个格子,取出周围9格的对象,向它们发送Enter(我)事件,同时向我发送Enter(对象)事件。
  • 离开场景leave:取出周围9格的对象,向它们发送Leave(我)事件。
  • 移动move:
    • 如果没跨格子,直接取9格的对象,向它们发送移动事件。
    • 如果跨过格子,计算{OldGrid-NewGrid},向它们发送Leave(我)事件,向我发送Leave(对象)事件;计算{NewGrid-OldGrid}集合,向它们发送Enter(我)事件,向我发送Enter(对象事件;计算{NewGrid*OldGrid}集合,向他们发送Move(我)事件。
       

空间分割在计算AOI对象时,只需要遍历周围几个空间格子即可,大大提高了计算效率。

但是该方法也有缺点,格子数和空间大小成为正比,空间越大,所需要的内存空间也越大。

如果玩家数里远远小于空间的格子数,使用这种方法来计算AOI可能比全部遍历效率还差。

实现

实体Entity:有三个事件,enter.leave,move事件

class Entity(object):
	# 场景实体
	def __init__(self, eid, x, y):
		self.id = eid  # 角色ID
		self.x = x
		self.y = y

		self.last_x = -1  # 是用来参与判断实体是否有进入或者离开 AOI
		self.last_y = -1

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

	def enter(self, eobj):
		print("{0} enter {1} view".format(eobj, self))

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

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

格子:场景根据大小分成若干格子,每个格子管理一个区域

角色所在的位置,由格子管理

class Grid(object):
	# 格子
	def __init__(self, gid, min_x, max_x, min_y, max_y):
		self.gid = gid  # 格子ID
		self.min_x = min_x  # 格子坐标x范围
		self.max_x = max_x

		self.min_y = min_y  # 格子坐标y范围
		self.max_y = max_y

		self.players = set([])  # 角色ID

比如此图,分成36个格子

场景:控制生成格子数量,管理格子和实体对象

class Scene(object):
	# 场景,由多个格子组成
	def __init__(self, min_x, max_x, cnts_x, min_y, max_y, cnts_y):
		self.min_x = min_x
		self.max_x = max_x
		self.cnts_x = cnts_x  # X轴方向格子的数量

		self.min_y = min_y
		self.max_y = max_y
		self.cnts_y = cnts_y  # y轴方向格子的数量

		self.grids = {}  # 管理格子对象
		
		self.map_entity = {}  # 管理实体对象

完整代码:

python版本:2.7

# -*- coding: utf-8 -*-

class Entity(object):
	# 场景实体
	def __init__(self, eid, x, y):
		self.id = eid  # 角色ID
		self.x = x
		self.y = y

		self.last_x = -1  # 是用来参与判断实体是否有进入或者离开 AOI
		self.last_y = -1

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

	def enter(self, eobj):
		print("{0} enter {1} view".format(eobj, self))

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

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


class Grid(object):
	# 格子
	def __init__(self, gid, min_x, max_x, min_y, max_y):
		self.gid = gid
		self.min_x = min_x
		self.max_x = max_x

		self.min_y = min_y
		self.max_y = max_y

		self.players = set([])  # 角色ID

	def __str__(self):
		return "<{0}, {1}-{2}: {3}>,".format(self.gid, self.min_x, self.min_y, str(self.players))

	def add(self, eid):
		self.players.add(eid)

	def remove(self, eid):
		self.players.remove(eid)

	def get_player_ids(self):
		return list(self.players)


class Scene(object):
	# 场景,由多个格子组成
	def __init__(self, min_x, max_x, cnts_x, min_y, max_y, cnts_y):
		self.min_x = min_x
		self.max_x = max_x
		self.cnts_x = cnts_x  # X轴方向格子的数量

		self.min_y = min_y
		self.max_y = max_y
		self.cnts_y = cnts_y  # y轴方向格子的数量

		self.grids = {}
		
		self.map_entity = {}  # 实体对象
		self.init_grid()

	def __str__(self):
		res = ""
		for y in xrange(self.cnts_y):
			for x in xrange(self.cnts_x):
				gid = y * self.cnts_x + x
				res += str(self.grids[gid])
			res += "\n"
		return res

	def init_grid(self):
		# 生成格子
		for y in xrange(self.cnts_y):
			for x in xrange(self.cnts_x):
				gid = y * self.cnts_x + x

				min_x = self.min_x + x * self.grid_width()
				max_x = self.min_x + (x + 1) * self.grid_width()
				min_y = self.min_y + y * self.grid_height()
				max_y = self.min_y + (y + 1) * self.grid_height()

				self.grids[gid] = Grid(gid, min_x, max_x, min_y, max_y)

	def grid_width(self):
		# 每个格子在x轴方向的宽度
		return (self.max_x - self.min_x) / self.cnts_x

	def grid_height(self):
		# 得到每个格子在Y轴方向高度
		return (self.max_y - self.min_y) / self.cnts_y

	def get_surround_grids_by_gid(self, gid, include_self=False):
		# 周边的格子对象
		surrounds = []
		grid = self.grids[gid]
		y, x = divmod(grid.gid, self.cnts_x)
		for y_i, x_j in ((-1, 1), (-1, 0), (-1, -1), (0, -1), (0, 1), (1, 1), (1, 0), (1, -1)):
			cal_y = y + y_i
			cal_x = x + x_j
			if cal_x < 0 or cal_x >= self.cnts_x:
				continue
			if cal_y < 0 or cal_y >= self.cnts_y:
				continue

			cal_gid = cal_y * self.cnts_x + cal_x
			surrounds.append(self.grids[cal_gid])
		return surrounds

	def add_eid_grid(self, eid, gid):
		self.grids[gid].add(eid)

	def remove_eid_grid(self, eid, gid):
		self.grids[gid].remove(eid)

	def get_eids_by_gid(self, gid):
		return self.grids[gid].get_player_ids()

	def get_gid_by_pos(self, x, y):
		# 通过,x, y得到对应格子ID
		idx = (x - self.min_x) / self.grid_width()
		idy = (y - self.min_y) / self.grid_height()

		gid = idy * self.cnts_x + idx
		return gid

	def get_surround_eids_by_pos(self, x, y, include_self=False):
		# 根据一个坐标 得到 周边九宫格之内的全部的 玩家ID集合
		gid = self.get_gid_by_pos(x, y)
		grids = self.get_surround_grids_by_gid(gid)
		eids = []
		for grid in grids:
			eids.extend(grid.get_player_ids())
		if include_self:
			eids.extend(self.grids[gid].get_player_ids())
		return eids

	def add_to_grid_by_pos(self, eid, x, y):
		# 通过坐标 将eid 加入到一个格子中
		gid = self.get_gid_by_pos(x, y)
		grid = self.grids[gid]
		grid.add(eid)
		return gid

	def remove_to_grid_by_pos(self, eid, x, y):
		# 通过坐标 将eid remove到一个格子中
		gid = self.get_gid_by_pos(x, y)
		grid = self.grids[gid]
		grid.remove(eid)

	def update_pos(self, update_eid, x, y):
		if update_eid not in self.map_entity:
			# 首次进入
			eobj = Entity(update_eid, x, y)
			self.map_entity[update_eid] = eobj
			grip_id = self.add_to_grid_by_pos(update_eid, x, y)
			eids = self.get_surround_eids_by_pos(x, y)
			for eid in eids:
				ob = self.map_entity[eid]
				ob.enter(eobj)
		else:
			eobj = self.map_entity[update_eid]
			eobj.last_x = eobj.x
			eobj.last_y = eobj.y
			eobj.x = x
			eobj.y = y

			# 格子内移动
			old_gid = self.get_gid_by_pos(eobj.last_x, eobj.last_y)
			new_gid = self.get_gid_by_pos(eobj.x, eobj.y)
			if old_gid == new_gid:
				eids = self.get_surround_eids_by_pos(x, y, True)
				for eid in eids:
					self.map_entity[eid].move(eobj)
			else:
				# 移动格子
				self.remove_eid_grid(update_eid, old_gid)
				self.add_eid_grid(update_eid, new_gid)
				old_surround_gids = self.get_surround_grids_by_gid(old_gid)
				new_surround_gids = self.get_surround_grids_by_gid(new_gid)

				# 新格子事件处理
				for grid in [grid for grid in new_surround_gids if grid not in old_surround_gids]:
					for eid in grid.get_player_ids():
						self.map_entity[eid].enter(eobj)

				# 老格子事件处理
				for grid in [grid for grid in old_surround_gids if grid not in new_surround_gids]:
					for eid in grid.get_player_ids():
						self.map_entity[eid].leave(eobj)

				for grid in [grid for grid in old_surround_gids if grid in new_surround_gids]:
					for eid in grid.get_player_ids():
						self.map_entity[eid].move(eobj)

def test():
	scene = Scene(0, 100, 4, 0, 100, 4)
	scene.update_pos(1, 0, 0)
	scene.update_pos(2, 50, 20)
	scene.update_pos(3, 99, 99)
	print(scene)

	print("<25-1> sorround: {0}".format(scene.get_surround_eids_by_pos(25, 1, True)))
	print("<50-50> sorround: {0}".format(scene.get_surround_eids_by_pos(50, 50, True)))
	scene.update_pos(3, 25, 1)
	scene.update_pos(3, 99, 99)


test()


 

运行结果:一个场景分成16个格子,大小为100 * 100

<0, 0-0: set([1])>,<1, 25-0: set([])>,<2, 50-0: set([2])>,<3, 75-0: set([])>,
<4, 0-25: set([])>,<5, 25-25: set([])>,<6, 50-25: set([])>,<7, 75-25: set([])>,
<8, 0-50: set([])>,<9, 25-50: set([])>,<10, 50-50: set([])>,<11, 75-50: set([])>,
<12, 0-75: set([])>,<13, 25-75: set([])>,<14, 50-75: set([])>,<15, 75-75: set([3])>,

<25-1> sorround: [1, 2]
<50-50> sorround: [3]
<3, 25-1> enter <1, 0-0> view
<3, 25-1> enter <2, 50-20> view
<3, 99-99> leave <1, 0-0> view
<3, 99-99> leave <2, 50-20> view

 深入探索AOI算法:深入探索AOI算法 - 知乎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity中实现AOI(Area of Interest,兴趣区域)九宫格算法的方法如下: 首先,创建一个AOIManager类,用于管理和维护AOI区域和对象。该类需要包含以下几个关键组成部分: 1. AOI格子的定义:定义一个格子的大小,并将整个场景划分为多个格子。可以使用二维数组来表示格子,每个元素存储在该格子中的对象列表。 2. 对象的定义:定义一个对象的结构,包括对象的唯一标识ID和对象的位置信息。 3. 对象的管理:AOIManager类需要负责管理所有对象的位置和状态。当一个对象进入或离开一个格子时,需要更新该对象在AOIManager中的位置信息。 4. AOI九宫格算法实现:在AOIManager中实现九宫格算法,根据一个对象的位置信息,计算出该对象所在的格子以及该对象周围的格子。 5. 状态更新:当一个对象的位置发生变化时,需要通过九宫格算法计算该对象应该进入或离开的格子,并更新该对象的位置信息。 6. 感知范围:根据九宫格算法计算出的周围格子,可以获取到该对象周围的其他对象。可以根据需要定义对象的感知范围,只获取特定范围内的对象。 总结一下,Unity实现AOI九宫格算法需要创建AOIManager类,实现格子划分和对象管理,以及实现九宫格算法来计算对象所在的格子和对象的感知范围。这种方法可以提高游戏中大量对象的更新效率,减少不必要的计算和通信开销,提升游戏性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值