Perlin噪音造成的世界中的产卵单位?


16

我在Perlin基于噪音的游戏中遇到了一些问题。请看下面的截图。

在此处输入图片说明

您看到的白色区域是墙壁,黑色区域是可步行的。中间的三角形是玩家。

我已经在游戏中实现了物理学,方法是将其绘制到纹理(白色或黑色像素)上,然后从CPU中获取。

但是,现在我面临另一个问题。我希望单位(或小兵,无论您叫什么)在屏幕边缘不断生成。这里的要点是,在最终游戏中,将会出现“战争迷雾”,无论如何玩家都无法看到它。

我认为我可以只扫描屏幕边缘的像素,看看它们的物理纹理是否为黑色,然后在其中随机生成东西。但是,如果您再看一下屏幕截图,则(在左上角)有一个我不想让小兵产生的示例(因为它们无法从那里到达玩家) 。

GPU是否可以以某种方式为我确定这些生成点,或者以其他方式确定?我考虑过要在屏幕边缘的建议点和播放器之间制作矢量,然后每10个体素跟随一次矢量,看一面墙是否发生碰撞,然后在此处生成一个单元。

但是,上面提出的解决方案可能会占用过多CPU资源。

关于这个问题有什么建议吗?

注意1 对于生成的单位,我不希望使用任何形式的寻路以避免墙碰撞,因为这些单位朝向玩家。因此,这些单位必须在屏幕的边缘生成,并且该位置必须朝向玩家的直线走,而不会碰到任何墙壁。


1
地图是随玩家一起移动还是玩家在地图中移动?也就是说,地图会改变吗?如果不是这样,我建议您在生成时填写所有不可访问的点,这样您就不必担心它们。
dlras2 2011年

如果玩家正在移动,则这些单位将需要一种寻路方法。如果您想要凹入区域,那么您将遇到这个问题,并且必须为试图到达移动玩家的移动单位提供解决方案……也就是寻找路径。
布劳

1
或者,换句话说,布劳的评论:事实证明,如果玩家可以移动,您的问题就没有有效的答案(除了地图上没有墙/像素的琐碎情况)。仍然需要您定义“直线”的意思,以便在播放器静止时给出答案。
Martin Sojka

Answers:


3

有一个非常有用的算法可以完成这项工作,在这种情况下,效率比泛洪算法要高得多(复杂度是运行时间与边界大小成正比,而不是与泛滥区域成正比):这是行进平方算法。这个概念很简单:从玩家的位置(屏幕中点)开始,选择一个方向,然后步行直到找到墙为止。与墙碰撞时,将启动算法:选择方向(向上或向下),然后开始在该区域的边界上行进,突出显示像素。这为您提供了允许区域的内部边界。然后,您只需检查生成单元的候选点是否在此边界上。

这是运行边界应遵循的原则:

http://en.wikipedia.org/wiki/File:Marchsquares.png (我还不能发布图片)

Wikipedia描述(尽管它具有很多复杂性,因为它已与其他应用程序结合使用):

http://en.wikipedia.org/wiki/Marching_squares


10

从玩家的位置进行洪水填充;那么每个“淹没”的区域就是一个有效的游戏区域,其他所有区域都是墙壁。

编辑:关于“在直线上可到达”的附加要求,请记住,在离散空间中,您必须对此进行进一步定义。例如,在这样的环境中,以上所有路径都可能是有效的“直线”,而我已经看到它们在某个时间点或另一个时间点都已用于游戏中:

“直线”变体

特别是,大多数都不是可交换的 -这意味着仅仅因为您可以沿“直线”从A到达B并不意味着您也可以从B到达A。相反也不一定是正确的。

此外,还有一个问题是,如果一个或两个“侧面”点被阻挡,那么如何处理对角线运动。


可以完全在HLSL中实施吗?
Mathias Lykkegaard Lorenzen 2011年

@Mathias Lykkegaard Lorenzen:是的,有疑问,例如,将算法的每个步骤都作为像素着色器并在两个纹理目标之间进行渲染,但是…… 为什么?无论如何,您可能至少都需要CPU上的算法信息,以便至少查找路径。
Martin Sojka

1
@Mathias Lykkegaard Lorenzen:不过,这与您的要求略有不同。在这种情况下:给定离散空间分区方案,如何定义“直线”?
Martin Sojka

2
即使您不想使用寻路,也可以要求cpu进行Floodfill工作,请记住,只需调用一次Floodfill,然后您将获得3种颜色的纹理,用于定义墙,自由空间和可生成空间。对于4096x4096的纹理,cpu只需不到一秒的时间即可完成Floodfill工作。
Ali1S232 2011年

1
关键是您只需要运行一次泛洪填充,即使您的地形在游戏过程中发生了变化,您也只需更新受影响的部分并通过泛洪即可快速完成。
TravisG 2011年

1

只让产卵怎么样?我认为没有任何特别的问题。


如果它们在墙后产卵怎么办?您将如何让他们接触玩家?
Mathias Lykkegaard Lorenzen 2011年

1
如果游戏中有一个杀死所有敌人的场景,并且产生了50个敌人,但是有几个是在墙后产生的,则可能会出现问题。玩家将无法杀死敌人,场景也无法完成。
Lie Ryan

1
视玩家生成后的移动方式而定,其他单位仍可能无法到达玩家,无论哪种情况,您都必须取消生成某些单位。
aaaaaaaaaaaaaa

战争的迷雾将覆盖the脚的产卵
KRB

1
无论如何,当玩家移动时,这是行不通的。
aaaaaaaaaaaaaa

1

