python游戏寻路_游戏服务端寻路的思路与实现

本文介绍了在Python服务端使用A*算法实现寻路,并结合Unity客户端展示路径的方法。讨论了A*算法相对于Dijkstra算法的效率优势,并提供了Python实现的详细代码,包括地图信息的导出、服务端寻路频率限制以及Unity客户端的路径应用逻辑。
摘要由CSDN通过智能技术生成

本文以Python作为服务器, Unity作为客户端引擎, 主要讨论的是服务端实现寻路并由客户端表现架构下的程序设计思路.

1. 整体思路

首先, 我们讨论寻路算法的比较和选择: A-star一般来说有更好的性能, 因为它是从起点到终点路径上的点与起点之间的最短路径计算, 而Dijkstra算法则是起点到图结构中所有点的最短路径计算, 有一些浪费了.

服务端寻路架构下, 服务端的作用在于定时根据游戏中玩家的位置变化, 进行新路线计算并把结果用point list的形式发送给客户端.

客户端的职责, 则是要把point list实际转化为画面上怪物单位的连续移动.

2. 讨论寻路算法

寻路算法, 指的是找出Start点到End点之间的一条最短路径. 这条最短路径通过的是地图上的可通行区域, 即应当绕开堵塞区域(block area).

我们可以从时间开销方面去比较这俩算法:

以常见的二叉堆(又名Priority Queue)实现为例, Dijkstra算法的时间开销为O(ElgV), E是边的数目, V是结点的数目, 算法最耗时的操作是从Q中提出V次的最小key值结点(extractMin), 和对所有E条边进行的decreaseKey操作.

值得注意的是: 迪杰斯特拉算法能够找出从S(Source)点到所有其他图结构中结点的最短路径.

Astar算法, 如果也是用二叉堆实现的话, 时间开销也是O(ElgV), 因为其最耗时操作也是Q中进行V次的extractMin操作, 以及对所有E条边进行的decreaseKey操作. 但是, 这里所说的都是算法的最坏情况, 也就是说找出来的最短路径需要遍历整个地图的最坏情况.

由于Astar算法是一个贪心算法(F = G+H中的H数值是用曼哈顿距离估算的, 并不是一个精确可靠的值), 因此虽然Dijkstra和Astar在二叉堆实现情况下都是O(ElgV), 大多数情况下Astar算法的时间开销只会明显少于Dijkstra, 我个人粗略估计至少是少一个数量级, 也就是说不到1/10, 特别是地图越大这个比例会越小.

用更直白的话来说, 迪杰斯特拉算法是找出S点到所有图中点的最短路径, A-star只找出S到E和S到路径上其他点的最短路径, 明显A-star要完成的任务更少. 由于游戏中我们大多数情况下只需要S到E点的最短路径, 因此A-star是更加省时节约开销的算法.

3. A-star with heap的实现

# encoding:utf-8

import time

from heapq import heappush, heappop

"""

path finding module: A* path finding算法, 用heap实现版本

quick guide:

start = Node(None, 20, 20)

end = Node(None, 50, 30)

print find_path(start, end) # return path list

It reads a map in the beginning of this script. Currently layout_medium.txt is chosen.

To download map(layout_medium.txt):

https://share.weiyun.com/5mtyHYi

"""

_2dmap = []

map_border = ()

g_close_list = {}

class Node:

def __init__(self, father, x, y, end=None):

if x < 0 or x >= map_border[0] or y < 0 or y >= map_border[1]:

raise Exception("node position can't beyond the border!")

self.father = father

self.x = x

self.y = y

if father != None:

G2father = calc_G(father, self)

if not G2father:

raise Exception("father is not valid!")

self.G = G2father + father.G

self.H = calc_H(self, end)

self.F = self.G + self.H # 初始化的时候计算出F

else: # it has no father, thus it is the start point

self.G = 0

self.H = 0

self.F = 0

def reset_father(self, father, new_G):

if father != None:

self.G = new_G

self.F = self.G + self.H

self.father = father

def calc_G(node1, node2): # 该点

dx = abs(node1.x - node2.x)

dy = abs(node1.y - node2.y)

if dx == 1 and dy == 0:

return 10 # same row

if dx == 0 and dy == 1:

return 10 # same col

if dx == 1 and dy == 1:

return 14 # cross

else:

return 0

def calc_H(cur, end): # 估算该点到终点距离(忽略墙等阻挡物), 采用Manhattan distance

return 10*(abs(end.x - cur.x) + abs(end.y - cur.y))

def addAdjacentIntoOpen_new(heap, open_list, close_list, node, end):

# 将该节点从开放列表移到关闭列表当中。

open_list.pop((node.x, node.y)) # key 为(x, y)形式的坐标tuple

close_list[(node.x, node.y)] = node

_adjacent = []

# 地图的layout的边界需要用0进行标记, 否则会进入except

try:

#_adjacent.append(Node(node, node.x - 1, node.y - 1, end)) # 这个时候就初始化了F值

_adjacent.append(Node(node, node.x, node.y - 1, end))

#_adjacent.append(Node(node, node.x + 1, node.y - 1, end))

_adjacent.append(Node(node, node.x + 1, node.y, end))

#_adjacent.append(Node(node, node.x + 1, node.y + 1, end))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值