펄린 노이즈로 만든 세계에서 스폰 유닛?


16

Perlin 소음 기반 게임에서 몇 가지 문제가 발생했습니다. 아래 첨부 된 스크린 샷을보십시오.

여기에 이미지 설명을 입력하십시오

보이는 흰색 영역은 벽이고 검은 영역은 걸을 수 있습니다. 가운데 삼각형이 플레이어입니다.

이 게임에서 텍스처 (흰색 또는 검은 색 픽셀)로 그린 다음 CPU에서 물리를 구현하여 물리를 구현했습니다.

그러나 이제는 다른 문제가 있습니다. 화면 가장자리에 유닛 (또는 크립)을 지속적으로 스폰하고 싶습니다. 여기서 중요한 점은 마지막 게임에서 플레이어가 멀리서 볼 수없는 "전쟁 안개"가있을 것이라는 점입니다.

화면 가장자리의 픽셀을 스캔하여 물리 텍스처가 검은 색인지 확인한 다음 임의로 물건을 스폰 할 수 있다고 생각했습니다. 그러나 스크린 샷을 다시 살펴보면 왼쪽 상단 모서리에 크립이 생성되지 않기를 원하는 예가 있습니다 (그곳에서 플레이어에게 도달 할 수 없기 때문에) .

어떻게 든 GPU 가이 스폰 스팟을 결정하거나 다른 방법으로 결정할 수 있습니까? 화면 가장자리의 제안 된 지점과 플레이어 사이에 벡터를 만든 다음 10 복셀마다 벡터를 만들고 벽이 충돌하는지 확인한 다음 장치를 생성합니다.

그러나 위의 제안 된 솔루션은 CPU를 너무 많이 사용합니다.

이 문제에 대한 제안?

참고 1 생성 된 유닛의 경우, 플레이어를 향한 벽 충돌을 피하기 위해 어떤 형태의 길 찾기를 사용하고 싶지 않습니다. 따라서 유닛은 플레이어를 향해 직선으로 걷는 것이 벽과 충돌 하지 않는 위치에서 스크린의 가장자리에 스폰되어야합니다 .


1
지도가 플레이어와 함께 이동합니까, 아니면 플레이어가지도에서 이동합니까? 즉,지도가 변경됩니까? 그렇지 않은 경우, 생성 할 수없는 모든 지점을 생성 할 것을 제안하므로 걱정할 필요가 없습니다.
dlras2

플레이어가 움직이고 있다면, 유닛은 경로 찾기 방법이 필요합니다. 오목한 부분을 원한다면이 문제가 생길 것입니다. 이동 장치가 움직이는 플레이어에게 접근하려고하는 경로를 찾는 솔루션을 제공해야합니다.
Blau

1
또는 Blau의 의견을 다른 방법으로 넣으려면 : 플레이어가 움직일 수 있다면 (정말 벽면 / 픽셀이없는 사소한 경우를 제외하고) 귀하의 질문에 유효한 답변 이 없을 것입니다. 플레이어가 움직이지 않는 경우 대답을 얻기 위해 "직선"으로 의미를 정의해야합니다.
Martin Sojka

답변:


3

이 작업에는 꽤 유용한 알고리즘이 있으며,이 상황에서는 플러드 알고리즘보다 훨씬 효율적입니다 (런타임은 플러딩 영역이 아닌 경계 크기에 비례합니다). 행진 사각형 알고리즘입니다. 개념은 간단합니다. 플레이어 위치 (스크린 중간 점)를 시작하고 방향을 선택하고 벽을 찾을 때까지 걷습니다. 벽과 충돌하면 알고리즘이 시작됩니다. 방향 (위 또는 아래)을 선택하고이 영역의 경계를 넘어 행진을 시작하여 픽셀을 강조 표시합니다. 이것은 허용 된 영역의 내부 경계를 제공합니다. 그런 다음, 스폰 유닛의 후보 포인트가이 경계에 있는지 확인하면됩니다.

이것이 경계를 실행하기 위해 따라야하는 원칙입니다.

http://en.wikipedia.org/wiki/File:Marchsquares.png (아직 사진을 올릴 수 없습니다)

위키 백과 설명 (다른 응용 프로그램을 염두에두고 사용하기 때문에 더 복잡하지만) :

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


10

플레이어의 위치에서 홍수를 채우 십시오 . "홍수 된"모든 영역은 유효한 놀이 영역이며, 다른 모든 영역은 벽입니다.

편집 : "직선으로 도달"추가 요구 사항에 관해서는 별도의 공간 에서이를 조금 더 정의 해야한다는 것을 명심하십시오 . 예를 들어, 위의 모든 경로는 그러한 환경에서 유효한 "직선"일 수 있으며, 어느 시점에서나 게임에서 사용 된 모든 경로를 보았습니다.

"직선"변형

특히, 대부분은 정류 적이 지 않습니다. 즉, "직선"으로 A에서 B에 도달 할 수 있다고해서 B에서 A에 도달 할 수도 있다는 의미는 아닙니다. 반대의 경우도 마찬가지입니다.

또한 "측면"지점 중 하나 또는 둘 다가 막히면 대각선 이동을 어떻게 처리해야하는지에 대한 의문이 있습니다.


