복도 및 객실 의존성이없는 던전 생성


15

나는 게임 시작시 생성 된 절차 적으로 생성 된 세계로 게임을 만들고 있는데, 그리드로 표현되는 여러 영역으로 구성됩니다 (예 : 8x8, 9x6, 크기는 이상적으로 임의적 임). 이 영역들은 의존성 목록을 통해 서로 연결되어 있어야합니다.

해당 그리드의 3 개 이상의 공간이이 두 영역 사이에 노출되면 연결이 존재합니다. 그 3 공간 연결 영역의 중간 셀에는 영역 사이의 출입구가 있습니다.

나는 그것들을 연결하는 방법을 찾으려고 노력했지만 동시에 고려해야 할 더 많은 영역이 점점 복잡해집니다.

나는 종이 프로토 타이핑을 시도했지만 시각적으로 할 때 매우 간단한 과정이지만 코드로 동일한 효율성을 가진 방을 배치 할 수있는 좋은 수학적 표현 세트를 찾지 못했습니다.

지금 당장 어려움을 겪고있는 "간단한"예는 다음과 같습니다.

  • 영역 'a'는 'b'와 'c'에 연결되어야합니다.
  • 영역 'b'는 'a'와 'd'에 연결되어야합니다.
  • 영역 'c'는 'a'와 'd'에 연결되어야합니다.
  • 영역 'd'는 'b'와 'c'에 연결되어야합니다.

간단히하기 위해 목록에 외관 순서대로 방을 배치하고 있습니다 (다른 사람들을 시도했습니다). 표준 절차 던전 생성 알고리즘으로 접근하고 있습니다.

우리는 보드의 어느 곳에 나 'a'를 배치합니다. 첫 번째 영역이기 때문입니다. 다음으로 벽을 무작위로 고르고 그 벽에 연결된 것이 없으므로 'b'를 배치 할 수 있습니다.

이제 'c'를 배치해야하지만 'a'는 이미 보드 위에 있고 점유 된 벽이 있으므로 다른 벽에 놓기로 결정합니다. 그러나 'd'가 나오고 'b'와 'c'도 연결해야하기 때문에 모든 게재 위치가 수행되는 것은 아닙니다.

동일한 종속성 세트를 가진 2 개의 방이 반대쪽 벽에있을 수는 없지만 성공을 보장하지는 않는다는 가능한 제한을 시도했습니다.

다른 경우에는 면적이 다른 벽의 반대편에있는 것이 효과가 있습니다.

또한 사용 된 벽을 고려하지 않는 것은 유효한 솔루션을 배제하기 때문에 잘못된 가정입니다.

Optimal Rectangle Packing 및 Graph Layout 알고리즘과 같은 다른 Procedural Generation 알고리즘 또는 이와 유사한 것에 대한 연구를 시도했지만 일반적으로 이러한 알고리즘은이 문제의 모든 제약 조건을 고려하지 않으며 혼합하기가 어렵습니다.

적절한 배치를 찾을 때까지 영역 및 역 추적 배치를 포함하여 여러 가지 접근 방식에 대해 생각했지만 시행 착오에 매우 의존하고 계산 측면에서 비용이 많이 드는 것으로 보입니다. 그러나 내가 언급 한 마지막 두 가지 문제에 대한 광범위한 연구를 고려할 때 이것이 유일한 해결책일까요?

나는 누군가 과거에 비슷한 문제가 있었는지 알고 싶었고 이것을 알아 내고 알고리즘으로 시작 해야하는 곳에 대한 몇 가지 지침을 제공하려고합니다. 또는 실패하면 설정 한 제약 조건을 풀어야합니다.


객실은 완전히 정사각형이어야합니까?
wolfdawn

만약 그들이 4 개의 벽을 가져야 만하고 그렇지 않다면, 그래도 월드 공간을 단순화하기 위해 그렇게했습니다. 각 영역이 차지하는 공간을 쉽게 계산해야하므로 원하는 모든 것을 넣을 수 있는지 알 수 있습니다.
Joana Almeida

답변:


6

이것은 멋진 문제입니다. 방 배치 공간에서 행동 계획을 사용하여 해결할 수 있다고 생각합니다.

