Bất kỳ mẫu nào để mô hình hóa trò chơi hội đồng? [đóng cửa]


93

Để giải trí, tôi đang cố gắng viết một trong những trò chơi hội đồng yêu thích của con trai tôi dưới dạng một phần mềm. Cuối cùng, tôi mong đợi xây dựng một giao diện người dùng WPF trên đó, nhưng ngay bây giờ tôi đang xây dựng một cỗ máy mô hình hóa trò chơi và các quy tắc của nó.

Khi làm điều này, tôi tiếp tục thấy những vấn đề mà tôi nghĩ là phổ biến đối với nhiều trò chơi trên bàn cờ và có lẽ những người khác đã giải quyết chúng tốt hơn tôi.

(Lưu ý rằng AI để chơi trò chơi và các mô hình xung quanh hiệu suất cao không thú vị với tôi.)

Cho đến nay mô hình của tôi là:

  • Một số loại bất biến đại diện cho các thực thể trong hộp trò chơi, ví dụ như xúc xắc, cờ caro, thẻ, bảng, khoảng trắng trên bàn, tiền, v.v.

  • Một đối tượng cho mỗi người chơi, chứa tài nguyên của người chơi (ví dụ: tiền, điểm số), tên của họ, v.v.

  • Một đối tượng đại diện cho trạng thái của trò chơi: người chơi, lượt của ai, cách bố trí các lựa chọn trên bàn cờ, v.v.

  • Một máy trạng thái quản lý trình tự rẽ. Ví dụ: nhiều trò chơi có một trò chơi nhỏ trước khi mỗi người chơi lăn để xem ai đi trước; đó là trạng thái bắt đầu. Khi lượt của một người chơi bắt đầu, đầu tiên họ lăn, sau đó họ di chuyển, sau đó họ phải nhảy tại chỗ, sau đó những người chơi khác đoán xem họ là giống gà gì, sau đó họ nhận được điểm.

Có một số nghệ thuật trước đây tôi có thể tận dụng không?

CHỈNH SỬA: Một điều tôi nhận ra gần đây là trạng thái trò chơi có thể được chia thành hai loại:

  • Trạng thái tạo tác trò chơi . "Tôi có 10 đô la" hoặc "tay trái của tôi màu xanh lam".

  • Trạng thái trình tự trò chơi . "Tôi đã tung cú đúp hai lần; lần tiếp theo khiến tôi phải ngồi tù". Một cỗ máy trạng thái có thể có ý nghĩa ở đây.

CHỈNH SỬA: Điều tôi thực sự đang tìm kiếm ở đây là cách tốt nhất để triển khai các trò chơi theo lượt nhiều người chơi như Cờ vua hoặc Scrabble hoặc Monopoly. Tôi chắc rằng mình có thể tạo ra một trò chơi như vậy bằng cách bắt đầu hoàn thành nó, nhưng, giống như các Mẫu thiết kế khác, có thể có một số cách để khiến mọi thứ diễn ra suôn sẻ hơn nhiều mà không rõ ràng nếu không nghiên cứu kỹ lưỡng. Đó là những gì tôi đang hy vọng.


3
Bạn đang xây dựng một số loại kết hợp Hokey Pokey, Monopoly, đố chữ?
Anthony Mastrean

Bạn sẽ muốn một máy trạng thái cho bất kỳ quy tắc nào dựa vào trạng thái (lỗi ...) như quy tắc ba nhân đôi cho Độc quyền. Tôi muốn đăng một câu trả lời đầy đủ hơn nhưng tôi không có kinh nghiệm làm việc này. Tôi có thể giáo hoàng về nó mặc dù.
MSN

Câu trả lời:


115

Có vẻ như đây là một chủ đề 2 tháng tuổi mà tôi mới nhận thấy bây giờ, nhưng cái quái gì vậy. Tôi đã thiết kế và phát triển khung trò chơi cho một trò chơi hội đồng thương mại được nối mạng trước đây. Chúng tôi đã có một trải nghiệm rất thú vị khi làm việc với nó.

