Dungeon thế hệ không có hành lang và phụ thuộc phòng


15

Tôi đang tạo một trò chơi với một thế giới được tạo theo thủ tục được tạo ra vào đầu trò chơi, bao gồm một số khu vực được đại diện bởi các lưới (giả sử, 8x8, 9x6, các kích thước sẽ lý tưởng là tùy ý). Các khu vực này được cho là được kết nối với nhau thông qua một danh sách phụ thuộc.

Một kết nối tồn tại khi có ít nhất 3 không gian của lưới đó được hiển thị giữa hai khu vực đó. Trong ô giữa của khu vực kết nối 3 không gian đó là ô cửa giữa các khu vực:

Tôi đã cố gắng tìm ra cách để kết nối chúng, nhưng nó ngày càng trở nên phức tạp hơn khi bạn cần xem xét cùng một lúc.

Tôi đã thử một số nguyên mẫu giấy và trong khi đó là một quy trình rất đơn giản khi thực hiện nó một cách trực quan, tôi đã không tìm ra một tập hợp các biểu thức toán học tốt cho phép tôi đặt các phòng có cùng hiệu quả bằng mã.

Đây là một ví dụ "đơn giản" mà tôi đang đấu tranh ngay bây giờ:

  • Khu vực 'a' cần được kết nối với 'b' và 'c'
  • Khu vực 'b' cần được kết nối với 'a' và 'd'
  • Khu vực 'c' cần được kết nối với 'a' và 'd'
  • Khu vực 'd' cần được kết nối với 'b' và 'c'

Để đơn giản, chúng tôi sẽ đặt các phòng theo thứ tự xuất hiện của chúng trong danh sách (Tôi đã thử người khác). Vì vậy, tôi đang tiếp cận điều này như là thuật toán Dungeon thế hệ thủ tục tiêu chuẩn của bạn.

Chúng tôi đặt 'a' bất cứ nơi nào trên bảng, vì đó là khu vực đầu tiên. Tiếp theo, chúng tôi chọn một bức tường một cách ngẫu nhiên và, vì không có gì được kết nối với bức tường đó, chúng tôi có thể đặt 'b' ở đó:

Bây giờ chúng tôi cần đặt 'c', nhưng 'a' đã có trên bảng và có một bức tường bị chiếm đóng, vì vậy chúng tôi quyết định đặt nó trên một bức tường khác. Nhưng không phải vị trí nào cũng được, vì 'd' sắp xuất hiện và nó cũng cần được kết nối với 'b' và 'c':

Tôi đã thử một giới hạn có thể là 2 phòng có cùng một bộ phụ thuộc không thể ở trên các bức tường đối diện, nhưng ngay cả điều đó không đảm bảo thành công:

Và trong các trường hợp khác, nơi các khu vực có kích thước khác nhau, nằm trên các bức tường đối diện có thể hoạt động:

Ngoài ra, không xem xét một bức tường được sử dụng là một giả định thiếu sót vì nó loại trừ các giải pháp hợp lệ:

Tôi đã thử tìm kiếm nghiên cứu về các thuật toán tạo thủ tục khác hoặc tương tự, chẳng hạn như thuật toán đóng gói hình chữ nhật và bố cục đồ thị tối ưu, nhưng thông thường các thuật toán đó không tính đến mọi ràng buộc của vấn đề này và khó có thể trộn lẫn với nhau.

Tôi đã nghĩ về một loạt các phương pháp, bao gồm đặt một khu vực và quay lui cho đến khi tìm thấy một vị trí phù hợp, nhưng chúng có vẻ rất phụ thuộc vào thử nghiệm và sai sót và tốn kém về mặt tính toán. Nhưng, dựa trên nghiên cứu sâu rộng về hai vấn đề cuối cùng mà tôi đã đề cập, nó có thể là giải pháp duy nhất / tốt nhất?

Tôi chỉ muốn xem liệu ai đó đã có vấn đề tương tự trong quá khứ hoặc sẵn sàng giúp tôi tìm ra điều này và cho tôi một vài gợi ý về nơi tôi nên bắt đầu với thuật toán. Hoặc, không thành công, tôi sẽ phải xem xét nới lỏng các ràng buộc mà tôi đã đặt ra.