如果仅对播放器有效的直线标记点对您很重要,则可以使用以下算法(这是C ++代码),它比普通的Floodfill消耗更多的资源。可能存在一些小错误(如果有人更正我会很高兴的),因为我自己没有测试代码,但是您会明白的。

void straightlineFill(Point startPoint, Texture target)
{
    queue<Point> pointQueue;
    for (int dx = -1;dx <=1;dx ++)
            for(int dy = -1;dy <=1;dy++)
                if(dx != 0 && dy != 0)
                    pointQueue.push(point(startPoint.x + dx, startPoint.y + dy));
    while (!pointQueue.empty())
    {
        point front = pointQueue.front();
        pointQueue.pop();
        if (target.pixelAt(front) == COLOR_SPAWNABLE||
            target.pixelAt(front) == COLOR_WALL||
            target.pixelAt(front) == COLOR_NOT_SPAWNABLE)
                continue;
        taraget.setPixelAt(front, colorFilled); 
        for (int dx = -1;dx <=1;dx ++)
            for(int dy = -1;dy <=1;dy++)
                if(dx != 0 && dy != 0)
                    pointQueue.push(point(front.x + dx, front.y + dy));
        // up until now was the normal floodfill code
        // and here is the part that will do the real straight line checking

        // lineDX & lineDY will keep how much the line we are checking is skewed
        int lineDX = front.x - startPoint.x;
        int lineDY = front.y - startPoint.y;

        // step will show us how much we have to travel to reach each point of line
        point step;
        if (abs(lineDX) < abs(lineDY))
        {
            if (lineDX < 0)
                step = point(-1,0);
            if (lineDX == 0)
                if (lineDY < 0)
                    step = point(0,-1);
                else
                    step = point(0,1);
            if (lineDX > 0)
                step = point(1,0);
        }

        if (abs(lineDX) < abs(lineDY))
        {
            if (lineDY < 0)
                step = point(0,-1);
            if (lineDY == 0)
                if (lineDX < 0)
                    step = point(-1,0);
                else
                    step = point(1,0);
            if (lineDY > 0)
                step = point(0,1);
        }

        // moved will keep how much we have traveled so far, it's just some value that will speed up calculations and doesn't have any mathematical value
        point moved = 0;

        // spawnable will keep if the current pixel is a valid spawnpaint

        bool spawnable = true;

        // now we will travel on the straight line from player position to the edges of the map and check if whole line is valid spawn points or not.

        while (target.isInside(front))
        {
            front = front + step;
            moved = moved + point(step.x * lineDX, step.y * lineDY);
            if (step.x != 0 && moved.x < moved.y)
            {
                moved.x = moved.x - lineDY * sign(lineDY);
                front.y = front.y + sign(lineDY);
            }
            if (step.y != 0 && moved.y < moved.x)
            {
                moved.y = moved.y - lineDX * sign(lineDX);
                front.x = front.x + sign(lineDX);
            }

            if (target.getPixelAt(front) == COLOR_WALL)
                spawnable = false;

            if (spawnable)
            {
                target.setPixelAt(front,COLOR_SPAWNABLE);
                for (int dx = -1;dx <=1;dx ++)
                    for(int dy = -1;dy <=1;dy++)
                        if(dx != 0 && dy != 0)
                            pointQueue.push(point(front.x + dx, front.y + dy));             
            }
            else
            {
                target.setPixelAt(front,COLOR_NOT_SPAWNABLE);
            }
        }
    }
}

1

您可以使用代表凸面区域的颜色来填充地图...,这样,如果您位于同一区域,则可以生成您的单位。或者,您可以更轻松地搜索可达区域。

这是静态数据,因此您可以对其进行预计算。

您必须填充从凹到凸的变化的图像查找点,在视觉上似乎很容易找到,您有两种情况:

  1. 水平:橙色变为蓝色。
  2. 垂直:红色变为绿色和橙色。

在此处输入图片说明


这行不通。查看右下角,特别是红色区域中的斑点。它完全是凸面的,因此不会发生变化以导致使用另一种颜色,但是显然从右边缘红色的底部到底部边缘红色的最右部分没有直线。
洛伦·佩希特尔

@Loren Pechtel这是手工制作的,您是对的,那里有一个错误,这是我的错,但是您可以意识到,橙色到蓝色过渡的情况也是如此。
布劳

@Loren Pechtel,请注意,不要在黄色区域产卵。此方法可确保如果您在与玩家所在的区域相同的区域释放敌人,这是可以到达的。当然,实现起来可能很棘手,但是这个想法是正确的。
布劳

您的修订没有帮助。凸曲线上的两个点之间永远不会有合法的直线。更多部门将无济于事。
洛伦·佩希特尔

请检查涉及区域或点集的凸面的定义... en.wikipedia.org/wiki/Convex
Blau

1

这是我在自己的游戏中实际使用过的东西(二维单纯形噪声产生的世界,几乎与您的世界完全一样)-射线。从玩家开始,确定方向(如果需要,可以随意),然后沿着那条线直到碰到某物(屏幕边缘或小行星边缘)。如果您击中屏幕边缘(而不是小行星/白色斑点),那么您会知道从屏幕边缘到播放器之间有一条直线。然后在击中点生成一个怪物。如果您撞上了小行星,请重新进行测试。


0

您可以使用的另一种(非GPU)解决方案是路径查找。在绘制地图之前,请在地图的每个边缘上从每个潜在的生成点寻找路径,并查看是否有通往中心的路径。A *寻路在成本/性能上还算不错,但如果有问题,您可以在游戏开始之前做到这一点。

任何没有路径的生成点都可以放在排除列表中;反之亦然(带路径的任何点都可以放在包含列表中)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.