Trò chơi của bạn có thể có (gần) trạng thái vô hạn vì sự hoán vị của những thứ như người chơi A có bao nhiêu tiền, người chơi B có bao nhiêu tiền, v.v. Do đó, tôi khá chắc chắn rằng bạn muốn để tránh xa các máy nhà nước.

Ý tưởng đằng sau khuôn khổ của chúng tôi là thể hiện trạng thái trò chơi dưới dạng cấu trúc với tất cả các trường dữ liệu cùng nhau, cung cấp trạng thái trò chơi hoàn chỉnh (nghĩa là: nếu bạn muốn lưu trò chơi vào đĩa, bạn ghi cấu trúc đó ra).

Chúng tôi đã sử dụng Mẫu lệnh để đại diện cho tất cả các hành động trò chơi hợp lệ mà người chơi có thể thực hiện. Đây sẽ là một hành động ví dụ:

class RollDice : public Action
{
  public:
  RollDice(int player);

  virtual void Apply(GameState& gameState) const; // Apply the action to the gamestate, modifying the gamestate
  virtual bool IsLegal(const GameState& gameState) const; // Returns true if this is a legal action
};

Vì vậy, bạn thấy rằng để quyết định xem một nước đi có hợp lệ hay không, bạn có thể xây dựng hành động đó và sau đó gọi hàm IsLegal của nó, chuyển trong trạng thái trò chơi hiện tại. Nếu nó hợp lệ và người chơi xác nhận hành động, bạn có thể gọi chức năng Áp dụng để thực sự sửa đổi trạng thái trò chơi. Bằng cách đảm bảo rằng mã trò chơi của bạn chỉ có thể sửa đổi trạng thái trò chơi bằng cách tạo và gửi các Hành động hợp pháp (nói cách khác, Hành động :: Áp dụng nhóm phương pháp là thứ duy nhất trực tiếp sửa đổi trạng thái trò chơi), thì bạn đảm bảo rằng trò chơi của mình trạng thái sẽ không bao giờ vô hiệu. Hơn nữa, bằng cách sử dụng mẫu lệnh, bạn có thể tuần tự hóa các bước di chuyển mong muốn của người chơi và gửi chúng qua mạng để thực hiện trên các trạng thái trò chơi của người chơi khác.

Cuối cùng, đã có một gotcha với hệ thống này hóa ra có một giải pháp khá thanh lịch. Đôi khi các hành động sẽ có hai hoặc nhiều giai đoạn. Ví dụ: người chơi có thể hạ cánh trên một tài sản trong Monopoly và bây giờ phải đưa ra quyết định mới. Trạng thái trò chơi giữa khi người chơi tung xúc xắc và trước khi họ quyết định mua tài sản hay không? Chúng tôi đã quản lý các tình huống như thế này bằng cách giới thiệu một thành viên "Bối cảnh hành động" trong trạng thái trò chơi của chúng tôi. Bối cảnh hành động thường sẽ là trống, cho thấy rằng trò chơi hiện không ở trạng thái đặc biệt nào. Khi người chơi tung xúc xắc và hành động lăn xúc xắc được áp dụng cho trạng thái trò chơi, người chơi sẽ nhận ra rằng người chơi đã hạ cánh xuống một thuộc tính chưa được sở hữu và có thể tạo một "PlayerDecideToPurchaseProperty" mới ngữ cảnh hành động có chứa chỉ số của người chơi mà chúng tôi đang chờ quyết định từ đó. Vào thời điểm hành động RollDice đã hoàn tất, trạng thái trò chơi của chúng tôi thể hiện rằng nó hiện đang chờ người chơi được chỉ định quyết định có mua tài sản hay không. Hiện tại, phương thức IsLegal của tất cả các hành động khác dễ dàng trả về false, ngoại trừ các hành động "BuyProperty" và "PassPropertyPurchaseOpportunity", chỉ hợp pháp khi trạng thái trò chơi có ngữ cảnh hành động "PlayerDecideToPurchaseProperty".