Các phòng phải vuông hoàn toàn?
sói

Nếu bạn có nghĩa là nếu họ phải có 4 bức tường và không nhiều hơn thì có, nhưng tôi đã làm điều đó để đơn giản hóa không gian thế giới. Tôi cần dễ dàng tính toán không gian mà mỗi khu vực chiếm giữ để tôi biết nếu tôi có thể đặt mọi thứ tôi muốn lên đó.
Joana Almeida

Câu trả lời:


6

Đây là một vấn đề tuyệt vời. Tôi tin rằng nó có thể được giải quyết bằng cách sử dụng kế hoạch hành động trong không gian của các vị trí phòng.

Xác định Nhà nước của thế giới như sau:

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

Xác định một ràng buộc là:

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

Trong đó "liền kề" là như bạn mô tả (chia sẻ ít nhất 3 hàng xóm)

Một ràng buộc được cho là vô hiệu mỗi khi hai phòng không liền kề và cả hai phòng đều tồn tại.

Xác định một Nhà nướchiệu lực khi:

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

Xác định một Hành động như một vị trí của một căn phòng, với một Trạng thái hiện tại. Các hành độnghợp lệ khi tình trạng phát sinh từ hành động là hợp lệ. Do đó, chúng tôi có thể tạo danh sách các hành động cho từng trạng thái:

// 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));

Bây giờ, những gì bạn còn lại là một biểu đồ , trong đó Trạng thái là các nút và Hành động là các liên kết. Mục đích là để tìm thấy một nhà nước mà là cả hai có giá trị, tất cả các phòng đã được đặt. Chúng ta có thể tìm thấy một vị trí hợp lệ bằng cách tìm kiếm thông qua biểu đồ một cách tùy ý, có thể sử dụng tìm kiếm theo chiều sâu. Tìm kiếm sẽ trông giống như thế này:

// 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;

Bây giờ chất lượng của ngục tối được tạo ra sẽ phụ thuộc vào thứ tự các phòng và hành động được xem xét. Bạn có thể nhận được kết quả thú vị và khác nhau có thể chỉ bằng cách ngẫu nhiên hoán vị các hành động bạn thực hiện ở mỗi giai đoạn, từ đó thực hiện bước đi ngẫu nhiên qua biểu đồ hành động trạng thái. Hiệu quả tìm kiếm sẽ phụ thuộc rất nhiều vào việc bạn có thể từ chối các trạng thái không hợp lệ nhanh như thế nào. Nó có thể giúp tạo các trạng thái hợp lệ từ các ràng buộc bất cứ khi nào bạn muốn tìm các hành động hợp lệ.


Hài hước bạn nên đề cập đến giải pháp này. Tôi đã nói chuyện với một người bạn trước đó và anh ấy đã đề cập rằng tôi có lẽ nên xem xét các thuật toán Tìm kiếm dựa trên cây, nhưng tôi không chắc chắn làm thế nào để sử dụng chúng trong bối cảnh này. Bài viết của bạn đã được mở mắt! Nó chắc chắn có vẻ là một giải pháp khả thi nếu bạn quản lý việc tạo chi nhánh và thực hiện một vài tối ưu hóa để cắt các nhánh xấu càng sớm càng tốt.
Joana Almeida

7

Ưu tiên thế hệ của bạn là xung đột. Khi tạo các cấp, mục tiêu đầu tiên của bạn phải là một mạng phẳng (không chồng chéo), các điểm được kết nối , không phân biệt quy mô. Sau đó tiến hành tạo phòng từ các điểm trong web đó. Tạo hình dạng phòng đầu tiên là một sai lầm, nói chung. Tạo kết nối trước, và sau đó xem những hình thức phòng nào có thể được cung cấp trong đó.