다음과 같이 세계 상태 를 정의하십시오 .

//State:
//    A list of room states.
//    Room state:
//      - Does room exist?
//      - Where is room's location?
//      - What is the room's size?

구속 조건 을 다음과 같이 정의하십시오 .

 // Constraint(<1>, <2>):
 //  - If room <1> and <2> exist, Room <1> is adjacent to Room <2>

"인접한"이 설명한대로 (적어도 3 명의 이웃 공유)

제약 것으로 알려져 무효화 개의 룸이있을 때마다 하지 인접한 두 실이 존재한다.

다음 과 같은 경우에 유효한 상태 를 정의하십시오 .

// foreach Constraint:
//        The Constraint is "not invalidated".
// foreach Room:
//       The room does not intersect another room.

현재 상태가 지정된 경우 조치 를 방의 배치로 정의하십시오 . 작업 이며 유효 작용으로부터 얻어진 상태가 유효 할 때마다. 따라서 각 상태에 대한 작업 목록을 생성 할 수 있습니다.

// Returns a list of valid actions from the current state
function List<Action> GetValidActions(State current, List<Constraint> constraints):
    List<Action> actions = new List<Action>();
    // For each non-existent room..
    foreach Room room in current.Rooms:
        if(!room.Exists)
            // Try to put it at every possible location
            foreach Position position in Dungeon:
                 State next = current.PlaceRoom(room, position)
                 // If the next state is valid, we add that action to the list.
                 if(next.IsValid(constraints))
                     actions.Add(new Action(room, position));

이제, 당신이 남아있는 것은입니다 그래프는 곳, 미국이 노드, 그리고 조치 링크입니다. 목표는 발견하는 국가 입니다 모두 유효을, 그리고 모든 객실이 배치되었습니다. 깊이 우선 검색을 사용하여 임의의 방식으로 그래프를 검색하여 유효한 게재 위치를 찾을 수 있습니다. 검색 결과는 다음과 같습니다.

// Given a start state (with all rooms set to *not exist*), and a set of
// constraints, finds a valid end state where all the constraints are met,
// using a depth-first search.
// Notice that this gives you the power to pre-define the starting conditions
// of the search, to for instance define some key areas of your dungeon by hand.
function State GetFinalState(State start, List<Constraint> constraints)
    Stack<State> stateStack = new Stack<State>();
    State current = start;
    stateStack.push(start);
    while not stateStack.IsEmpty():
        current = stateStack.pop();
        // Consider a new state to expand from.
        if not current.checked:
            current.checked = true;
            // If the state meets all the constraints, we found a solution!
            if(current.IsValid(constraints) and current.AllRoomsExist()):
                return current;

            // Otherwise, get all the valid actions
            List<Action> actions = GetValidActions(current, constraints);

            // Add the resulting state to the stack.
            foreach Action action in actions:
                State next = current.PlaceRoom(action.room, action.position);
                stateStack.push(next);

    // If the stack is completely empty, there is no solution!
    return NO_SOLUTION;

이제 생성 된 던전 의 품질 은 방과 행동이 고려되는 순서에 달려 있습니다. 당신은 무작위로하여하고, 각 단계에서 취할 조치를 치환하는 의해 아마 흥미와 다른 결과를 얻을 수있는 랜덤 워크 (random walk) 상태 액션 그래프를 통해입니다. 검색 효율은 무효 상태를 얼마나 빨리 거부 할 수 있는지에 따라 크게 달라집니다. 유효한 조치를 찾을 때마다 제한 조건에서 유효한 상태 를 생성하는 데 도움이 될 수 있습니다 .


이 솔루션에 대해 언급해야합니다. 이전에 친구와 이야기를했을 때 아마도 트리 기반 검색 알고리즘을 살펴 봐야한다고 언급했지만이 맥락에서 어떻게 사용하는지 잘 모르겠습니다. 귀하의 게시물이 눈을 뜨고있었습니다! 지점 생성을 관리하고 가능한 빨리 불량 지점을 잘라 내기 위해 몇 가지 최적화를 수행하는 경우 실현 가능한 솔루션처럼 보입니다.
Joana Almeida

7

