2D지도에서 "본토"를 감지하는 알고리즘이 있습니까?


28

이지도에서 "본토"는 네 개의 기본 방향 (북쪽, 남쪽, 동쪽, 서쪽-대각선이 아님)으로지도의 중심에 연결할 수있는 모든 땅입니다. 여기에 이미지 설명을 입력하십시오

본토를 감지하고 구멍을 채우고 싶습니다. 나는 세 가지를 생각했다 :

  1. 경로 찾기 알고리즘을 사용하여지도의 중앙에 연결할 수 있으면 모든 비수 (암세포) 셀에서 검색하십시오. 너무 비싼! 그러나 이것은 섬에 효과가 있습니다.

  2. 본토에는 녹색 페인트 통이 가득합니다. 각 구멍은 페인트로 둘러싸여 있습니다. 이제 무엇? 본토 내부의 모든 수위에서 인접성을 확인하면 해안선에 표시된 일부 반도 및 기타 지리적 특징을 삭제합니다.

  3. 본토를 알아 내기위한 일종의 가장자리 탐지. 안에있는 것을 유지하고, 물인 경우 채우고, 밖에있는 것을 제거하십시오. 복잡한?

아마도 일부 게임 경험이 풍부한 개발자 가이 문제를 해결하여 알려진 알고리즘이나 기술의 이름을 알려줄 수 있습니까?


4
이지도를 생성하기 위해 어떤 종류의 알고리즘을 사용했는지 궁금합니다. 그렇다면 무엇을 사용 했습니까?
jgallant

타일 ​​기반 환경에서 작업 할 때 가치가있는 것은 Marching Squares Algorithm 입니다. 그것으로 당신은 작은 섬도 감지하고 유지할 수 있으며, 단일 셀 섬을 버리는 크기 또는 기준에 따라 크기별로 정렬 할 수 있습니다.
윌리엄 마리아 거

@ 예 네! 다이아몬드 사각형 알고리즘을 사용하여 높이 맵을 생성 한 다음 한 값은 모두 물, 나머지, 땅입니다. 이것을 대륙 모양으로 사용하고 지형 세부 사항을 추가하는 또 다른 패스를 사용할 계획입니다.
Gabriel A. Zorrilla가

@Mind Marching Squares 알고리즘은 대륙의 경계를 그리는 데 유용합니다. 좋은 제안 감사합니다!
가브리엘 A. 조 릴라

답변:


32

제도 제거

나는 내 게임 중 하나에서 이런 종류의 일을 해본 적이있다. 외부 섬을 제거하기 위해 프로세스는 기본적으로 다음과 같습니다.

  1. 먼저지도의 중심이 항상 본토에 속하고 각 픽셀이 "랜드"또는 "물"(즉, 다른 색상)로 시작한다는 보장이 있어야합니다.
  2. 그런 다음 지도 중심에서 시작하여 "랜드"타일 전체에 4 방향 홍수 채우기를 수행합니다 . 이 플러드 필에 방문한 모든 픽셀을 "MainLand"와 같은 다른 유형으로 표시하십시오.
  3. 마지막으로 전체지도를 살펴보고 남은 "랜드"픽셀을 "물"로 변환하여 다른 섬을 제거하십시오.

호수 제거

섬 내부의 구멍 (또는 호수)을 제거하는 것과 비슷한 과정을하지만지도 모서리에서 시작하여 대신 "물"타일을 통해 퍼집니다. 이렇게하면 "바다"를 다른 물 타일과 구별 할 수 있으며 이전에 섬을 제거한 것처럼 제거 할 수 있습니다.

여기 어딘가에있는 홍수 채우기 구현을 파헤 보겠습니다 (면책 조항, 효율성에 대해서는 신경 쓰지 않았으므로 그것을 구현하는 더 효율적인 방법 이 많이 있다고 확신 합니다).

private void GenerateSea()
{
    // Initialize visited tiles list
    visited.Clear();

    // Start generating sea from the four corners
    GenerateSeaRecursive(new Point(0, 0));
    GenerateSeaRecursive(new Point(size.Width - 1, 0));
    GenerateSeaRecursive(new Point(0, size.Height - 1));
    GenerateSeaRecursive(new Point(size.Width - 1, size.Height - 1));
}

private void GenerateSeaRecursive(Point point)
{
    // End recursion if point is outside bounds
    if (!WithinBounds(point)) return;

    // End recursion if the current spot is a land
    if (tiles[point.X, point.Y].Land) return;

    // End recursion if this spot has already been visited
    if (visited.Contains(point)) return;

    // Add point to visited points list
    visited.Add(point);

    // Calculate neighboring tiles coordinates
    Point right = new Point(point.X + 1, point.Y);
    Point left = new Point(point.X - 1, point.Y);
    Point up = new Point(point.X, point.Y - 1);
    Point down = new Point(point.X, point.Y + 1);

    // Mark neighbouring tiles as Sea if they're not Land
    if (WithinBounds(right) && tiles[right.X, right.Y].Empty)
        tiles[right.X, right.Y].Sea = true;
    if (WithinBounds(left) && tiles[left.X, left.Y].Empty)
        tiles[left.X, left.Y].Sea = true;
    if (WithinBounds(up) && tiles[up.X, up.Y].Empty)
        tiles[up.X, up.Y].Sea = true;
    if (WithinBounds(down) && tiles[down.X, down.Y].Empty)
        tiles[down.X, down.Y].Sea = true;

    // Call the function recursively for the neighboring tiles
    GenerateSeaRecursive(right);
    GenerateSeaRecursive(left);
    GenerateSeaRecursive(up);
    GenerateSeaRecursive(down);
}