Thông qua việc sử dụng các bối cảnh hành động, không bao giờ có một điểm nào trong thời gian tồn tại của board game mà cấu trúc trạng thái trò chơi không hoàn toàn thể hiện CHÍNH XÁC những gì đang xảy ra trong trò chơi tại thời điểm đó. Đây là một thuộc tính rất đáng mơ ước của hệ thống board game của bạn. Bạn sẽ viết mã dễ dàng hơn nhiều khi bạn có thể tìm thấy mọi thứ bạn muốn biết về những gì đang xảy ra trong trò chơi bằng cách chỉ kiểm tra một cấu trúc.

Hơn nữa, nó mở rộng rất độc đáo cho các môi trường được nối mạng, nơi máy khách có thể gửi hành động của họ qua mạng tới máy chủ, máy chủ có thể áp dụng hành động đó cho trạng thái trò chơi "chính thức" của máy chủ và sau đó lặp lại hành động đó cho tất cả các máy khách khác để để họ áp dụng nó cho các trạng thái trò chơi nhân bản của họ.

Tôi hy vọng điều này ngắn gọn và hữu ích.


4
Tôi không nghĩ nó ngắn gọn, nhưng nó hữu ích! Đã ủng hộ.
Jay Bazuzi

Rất vui vì nó hữu ích ... Phần nào chưa ngắn gọn? Tôi rất vui khi thực hiện chỉnh sửa làm rõ.
Andrew Top

Tôi đang xây dựng một trò chơi theo lượt ngay bây giờ và bài đăng này thực sự hữu ích!
Kiv 22/09/09

Tôi đọc mà Memento là mô hình để sử dụng cho lùi lại ... Memento vs lệnh Pattern cho lùi lại, suy nghĩ của bạn plz ..
zotherstupidguy

Đây là câu trả lời hay nhất mà tôi đã đọc trong Stackoverflow cho đến nay. CẢM ƠN!
Papipo

18

Cấu trúc cơ bản của game engine của bạn sử dụng State Pattern . Các vật phẩm trong hộp trò chơi của bạn là các đĩa đơn thuộc nhiều lớp khác nhau. Cấu trúc của mỗi trạng thái có thể sử dụng Mẫu Chiến lược hoặc Phương pháp Mẫu .

Một Nhà máy được sử dụng để tạo các trình phát được chèn vào danh sách các trình phát, một singleton khác. GUI sẽ theo dõi trên Game Engine bằng cách sử dụng mẫu Observer và tương tác với nó bằng cách sử dụng một trong số các đối tượng Command được tạo bằng Command Pattern . Việc sử dụng Observer và Command có thể được sử dụng trong ngữ cảnh của Chế độ xem thụ động Nhưng chỉ có thể sử dụng bất kỳ mẫu MVP / MVC nào tùy thuộc vào sở thích của bạn. Khi bạn lưu trò chơi, bạn cần lấy một vật lưu niệm về trạng thái hiện tại của nó

Tôi khuyên bạn nên xem qua một số mẫu trên trang web này và xem liệu có mẫu nào thu hút bạn làm điểm khởi đầu hay không. Một lần nữa trung tâm của bảng trò chơi của bạn sẽ là một cỗ máy trạng thái. Hầu hết các trò chơi sẽ được thể hiện bằng hai trạng thái trước khi trò chơi / thiết lập và trò chơi thực tế. Nhưng bạn có thể có nhiều trạng thái hơn nếu trò chơi bạn đang mô hình hóa có một số chế độ chơi riêng biệt. Các bang không nhất thiết phải tuần tự, ví dụ như trò chơi chiến tranh Axis & Battles có một bảng chiến đấu mà người chơi có thể sử dụng để giải quyết các trận chiến. Vì vậy, có ba trạng thái trước trò chơi, bảng chính, bảng chiến đấu với trò chơi liên tục chuyển đổi giữa bảng chính và bảng chiến đấu. Tất nhiên trình tự lần lượt cũng có thể được biểu diễn bằng một máy trạng thái.


15