HLSL에서 완전히 구현할 수 있습니까?
Mathias Lykkegaard Lorenzen

@Mathias Lykkegaard 로렌 : 예, 픽셀 쉐이더와 같은 알고리즘의 각 단계를 수행 예를 들어 두 개의 텍스처 목표 사이에 렌더링하지만 ...에 의해 의심 ? 최소한 경로를 찾기 위해서는 CPU의 알고리즘 정보가 필요할 것입니다.
Martin Sojka

1
@Mathias Lykkegaard Lorenzen : 그래도 요청한 것과 약간 다릅니다. 이 경우 : 개별 공간 분할 스키마를 고려하여 "직선"을 어떻게 정의합니까?
Martin Sojka

2
플러드 필 작업을 수행하도록 CPU에 요청하여 경로 찾기를 사용하지 않더라도 플러드 필을 한 번만 호출하면 벽, 여유 공간 및 생성 가능한 공간을 정의하는 3 가지 색상의 질감이 생깁니다. 4096x4096 텍스처의 경우 CPU가 플러드 필 작업을 완료하는 데 1 초 미만이 소요됩니다.
Ali1S232

1
요점은이 플러드 필을 한 번만 실행하면되며 게임 플레이 중에 지형이 변경 되더라도 영향을받는 섹션 만 업데이트하고 플러드 필을 통해 해당 섹션을지나 쳐야한다는 것입니다.
TravisG

1

스폰이 발생하게하는 것은 어떻습니까? 나는 그것에 특별한 문제가 보이지 않습니다.


그리고 그들이 벽 뒤에 산다면 어떻게 될까요? 플레이어에게 어떻게 도달 할 수 있습니까?
Mathias Lykkegaard Lorenzen

1
게임에서 모든 적을 죽일 시나리오가 있고 50 명의 적을 스폰하지만 벽 뒤에는 몇 명을 스폰하면 문제가 될 수 있습니다. 플레이어는 적을 죽일 수 없어 시나리오가 완료되지 않았습니다.
Lie Ryan

1
다른 유닛은 플레이어가 스폰 된 후 어떻게 움직이는 지에 따라 플레이어에게 도달하지 못할 수 있습니다. 두 경우 모두 일부 유닛을 스폰해야합니다.
aaaaaaaaaaaa

전쟁의 안개는 울퉁불퉁 한 산란을 덮을 것입니다
KRB

1
플레이어가 움직일 때 어쨌든 작동하지 않습니다.
aaaaaaaaaaaa

1

플레이어에게 유효한 직선으로 포인트를 표시하는 것이 중요하다면 다음과 같은 알고리즘을 사용할 수 있습니다 (c ++ 코드 임). 코드를 직접 테스트하지 않았지만 아이디어를 얻을 수 있기 때문에 사소한 버그가있을 수 있습니다 (누군가 수정하면 기쁠 것입니다).

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 이것은 손으로 만들었습니다. 맞습니다. 오류가 있습니다. 제 잘못입니다. 그러나 오렌지에서 파랑으로 전환되는 것과 같은 상황임을 알 수 있습니다.
Blau

@Loren Pechtel은 노란색과 같은 지역에서 산란을 피하는 것을 피하십시오. 이 방법을 사용하면 플레이어가있는 같은 지역에서 적을 놓을 수 있습니다. 물론 구현하기 까다로울 수 있지만 아이디어는 유효합니다.
Blau

수정본이 도움이되지 않습니다. 볼록한 곡선의 두 점은 그 사이에 올바른 직선을 갖지 않습니다. 더 많은 부서가 도움이되지 않습니다.
Loren Pechtel

영역 또는 포인트 세트를 참조하여 볼록한 정의를 확인하십시오 ... en.wikipedia.org/wiki/Convex
Blau

1

여기 내 게임에서 실제로 사용한 것이있다 (2 차원 심플 렉스 노이즈 생성 세계, 거의 당신과 똑같 음). 플레이어에서 시작하여 방향 (원하는 경우 무작위)을 결정한 다음 무언가를 칠 때까지 (화면 가장자리 또는 소행성) 해당 줄을 따라 가십시오. 화면의 가장자리 (소행성 / 흰색 얼룩이 아님)에 부딪 치면 화면 가장자리에서 플레이어까지 직선으로 된 직선이있는 것입니다. 그런 다음 적중 한 지점에서 몬스터를 스폰합니다. 소행성에 부딪 치면 테스트를 다시 수행하십시오.


0

사용할 수있는 또 다른 (GPU가 아닌) 솔루션은 경로 찾기입니다. 맵을 그리기 전에 맵의 각 가장자리에있는 각 잠재적 스폰 지점에서 경로를 찾고 중심에 대한 경로가 있는지 확인하십시오. A * 경로 찾기는 비용 / 성능면에서 꽤 괜찮지 만 문제가있는 경우 게임을 시작하기 전에이 작업을 수행 할 수 있습니다.

경로가없는 스폰 지점은 제외 목록에 올릴 수 있습니다. 또는 그 반대도 가능합니다 (경로가있는 모든 지점을 포함 목록에 추가 할 수 있음).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.