【问题标题】:Efficient enemy avoidance for pathfinding with fewer enemies高效的敌人回避,以更少的敌人寻路
【发布时间】:2020-02-26 22:20:24
【问题描述】:

我目前正在使用 Python 开发一款 2D 自上而下的类 Rogue 游戏。地图是一个地牢,包含许多开放的矩形房间(image),每个房间里面都有大约 2-4 个敌人。我目前正在寻求实现一个寻路系统,在该系统中,敌人会互相移动并试图蜂拥而至玩家。

到目前为止,我已经实现了一个 A* 算法,它确实允许敌人以这种方式导航和蜂拥而至玩家。但是,我的方法会导致帧速率非常低:通常在 15 FPS 左右,但当敌人无法接近玩家时,它会降至 1 FPS 以下。我觉得这非常低效,因为每帧上的每个敌人都在进行寻路。目前,其他敌人被视为 A* 算法的障碍物,唯一的优化是如果没有障碍物,敌人会直接向玩家移动。代码如下:

import heapq
#...
FLOOR = 1
#...

class Game:
    def __init__(self):
        #...
        self.pathfindingGranularity = 5

    # Slope and line intersection functions are based on: https://www.codeproject.com/Tips/864704/Python-Line-Intersection-for-Pygame
    def lineInRect(self, start, end, r):
        if start in r and end in r: return True

        if self.segmentIntersect(start, end, r.origin, Point(r.x + r.width, r.y)) is not None: return True
        if self.segmentIntersect(start, end, Point(r.x, r.y + r.height), Point(r.x + r.width, r.y + r.height)) is not None: return True
        if self.segmentIntersect(start, end, r.origin, Point(r.x, r.y + r.height)) is not None: return True
        if self.segmentIntersect(start, end, Point(r.x + r.width, r.y), Point(r.x + r.width, r.y + r.height)) is not None: return True

        return False

    def slope(self, p1, p2):
        if p2.x - p1.x == 0: return 1e10
        return (p2.y - p1.y) / (p2.x - p1.x)

    def yIntercept(self, slope, p1):
        return p1.y - slope * p1.x

    def lineIntersect(self, start1, end1, start2, end2):
        min_allowed = 1e-5
        big_value = 1e10
        m1 = self.slope(start1, end1)
        b1 = self.yIntercept(m1, start1)
        m2 = self.slope(start2, end2)
        b2 = self.yIntercept(m2, start2)
        if abs(m1 - m2) < min_allowed: x = big_value if (b2 - b1 >= 0) else -big_value
        else: x = (b2 - b1) / (m1 - m2)
        y = m1 * x + b1
        return Point(x, y)

    def segmentIntersect(self, start1, end1, start2, end2):
        intersection = self.lineIntersect(start1, end1, start2, end2)

        def approx(f):
            return round(f * 10000) / 10000

        if not approx(start1.x) <= approx(intersection.x) <= approx(end1.x):
            if not approx(end1.x) <= approx(intersection.x) <= approx(start1.x):
                return None

        if not approx(start2.x) <= approx(intersection.x) <= approx(end2.x):
            if not approx(end2.x) <= approx(intersection.x) <= approx(start2.x):
                return None

        if not approx(start1.y) <= approx(intersection.y) <= approx(end1.y):
            if not approx(end1.y) <= approx(intersection.y) <= approx(start1.y):
                return None

        if not approx(start2.y) <= approx(intersection.y) <= approx(end2.y):
            if not approx(end2.y) <= approx(intersection.y) <= approx(start2.y):
                return None

        return intersection

class Enemy (Entity):
    def update(self, game):
        #...

        if not self.getRect().intersects(game.player.getRect()) and self.canMove():
            self.generatePath(game)

            if self.path:
                # Move towards player

        elif self.canMove():
            # Hurt the player

    #...

    def generatePath(self, game):
        if not self.lineOccupied(Point(self.x, self.y), game.player.getCenterpoint(), game):
            self.path = [game.player.getCenterpoint()]
            return

        frontier = PriorityQueue()
        start = Point(self.x, self.y)
        frontier.put(start, 0)
        came_from = {}
        came_from[start] = None

        done = False
        while not frontier.empty():
            current = frontier.get()

            if Rect(current.x + self.hitbox.x, current.y + self.hitbox.y, self.hitbox.w, self.hitbox.h).intersects(game.player.getRect()):
                done = True
                break

            for next in self.findAdjacents(current, game):
                if self.lineOccupied(current, next, game): continue
                if next not in came_from:
                    priority = self.heuristic(next, game)
                    frontier.put(next, priority)
                    came_from[next] = current

        if not done:
            self.path.clear()
        else:
            p = [current]
            while came_from[p[-1]] is not None:
                p.append(came_from[p[-1]])

            self.path = p[::-1][1:]

            i = 0

    def findAdjacents(self, currentPoint, game):
        d = 1 / game.pathfindingGranularity
        for x in (currentPoint.x - d, currentPoint.x, currentPoint.x + d):
            for y in (currentPoint.y - d, currentPoint.y, currentPoint.y + d):
                if x == currentPoint.x and y == currentPoint.y: continue
                elif self.canWalkAtCoords(x, y, game):
                    yield Point(x, y)

    def canWalkAtCoords(self, x, y, game):
        for nx in (x, x + self.hitbox.w):
            for ny in (y, y + self.hitbox.h):
                if game.blockAt(nx, ny) != FLOOR:
                    return False
        return True

    def lineOccupied(self, start, end, game):
        for e in self.room.enemies:
            if e is self:
                continue
            for xo in (self.hitbox.x, self.hitbox.x + self.hitbox.w):
                for yo in (self.hitbox.y, self.hitbox.y + self.hitbox.h):
                    if game.lineInRect(start + Point(xo, yo), end + Point(xo, yo), e.getRect()):
                        return True

        return False



我觉得应该有一个更有效的解决方案,特别是考虑到房间是矩形的,没有额外的墙壁或障碍物让敌人四处移动,但到目前为止,我已经找到了解决方案两手空空。我可以做一些优化来提高程序的性能吗?或者如果没有,是否有更好的寻路方法我应该研究?任何帮助将不胜感激!

【问题讨论】:

  • 一种简单的方法是仅以特定间隔(例如 60 帧)执行寻路算法。
  • 路径查找并不是真正必要的(当没有固定障碍物时)。只需向玩家的方向移动一步即可。如果道路被另一个敌人挡住了,并且与玩家的距离很大,那么只需等待其他敌人移动即可。只有当敌人都聚集在玩家周围时,你才需要寻路,但这种情况下,你只需要在很小的区域内寻路。

标签: python path-finding


【解决方案1】:

您应该尝试让您的路径查找从您的角色开始,并使用广度优先搜索(对坡度进行一些调整)展开。每次遇到敌人时,您都可以计算出它朝向玩家的最佳路径。

这样一来,您只需在整个棋盘上进行一次传球,而不是为每个敌人传球一次。

如果您想了解更多详情,请告诉我。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 2013-08-24
    • 1970-01-01
    相关资源
    最近更新 更多