【互动编程】基于集群单位的有效路径控制

本文分析了游戏《最高指挥官》和《全面战争》系列中的群体移动算法,包括单位集群的列线状态、Steer、Arrive等运动控制策略。同时,介绍了Dijkstra-Flow寻路算法在解决单位路径规划问题中的应用,以及如何实现平滑的单位路径控制和群体位置布局。案例代码展示了路径生成和寻路过程,强调了算法在非严肃游戏领域的应用。
摘要由CSDN通过智能技术生成

在这里插入图片描述

在这里插入图片描述
无论是在《最高指挥官》和《全面战争》系列中常见的群体移动算法分析

单位集群的列线状态,用简单steer和offset模式驱动单位的整体移动和避开障碍,首先看无障碍情况下单位地移动方式,

移动

  • 单位与单位间需要保持阵型不变,离开原先位置后,应该返回原位置
  • 单位无法穿过其它单位(正常请跨下)
  • 单位有速度限制,除非死亡,个体单位无法单独脱离组运动

Steer、Pursuit 、Arrive是常用于运动控制的术语,用于描述集群或群体中个体的运动行为。

  1. Flock(集群):指的是一群个体以一种协调的方式一起移动,保持一定的群体形态和群体行为。在集群中,个体之间相互作用,通过观察和学习邻近个体的位置和速度来决定自己的运动方向和速度,以实现整体的协调性。

  2. Steer(操纵):在集群运动中,个体通过操纵自己的运动方向和速度来实现对集群的调整和协调。Steer可以基于不同的策略和规则来实现,例如避免与其他个体碰撞、保持一定的距离、朝向群体中心或指定的目标等。

  3. Arrive(到达):Arrive是一种基于目标到达的运动控制策略,个体通过调整自己的速度和方向来逐渐接近目标,并在到达目标附近时减速。这种策略可以用于模拟个体在到达目标点时的缓慢减速和停止行为。

结合这些运动控制术语通常与仿生学等领域中的模型和算法相结合,用于描述和实现集群或群体中个体的协调运动行为。利用这些方法,可以模拟和控制各种集体运动,如鸟群、鱼群、蚁群等。

在这里插入图片描述
带有平滑效果地单位路径控制

    def seek(self,dest):
        desire = dest - self.pos
        distance = desire.get_magnitude()
        # 到达行为
        mul = 1
        if distance<30:
            mul = distance/30    
            desire = desire.get_normalize()*mul*self.maxspeed
        else:
            desire = desire.get_normalize()*self.maxspeed
        return desire

    def pursuit(self):
        desire = self.seek(self.dest)
        distance  = self.dest - self.pos
        if distance.get_magnitude() < 10:
            return
        dot = self.heading.get_normalize().dot(desire.get_normalize())
        k = (dot-1.0)/-2.0
        self.heading = lerp(self.heading , desire,0.94-k)
        self.pos = self.pos + self.heading*10

lerp 通过线性插值将转向和目标驱动结合在一起,让单位转向更平滑

self.heading = leap(self.heading , desire,0.94-k)

而 Offset Pursuit 可以参考 《游戏人工智能编程案例精粹》 中的介绍

在这里插入图片描述
群体位置函数可以有,矩形,圆形,空心矩形等,这里用6人为一排进行排列

    def rect_spawn(self,vec):
        n = len(self.entities)

        row_num = []
        while n>6:
            n = n - 6
            row_num.append(6)
        row_num.append(n%6)

        wid = 6*50
        height = len(row_num)*50
        Perpendicular = Vector2(vec.y,-vec.x)
        start = 0
        for i in range(len(row_num)):
            for j in range(row_num[i]):
                ve = Vector2(0,0)
                ve = ve + Perpendicular*(j-2.5)*50 
                ve = ve + vec*(len(row_num)-i-3)*50
                self.entities[start+j].dest = ve + self.el.pos
            start = start + row_num[i]

这里需要考虑到旋转和平移,所以一定要使用局部坐标系,单位有两个方向,一个是正面的方向(heading vector),一个是与正面垂直的方向(Perpendicular vector)

寻路算法

  • 无需对每个单位经行寻路
  • 物体间障碍物处理流程
  • 避免过多计算造成抖动

经典的寻路算法是用于解决在图或网格上找到从起点到目标点的最短路径的问题。以下是几个经典的寻路算法:

  1. Dijkstra算法:Dijkstra算法是最经典的最短路径算法之一。它使用贪心策略,在每一步选择当前最短路径的节点进行扩展,直到找到目标节点或遍历完所有可达节点。Dijkstra算法适用于权重非负的图。

  2. A*算法:A-star 算法是一种启发式搜索算法,结合了Dijkstra算法和贪心算法的优点。它使用估计函数来评估每个节点的代价,并利用这个估计值来指导搜索方向。A star 算法通过在每个步骤中选择一个最优的节点进行扩展,逐渐逼近最短路径。A star 算法在搜索过程中既考虑了路径长度,也考虑了距离目标的启发式估计值。

本案例中使用基于Dijkstra-Flow 寻路算法

代码涉及到了寻路生成

    def __init__(self):
        self.map = self.create_map(20,15)
        self.size = 40
        self.textsize = self.size/2-4
        self.star = [2,5]
        self.reached = {}
        self.frontier = deque()
        self.frontier.append(self.star)
        self.reached[self.id2num(self.star)] = {"distance":1}

    def create_map(self,col,row):
        array = []
        for j in range(row):
        	row = []
        	for i in range(col):
        		row.append(1)
        	array.append(row)

        return array

    def get_neighb(self,pos):
    	arr = []
    	right = [pos[0]+1,pos[1]]
    	left = [pos[0]-1,pos[1]]
    	up = [pos[0],pos[1]-1]
    	down = [pos[0],pos[1]+1]

    	if self.valid_pos(right):
    		arr.append(right)
    	if self.valid_pos(left):
    		arr.append(left)
    	if self.valid_pos(up):
    		arr.append(up)
    	if self.valid_pos(down):
    		arr.append(down)
    	return arr

    def valid_pos(self,pos):
    	if pos[0]<len(self.map[0]) and pos[0]>=0 and pos[1]<len(self.map) and pos[1]>=0:
    		if self.map[pos[1]][pos[0]]==0:
    			return False
    		else:
    			return True
    	else:
    		return False

    def id2num(self,pos):
    	return pos[1]*len(self.map[0])+pos[0]

    def num2id(self,num):
    	ix = math.floor(num/len(self.map[0]))
    	iy = num%len(self.map[0])
    	return [iy,ix]

    def flood(self):
    	while  len(self.frontier)>0:
	    	current = self.frontier.popleft()
	    	for next in self.get_neighb(current):
	    		if self.id2num(next) not in self.reached:
	    			self.frontier.append(next)
	    			self.reached[self.id2num(next)] = {"distance":self.reached[self.id2num(current)]["distance"]+1}
	    	self.update()

在这里插入图片描述

操作说明:

蓝色线条为群体控制点运动的路径
左键:设定新导航点,目标到达导航点后会停止运行
中键:移动画布
鼠标滚轮:缩放画布

内容说明

为了能运行本案例,请自行安装python环境 和相关依赖包,具体配置和安装在本文中不涉及也无需涉及,本案例不支大部分智能设备
算法未经严格优化,只为了说明算法的使用方法
本案例应用于非严肃领域,数值结果和算法探讨请@我,如需转载请注明

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值