Tôi vừa hoàn thành việc thiết kế và triển khai một trò chơi dựa trên trạng thái sử dụng tính đa hình.

Sử dụng một lớp trừu tượng cơ sở được gọi là GamePhasecó một phương thức quan trọng

abstract public GamePhase turn();

Điều này có nghĩa là mọi GamePhaseđối tượng giữ trạng thái hiện tại của trò chơi và lệnh gọi để turn()xem trạng thái hiện tại của nó và trả về trạng thái tiếp theo GamePhase.

Mỗi bê tông GamePhasecó các nhà xây dựng giữ toàn bộ trạng thái trò chơi. Mỗi turn()phương pháp có một chút luật chơi bên trong chúng. Trong khi điều này lan truyền các quy tắc xung quanh, nó giữ các quy tắc liên quan gần nhau. Kết quả cuối cùng của mỗi thứ turn()chỉ là tạo ra phần tiếp theo GamePhasevà chuyển ở trạng thái đầy đủ vào giai đoạn tiếp theo.

Điều này cho phép turn()rất linh hoạt. Tùy thuộc vào trò chơi của bạn, một trạng thái nhất định có thể phân nhánh thành nhiều loại giai đoạn khác nhau. Điều này tạo thành một biểu đồ của tất cả các giai đoạn trò chơi.

Ở cấp độ cao nhất, mã để lái nó rất đơn giản:

GamePhase state = ...initial phase
while(true) {
    // read the state, do some ui work
    state = state.turn();
}

Điều này cực kỳ hữu ích vì giờ đây tôi có thể dễ dàng tạo bất kỳ trạng thái / giai đoạn nào của trò chơi để thử nghiệm

Bây giờ để trả lời phần thứ hai của câu hỏi của bạn, điều này hoạt động như thế nào trong nhiều người chơi? Trong các GamePhases nhất định yêu cầu đầu vào của người dùng, một cuộc gọi từ turn()sẽ hỏi dòng điện Playercủa họ Strategyvề trạng thái / pha hiện tại. Strategychỉ là một giao diện của tất cả các quyết định khả thi mà một người Playercó thể đưa ra. Thiết lập này cũng cho phép Strategythực hiện với AI!

Cũng Andrew Top cho biết:

Trò chơi của bạn có thể có (gần) trạng thái vô hạn vì sự hoán vị của những thứ như người chơi A có bao nhiêu tiền, người chơi B có bao nhiêu tiền, v.v. Do đó, tôi khá chắc chắn rằng bạn muốn để tránh xa các máy nhà nước.

Tôi nghĩ câu nói đó rất sai lầm, trong khi đúng là có rất nhiều trạng thái game khác nhau, chỉ có một số pha game thôi. Để xử lý ví dụ của anh ấy, tất cả những gì nó sẽ là một tham số số nguyên cho các hàm tạo của bê tông của tôi GamePhase.

Sự độc quyền

Ví dụ về một số GamePhases sẽ là:

  • GameStarts
  • PlayerRolls
  • PlayerLandsOnProperty (FreeParking, GoToJail, Go, v.v.)
  • PlayerTrades
  • PlayerPurchasesProperty
  • PlayerPurchasesHouses
  • PlayerPurchasesHotels
  • PlayerPaysRent
  • PlayerBankrupts
  • (Tất cả các thẻ Cơ hội và Rương Cộng đồng)

Và một số trạng thái trong căn cứ GamePhaselà:

  • Danh sách người chơi
  • Người chơi hiện tại (đến lượt)
  • Tiền / Tài sản của Người chơi
  • Nhà / Khách sạn trong tài sản
  • Vị trí người chơi

Và sau đó một số pha sẽ ghi lại trạng thái của chính chúng khi cần thiết, ví dụ như PlayerRolls sẽ ghi lại số lần một người chơi tung các cú đúp liên tiếp. Khi chúng tôi rời khỏi giai đoạn PlayerRolls, chúng tôi không quan tâm đến các lần cuộn liên tiếp nữa.