Thuật toán chung

  1. Tạo lưới sàn được lượng tử hóa với kích thước đủ để hỗ trợ cấp độ của bạn, sử dụng mảng hoặc hình ảnh 2D.

  2. Điểm phân tán ngẫu nhiên trên không gian sàn trống này. Bạn có thể sử dụng kiểm tra ngẫu nhiên đơn giản trên mỗi ô để xem liệu nó có được một điểm hay không, hoặc sử dụng phân phối chuẩn / Gaussian để phân tán các điểm. Gán một giá trị màu / số duy nhất cho mỗi và mọi điểm. Đây là những ID. (PS Nếu sau bước này, bạn cảm thấy cần phải mở rộng không gian của mình, bằng mọi cách, hãy làm.)

  3. Đối với mỗi điểm được tạo như vậy, theo trình tự, tăng dần một vòng tròn giới hạn hoặc giới hạn hình chữ nhật ra bằng một bước duy nhất (thường là tốc độ 0,5-1,0 tế bào / pixel mỗi bước) trong xy. Bạn có thể phát triển song song tất cả các giới hạn, tất cả bắt đầu từ kích thước 0 ở cùng một bước hoặc bạn có thể bắt đầu phát triển chúng ở các thời điểm khác nhau và ở các mức độ khác nhau, tạo ra sự thiên vị cho kích thước của những người bắt đầu sớm hơn (tưởng tượng cây con đang phát triển, trong đó một số bắt đầu muộn). Bằng cách "phát triển", tôi có nghĩa là điền vào các giới hạn mới tăng với màu / ID duy nhất cho điểm bắt đầu cho các giới hạn đó. Một phép ẩn dụ cho điều này sẽ là cầm bút đánh dấu vào mặt sau của một tờ giấy và xem các vết mực có màu khác nhau phát triển, cho đến khi chúng gặp nhau.

  4. Tại một số điểm, giới hạn của một số điểm và một điểm khác sẽ va chạm, trong bước phát triển. Đây là điểm mà bạn nên ngừng tăng giới hạn cho hai điểm đó - ít nhất là theo nghĩa thống nhất được mô tả trong bước 3.

  5. Khi bạn đã tăng giới hạn của tất cả các điểm càng xa càng tốt và dừng tất cả các quy trình tăng trưởng, bạn sẽ có một bản đồ nên phần lớn, nhưng không được điền đầy đủ. Bây giờ bạn có thể muốn đóng gói những khoảng trống đó, mà tôi sẽ cho là màu trắng, như thể tô màu trong một tờ giấy.

Không gian hậu xử lý

Một loạt các kỹ thuật có thể được sử dụng để điền vào các khoảng trống / trắng còn lại, mỗi bước 5:

  • Có một khu vực lân cận, đã được tô màu, yêu cầu không gian, bằng cách lấp đầy màu đó để tất cả cùng tham gia.
  • Lũ với các màu / số / ID mới, chưa sử dụng, sao cho chúng tạo thành các khu vực hoàn toàn mới.
  • Cách tiếp cận vòng tròn sao cho mỗi khu vực lân cận đã được lấp đầy "phát triển" một chút vào khoảng trống. Hãy nghĩ về động vật uống xung quanh một lỗ tưới nước: tất cả chúng đều lấy một ít nước.
  • Đừng hoàn toàn lấp đầy không gian trống, chỉ cần băng qua nó để liên kết các khu vực hiện có bằng các đoạn thẳng.

Lo lắng

Là bước cuối cùng để làm cho mọi thứ trông hữu cơ hơn, bạn có thể thực hiện nhiễu cạnh ở các mức độ khác nhau, trên các ô của các khu vực. Chỉ cần chắc chắn không chặn các tuyến đường di chuyển quan trọng.

Lý thuyết, vì lợi ích

Điều này tương tự như cách tiếp cận được thực hiện trong Voronoi Diagrams / Delaunay Triangulation , ngoại trừ việc ở trên bạn không rõ ràng tạo ra các cạnh - thay vào đó, khi các khu vực giới hạn va chạm, ngừng tăng trưởng. Bạn sẽ nhận thấy rằng Biểu đồ Voronoi được lấp đầy không gian; điều này là do họ không ngừng tăng trưởng chỉ bằng cách chạm vào, mà là ở một mức độ chồng chéo danh nghĩa. Bạn có thể thử tương tự.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.