나는 이것을 게임에서 호수를 없애기위한 첫 단계로 사용했다. 그것을 부른 후, 내가해야 할 일은 다음과 같습니다.

private void RemoveLakes()
{
    // Now that sea is generated, any empty tile should be removed
    for (int j = 0; j != size.Height; j++)
        for (int i = 0; i != size.Width; i++)
            if (tiles[i, j].Empty) tiles[i, j].Land = true;
}

편집하다

주석을 기반으로 추가 정보를 추가합니다. 검색 공간이 너무 큰 경우 재귀 버전의 알고리즘을 사용할 때 스택 오버플로가 발생할 수 있습니다. 여기의 링크 알고리즘의 비 재귀 버전으로 유래에은 (말장난 의도 :-))는을 사용하여, Stack<T>내 대답에 맞게 (또한 C #으로 대신하지만, 다른 언어에 적응하기 용이해야하고, 그 다른 구현이있다 링크도).


11
중앙 타일이 항상 토지와 본토의 일부임을 보장 할 수없는 경우 홍수 채우기를 사용하여 각 토지 타일을 특정 섬에 속하는 것으로 표시하십시오 (지도에 얼룩 ID가없는 모든 타일의 홍수 채우기, 채워진 타일 표시) blobid가없는 경우 동일한 blobid). 그런 다음 타일이 가장 많은 얼룩을 제외한 모든 얼룩을 제거하십시오.
George Duckett

GeorgeDuckett의 말과 일치하여 Googling Blob 감지 알고리즘을 사용해 볼 수 있습니다. FTIR을 사용하는 멀티 터치에서 일반적인 문제입니다. 나는 더 똑똑한 알고리즘을 생각해 냈지만 그것이 어떻게 작동했는지를 기억하지 못한다.
Jonathan Dickinson 2012 년

PHP에서 스택 문제가 있었으므로 LIFO 플러드 필을 구현하고 놀랐습니다.
Gabriel A. Zorrilla

BFS 알고리즘에서 DFS 알고리즘을 호출 할 때만 플러드 필이 아닙니까? 누군가를 설명하십시오.
jcora

@ 베인 무슨 뜻인가요? DFS 또는 BFS의 차이점은 노드 방문 순서입니다. 플러드 필 알고리즘은 순회 순서를 지정한다고 생각하지 않습니다. 노드를 다시 방문하지 않고 전체 지역을 채우는 한 신경 쓰지 않습니다. 순서는 구현에 따라 다릅니다. 대기열을 사용할 때와 스택을 사용할 때 순회 순서를 비교하는 그림 은 위키 백과 항목 의 하단을 참조하십시오 . 재귀 구현은 호출 스택을 사용하기 때문에 스택으로 간주 될 수 있습니다.
David Gouveia


5

이것은 이미지 처리의 표준 작업입니다. 2 단계 작업을 사용합니다.

지도 사본을 만들어 시작하십시오. 이지도에서 바다와 접하는 모든 랜드 픽셀을 바다 픽셀로 바꿉니다. 이 작업을 한 번 수행하면 2x2 섬이 제거되고 더 큰 섬이 축소됩니다. 두 번 수행하면 4x4 섬 등이 제거됩니다.

2 단계에서는 거의 반대 가됩니다. 원래지도에서 토지 픽셀 인 경우에만 육지와 접하는 모든 바다 픽셀을 토지 픽셀로 바꿉니다 (1 단계에서 사본을 만든 이유). 이것은 1 단계에서 완전히 제거되지 않는 한 섬을 원래 형태로 다시 자릅니다.


1

비슷한 문제가 있었지만 게임 개발에는 없었습니다. 서로 인접하고 동일한 값 (연결된 영역)을 가진 이미지에서 픽셀을 찾아야했습니다. 재귀 플러드 필을 사용해 보았지만 스택 오버플로가 계속 발생했습니다 (초보 프로그래머입니다 : P). 그런 다음 http://en.wikipedia.org/wiki/Connected-component_labeling 이 방법을 사용해 보았습니다 . 어쨌든 내 문제는 실제로 훨씬 더 효율적입니다.


플러드 알고리즘의 스택 버전을 사용해보고 스택 문제를 저장해야합니다. CCL보다 훨씬 더 단순하게 결과 (내가 행운과 함께 지난 2 주 근무했습니다.)
가브리엘 A. Zorrilla

네, 둘 다 시도했지만 CCL이 먼저 작동하도록했습니다 .P는 깔끔한 아이디어라고 생각했습니다. 문제를 해결하게 된 것을 기쁘게 생각합니다 :)
Elegant_Cow
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.