Rất nhiều pha có thể được tái sử dụng và liên kết với nhau. Ví dụ, GamePhase CommunityChestAdvanceToGosẽ tạo giai đoạn tiếp theo PlayerLandsOnGovới trạng thái hiện tại và trả về nó. Trong hàm tạo của PlayerLandsOnGongười chơi hiện tại sẽ được chuyển sang cờ vây và tiền của họ sẽ được tăng thêm 200 đô la.


8

Tất nhiên có rất nhiều, rất nhiều, rất nhiều, rất nhiều, rất nhiều, rất nhiều tài nguyên về chủ đề này. Nhưng tôi nghĩ rằng bạn đang đi đúng hướng khi phân chia các đối tượng và để chúng xử lý các sự kiện / dữ liệu của chính nó, v.v.

Khi chơi trò chơi bảng dựa trên lát gạch, bạn sẽ thấy thật thú vị khi có các thói quen để ánh xạ giữa mảng bảng và hàng / cột và trở lại, cùng với các tính năng khác. Tôi nhớ trò chơi board đầu tiên của mình (cách đây đã lâu) khi tôi đấu tranh với cách lấy hàng / col từ boardarray 5.

1  2  3  
4 (5) 6  BoardArray 5 = row 2, col 2
7  8  9  

Hoài cổ. ;)

Dù sao, http://www.gamedev.net/ là một nơi tốt cho thông tin. http://www.gamedev.net/reference/


Tại sao bạn không chỉ sử dụng mảng 2 chiều? Sau đó, trình biên dịch có thể xử lý điều này cho bạn.
Jay Bazuzi 13/12/08

Lời bào chữa của tôi là chuyện này đã lâu lắm rồi. ;)
Stefan

1
gamedev có rất nhiều thứ, nhưng tôi không thấy rõ thứ mà tôi đang tìm kiếm.
Jay Bazuzi

bạn đang sử dụng ngôn ngữ nào?
zotherstupidguy

Cơ bản, Basica, QB, QuickBasic, v.v. ;)
Stefan

5

Phần lớn tài liệu tôi có thể tìm thấy trên mạng là danh sách các tài liệu tham khảo đã xuất bản. Phần ấn phẩm của Mẫu thiết kế trò chơi có liên kết đến phiên bản PDF của các bài báo và luận văn. Nhiều trong số này trông giống như các bài báo học thuật như Mẫu thiết kế cho trò chơi . Cũng có ít nhất một cuốn sách có sẵn từ Amazon, Các mẫu trong Thiết kế Trò chơi .


3

Three Rings cung cấp các thư viện Java của LGPL. Nenya và Vilya là thư viện cho những thứ liên quan đến trò chơi.

Tất nhiên, sẽ hữu ích nếu câu hỏi của bạn đề cập đến các hạn chế về nền tảng và / hoặc ngôn ngữ mà bạn có thể có.


"Cuối cùng, tôi mong đợi xây dựng một giao diện người dùng WPF" - nghĩa là .NET. Ít nhất, theo như tôi có thể nói.
Mark Allen

Súp bảng chữ cái tôi không biết.
jmucchiello 11/08/08

Vâng, tôi đang làm .NET, nhưng câu hỏi của tôi không phải là ngôn ngữ hoặc nền tảng cụ thể.
Jay Bazuzi

2

Tôi đồng ý với câu trả lời của Pyrolistical và tôi thích cách làm việc của anh ấy hơn (tôi chỉ đọc lướt qua các câu trả lời khác).

Thật trùng hợp, tôi cũng sử dụng cách đặt tên "GamePhase" của anh ấy. Về cơ bản những gì tôi sẽ làm trong trường hợp trò chơi hội đồng theo lượt là có lớp GameState của bạn chứa một đối tượng của GamePhase trừu tượng như đã được Pyrolistical đề cập.

Giả sử các trạng thái trò chơi là:

  1. Cuộn
  2. Di chuyển
  3. Mua / Không mua
  4. Nhà giam

Bạn có thể có các lớp dẫn xuất cụ thể cho từng trạng thái. Có ít nhất các chức năng ảo cho:

