我发现在《吃豆人》中有很多关于幽灵AI的参考,但没有一个提到当幽灵被《吃豆人》吃掉后,眼睛是如何找到中央幽灵洞的。

在我的实现中,我实现了一个简单但糟糕的解决方案。我只是在每个角落都用硬编码标明了应该往哪个方向走。

有没有更好的/最好的解决办法?也许是适用于不同关卡设计的通用设计?


当前回答

我的方法有点内存密集型(从《吃豆人》时代的角度来看),但你只需要计算一次,它适用于任何关卡设计(包括跳跃)。

一次标记节点

当你第一次加载一个关卡时,将所有怪物巢穴节点标记为0(代表与巢穴的距离)。继续向外标记已连接的节点1,连接到它们的节点2,依此类推,直到所有节点都被标记。(注意:如果巢穴有多个入口,这也是有效的)

我假设您已经有了表示每个节点和到它们的邻居的连接的对象。伪代码可能看起来像这样:

public void fillMap(List<Node> nodes) { // call passing lairNodes
    int i = 0;

    while(nodes.count > 0) {
        // Label with distance from lair
        nodes.labelAll(i++);

        // Find connected unlabelled nodes
        nodes = nodes
            .flatMap(n -> n.neighbours)
            .filter(!n.isDistanceAssigned());
    }
}

眼睛移动到距离标签最小的邻居

一旦所有节点都标记好了,路由眼睛就变得很简单了……只需要选择距离标签最小的相邻节点(注意:如果多个节点的距离相等,那么选择哪个节点并不重要)。伪代码:

public Node moveEyes(final Node current) {
    return current.neighbours.min((n1, n2) -> n1.distance - n2.distance);
}

全标记示例

其他回答

任何简单、可维护、可靠、性能足够好的解决方案都是好的解决方案。听起来你已经找到了一个很好的解决办法。

寻径解决方案可能比当前解决方案更复杂,因此更可能需要调试。它可能也会变慢。

在我看来,如果它没坏,就不要修。

EDIT

在我看来,如果迷宫是固定的,那么你当前的解决方案就是好的/优雅的代码。不要错误地把“好”或“优雅”等同于“聪明”。简单的代码也可以是“好的”和“优雅的”。

如果你有可配置的迷宫级别,那么也许你应该在最初配置迷宫时进行寻路。最简单的方法就是让迷宫设计师亲自动手。如果你有无数个迷宫,我才会费心自动化这个……或者用户可以自行设计。

(另外:如果路线是手工配置的,那么迷宫设计师可以通过使用次优路线来让关卡变得更有趣……)

我建议幽灵存储他从洞到吃豆人的路径。所以一旦鬼魂死了,他就可以沿着这条存储路径向相反的方向移动。

在最初的《吃豆人》中,幽灵会通过“气味”在地图上留下痕迹,鬼魂会随机四处游荡,直到它们找到气味,然后它们会沿着气味路径直接找到玩家。吃豆人每移动一次,“气味值”就会减少1。

现在,一个扭转整个过程的简单方法是建立一个“幽灵气味金字塔”,它的最高点在地图的中心,然后鬼魂就会朝着气味的方向移动。

我的方法有点内存密集型(从《吃豆人》时代的角度来看),但你只需要计算一次,它适用于任何关卡设计(包括跳跃)。

一次标记节点

当你第一次加载一个关卡时,将所有怪物巢穴节点标记为0(代表与巢穴的距离)。继续向外标记已连接的节点1,连接到它们的节点2,依此类推,直到所有节点都被标记。(注意:如果巢穴有多个入口,这也是有效的)

我假设您已经有了表示每个节点和到它们的邻居的连接的对象。伪代码可能看起来像这样:

public void fillMap(List<Node> nodes) { // call passing lairNodes
    int i = 0;

    while(nodes.count > 0) {
        // Label with distance from lair
        nodes.labelAll(i++);

        // Find connected unlabelled nodes
        nodes = nodes
            .flatMap(n -> n.neighbours)
            .filter(!n.isDistanceAssigned());
    }
}

眼睛移动到距离标签最小的邻居

一旦所有节点都标记好了,路由眼睛就变得很简单了……只需要选择距离标签最小的相邻节点(注意:如果多个节点的距离相等,那么选择哪个节点并不重要)。伪代码:

public Node moveEyes(final Node current) {
    return current.neighbours.min((n1, n2) -> n1.distance - n2.distance);
}

全标记示例

假设你已经有了追逐吃豆人所需的逻辑,为什么不重用它呢?只要改变目标。这似乎比尝试使用完全相同的逻辑创建一个全新的例程要少得多。