세대 우선 순위가 충돌합니다. 레벨을 생성 할 때 첫 번째 목표는 스케일에 관계없이 평면 웹 (겹치지 않음), 연결된 이어야합니다 . 그런 다음 해당 웹 내의 지점에서 회의실을 작성하십시오. 방 모양을 먼저 만드는 것은 일반적으로 실수입니다. 먼저 연결성을 만든 다음 그 안에 어떤 회의실 양식을 수용 할 수 있는지 확인하십시오.

일반 알고리즘

  1. 2D 배열 또는 이미지를 사용하여 레벨을 지원하기에 충분한 크기의 정량화 된 바닥 격자를 만듭니다.

  2. 이 빈 바닥 공간에 산란 지점이 무작위로 있습니다. 각 타일에서 일반 임의 검사를 사용하여 점이 있는지 확인하거나 표준 / 가우시안 분포를 사용하여 점을 분산시킬 수 있습니다. 각 점마다 고유 한 색상 / 숫자 값을 지정하십시오. 이들은 ID입니다. (PS이 단계 후에 공간을 확장해야한다고 생각되면 반드시 그렇게하십시오.)

  3. 각각의 이러한 생성 된 지점에 대해 순차적으로, 점진적 경계 원 성장 또는 경계 (일반적으로 0.5-1.0 세포 단당 / 픽셀 비율) 단일 단계에 의해 직사각형 xy. 동일한 단계에서 크기가 0부터 시작하여 모든 경계를 동시에 확장하거나 다른 시간과 다른 속도로 성장을 시작할 수 있습니다. 늦게 시작). "성장"이란 새로 증가한 범위를 해당 경계의 시작점에 고유 한 색상 / ID로 채우는 것을 의미합니다. 이것에 대한 은유는 마커 펜을 종이 뒷면에 대고 잡고 서로 다른 색상의 잉크 블랏이 만나는 것을 지켜 보는 것입니다.

  4. 어떤 시점에서 성장 단계 동안 어떤 지점과 다른 지점의 경계가 충돌합니다. 이것은 적어도 두 단계에서 설명 된 균일 한 의미에서 두 지점에 대한 경계 확장을 중단해야하는 지점입니다.

  5. 가능한 한 모든 포인트의 경계를 확장하고 모든 성장 프로세스를 중지하면 맵은 크게 작성되었지만 완전히 채워지지는 않습니다. 이제 빈 공간을 포장하고 싶을 것입니다. 종이에 색칠하는 것처럼 흰색으로 가정합니다.

후 처리 공간 채우기

5 단계마다 남아있는 빈 / 공백을 채우는 데 다양한 기술을 사용할 수 있습니다.

  • 이웃에 이미 색이 칠해진 지역이 공간을 차지하도록하십시오. 그 색으로 물을 채우면 모두 합쳐집니다.
  • 아직 사용하지 않은 새로운 색상 / 숫자 / ID로 홍수를 일으켜 완전히 새로운 영역을 형성하십시오.
  • 라운드 로빈 방식은 이미 채워진 각 인접 영역이 빈 공간으로 약간 "성장"하도록합니다. 급수 구 주위에서 마시는 동물을 생각해보십시오. 모두 물을 얻습니다.
  • 빈 공간을 완전히 채우지 말고 똑 바른 통로를 사용하여 기존 영역을 연결하기 위해 교차하십시오.

섭동

사물을보다 유기적으로 보이게하는 마지막 단계로, 영역의 가장자리 셀에서 다양한 각도로 가장자리 섭동을 수행 할 수 있습니다. 중요한 이동 경로를 막지 마십시오.

관심사를위한 이론

이것은 Voronoi Diagrams / Delaunay Triangulation 의 접근 방식과 비슷하지만 , 위에서 경계가 충돌 할 때 가장자리가 명시 적으로 생성되지 않고 성장이 중단된다는 점을 제외하고는 다릅니다. Voronoi Diagrams는 공간을 채운다는 것을 알 수 있습니다. 터치만으로도 성장을 멈추지 않고 공칭 정도의 중첩으로 성장을 멈추기 때문입니다. 비슷한 시도를 할 수 있습니다.

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