StartPhase();
EndPhase();
Action();

Trong hàm StartPhase (), bạn có thể đặt tất cả các giá trị ban đầu cho một trạng thái, ví dụ như vô hiệu hóa đầu vào của người chơi khác, v.v.

Khi roll.EndPhase () được gọi, hãy đảm bảo rằng con trỏ GamePhase được đặt ở trạng thái tiếp theo.

phase = new MovePhase();
phase.StartPhase();

Trong MovePhase :: StartPhase () này, ví dụ, bạn sẽ đặt số lần di chuyển còn lại của người chơi đang hoạt động thành số lượng đã lăn trong giai đoạn trước.

Giờ đây, với thiết kế này, bạn có thể giải quyết vấn đề "3 x double = jail" trong giai đoạn Roll. Lớp RollPhase có thể xử lý trạng thái của chính nó. Ví dụ

GameState state; //Set in constructor.
Die die;         // Only relevant to the roll phase.
int doublesRemainingBeforeJail;
StartPhase()
{
    die = new Die();
    doublesRemainingBeforeJail = 3;
}

Action()
{
    if(doublesRemainingBeforeJail<=0)
    {
       state.phase = new JailPhase(); // JailPhase::StartPhase(){set moves to 0};            
       state.phase.StartPhase();
       return;
    }

    int die1 = die.Roll();
    int die2 = die.Roll();

    if(die1 == die2)
    {
       --doublesRemainingBeforeJail;
       state.activePlayer.AddMovesRemaining(die1 + die2);
       Action(); //Roll again.
    }

    state.activePlayer.AddMovesRemaining(die1 + die2);
    this.EndPhase(); // Continue to moving phase. Player has X moves remaining.
}

Tôi khác Pyrolistical ở chỗ cần phải có một giai đoạn cho mọi thứ, kể cả khi người chơi hạ cánh xuống Rương Cộng đồng hoặc thứ gì đó. Tôi sẽ xử lý tất cả điều này trong MovePhase. Điều này là do nếu bạn có quá nhiều pha nối tiếp nhau, người chơi rất có thể sẽ cảm thấy bị “hướng dẫn” quá nhiều. Ví dụ, nếu có một giai đoạn mà người chơi CHỈ có thể mua tài sản và sau đó CHỈ mua khách sạn và sau đó CHỈ mua nhà, nó giống như không có tự do. Chỉ cần gộp tất cả những phần đó vào một BuyPhase và cho phép người chơi tự do mua bất cứ thứ gì anh ta muốn. Lớp BuyPhase có thể dễ dàng xử lý các giao dịch mua nào là hợp pháp.

Cuối cùng, hãy giải quyết trò chơi. Mặc dù mảng 2D cũng được, nhưng tôi khuyên bạn nên có một biểu đồ ô (trong đó ô là một vị trí trên bảng). Trong trường hợp monoppoly, nó sẽ là một danh sách được liên kết kép. Sau đó, mọi ô sẽ có:

  1. trước đó
  2. nextTile

Vì vậy, sẽ dễ dàng hơn nhiều khi làm những việc như:

While(movesRemaining>0)
  AdvanceTo(currentTile.nextTile);

Chức năng AdvanceTo có thể xử lý hoạt ảnh từng bước của bạn hoặc bất cứ thứ gì bạn thích. Và tất nhiên cũng giảm dần các động thái còn lại.

Lời khuyên của RS Conley về Mẫu người quan sát cho GUI là tốt.

Tôi đã không đăng nhiều trước đây. Hy vọng điều này sẽ giúp ai đó.


1

Có một số nghệ thuật trước đây tôi có thể tận dụng không?

Nếu câu hỏi của bạn không phải là ngôn ngữ hoặc nền tảng cụ thể. thì tôi khuyên bạn nên xem xét các Mẫu AOP cho State, Memento, Command, v.v.

Câu trả lời .NET cho AOP là gì ???

Cũng cố gắng tìm một số trang web thú vị như http://www.chessbin.com

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.