Masa oyunlarını modellemek için herhangi bir model var mı? [kapalı]


94

Eğlenmek için oğlumun en sevdiği masa oyunlarından birini yazılım olarak yazmaya çalışıyorum. Sonunda üstüne bir WPF kullanıcı arayüzü oluşturmayı bekliyorum, ancak şu anda oyunları ve kurallarını modelleyen makineyi yapıyorum.

Bunu yaparken, birçok tahta oyununda ortak olduğunu düşündüğüm sorunları görüyorum ve belki başkaları bunları benden daha iyi çözmüş olabilir.

(Oyunu oynamak için yapay zekanın ve yüksek performansla ilgili kalıpların benim için ilginç olmadığını unutmayın.)

Şimdiye kadarki kalıplarım:

  • Oyun kutusundaki varlıkları temsil eden birkaç değişmez tür, örneğin zar, dama, kart, tahta, tahtadaki boşluklar, para vb.

  • Her oyuncu için oyuncu kaynaklarını (örneğin para, skor), isimlerini vb. İçeren bir nesne.

  • Oyunun durumunu temsil eden bir nesne: sıra gelen oyuncular, tahtadaki peiceslerin düzeni vb.

  • Dönüş sırasını yöneten bir durum makinesi. Örneğin, birçok oyunda her oyuncunun ilk kimin gittiğini görmek için yuvarlandığı küçük bir ön oyun vardır; bu başlangıç ​​durumu. Bir oyuncunun sırası başladığında, önce yuvarlanırlar, sonra hareket ederler, sonra yerinde dans etmek zorundadırlar, sonra diğer oyuncular ne tür tavuk olduklarını tahmin ederler, sonra puan alırlar.

Yararlanabileceğim önceki teknikler var mı?

DÜZENLEME: Son zamanlarda fark ettiğim bir şey, oyun durumunun iki kategoriye ayrılabileceğiydi:

  • Oyun artefakt durumu . "10 dolarım var" veya "sol elim mavi".

  • Oyun sıralaması durumu . "İki kez çift attım; bir sonraki beni hapse atıyor". Bir durum makinesi burada mantıklı olabilir.

DÜZENLEME: Burada gerçekten aradığım şey, Satranç veya Scrabble veya Monopoly gibi çok oyunculu sıra tabanlı oyunları uygulamanın en iyi yolu. Eminim baştan sona üzerinde çalışarak böyle bir oyun yaratabilirim, ancak diğer Tasarım Kalıpları gibi, muhtemelen dikkatli bir çalışma olmadan açık olmayan bazı şeyleri daha sorunsuz ilerletmenin bazı yolları vardır. Umduğum şey bu.


3
Bir tür Hokey Pokey, Monopoly, sessiz sinema karması mı inşa ediyorsun?
Anthony Mastrean

Monopoly için üç çift kuralı gibi devlete (err ...) dayanan herhangi bir kural için bir durum makinesi isteyeceksiniz. Daha kapsamlı bir cevap verirdim ama bunu yapmakta hiç tecrübem yok. Yine de bunu affedebilirim.
MSN

Yanıtlar:


116

Görünüşe göre bu şimdi fark ettiğim 2 aylık bir konu, ama ne halt ediyorsun. Daha önce ticari, ağ bağlantılı bir masa oyunu için oyun çerçevesini tasarladım ve geliştirdim. Onunla çalışırken çok hoş bir deneyim yaşadık.

Oyuncu A'nın ne kadar parası olduğu, oyuncu B'nin ne kadar parası olduğu vb. Gibi şeylerin permütasyonları nedeniyle oyununuz muhtemelen sonsuz sayıda (yakın) durumda olabilir ... Bu nedenle, istediğinizden oldukça eminim devlet makinelerinden uzak durmak.

Çerçevemizin arkasındaki fikir, oyun durumunu, birlikte tam oyun durumunu sağlayan tüm veri alanlarıyla bir yapı olarak temsil etmekti (yani: oyunu diske kaydetmek istiyorsanız, bu yapıyı yazarsınız).

Bir oyuncunun yapabileceği tüm geçerli oyun eylemlerini temsil etmek için Komut Modelini kullandık . İşte örnek bir eylem:

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

Gördüğünüz gibi, bir hareketin geçerli olup olmadığına karar vermek için, bu eylemi oluşturabilir ve ardından mevcut oyun durumuna geçerek onun IsLegal işlevini çağırabilirsiniz. Geçerliyse ve oyuncu eylemi onaylarsa, oyun durumunu gerçekten değiştirmek için Uygula işlevini çağırabilirsiniz. Oyun kodunuzun yalnızca yasal İşlemler oluşturarak ve göndererek oyun durumunu değiştirebileceğinden emin olarak (başka bir deyişle, Eylem :: Uygulama yöntemi ailesi, oyunun durumunu doğrudan değiştiren tek şeydir), ardından oyununuzun devlet asla geçersiz olmayacak. Ayrıca, komut kalıbını kullanarak, oyuncunuzun istediği hareketleri seri hale getirmeyi ve bunları diğer oyuncunun oyun durumlarında uygulanmak üzere bir ağ üzerinden göndermeyi mümkün kılarsınız.

Oldukça zarif bir çözüme sahip olduğu ortaya çıkan bu sistemle sonuçta bir sorun yaşandı. Bazen eylemlerin iki veya daha fazla aşaması olabilir. Örneğin, oyuncu Monopoly'deki bir mülke inebilir ve şimdi yeni bir karar vermelidir. Oyuncunun zar atması ile bir mülk satın almaya karar vermesi arasındaki oyun durumu nedir? Oyun durumumuzun bir "Eylem Bağlamı" üyesini öne çıkararak bu gibi durumları yönettik. Eylem bağlamı normalde boş olur ve oyunun şu anda herhangi bir özel durumda olmadığını gösterir. Oyuncu zarı attığında ve zar atma eylemi oyun durumuna uygulandığında, oyuncunun sahip olunmayan bir mülke indiğini anlayacak ve yeni bir "PlayerDecideToPurchaseProperty" oluşturabilir. Karar beklediğimiz oyuncunun indeksini içeren eylem bağlamı. RollDice eylemi tamamlandığında, oyun durumumuz şu anda belirtilen oyuncunun bir mülk satın alıp almayacağına karar vermesini beklediğini gösterir. Yalnızca oyun durumu "PlayerDecideToPurchaseProperty" eylem içeriğine sahip olduğunda yasal olan "BuyProperty" ve "PassPropertyPurchaseOpportunity" eylemleri dışında, diğer tüm eylemlerin IsLegal yönteminin yanlış döndürmesi artık kolaydır.

Aksiyon bağlamlarının kullanımıyla, oyun durumu yapısının tam olarak oyunda o noktada olanları tam olarak temsil etmediği masa oyununun yaşam süresi boyunca hiçbir zaman tek bir nokta yoktur. Bu, masa oyunu sisteminizin çok arzu edilen bir özelliğidir. Sadece tek bir yapıyı inceleyerek oyunda olup bitenlerle ilgili bilmek istediğiniz her şeyi bulabildiğinizde kod yazmanız çok daha kolay olacaktır.

Ayrıca, istemcilerin eylemlerini bir ağ üzerinden bir ana makineye gönderebildikleri, eylemi ana bilgisayarın "resmi" oyun durumuna uygulayabildiği ve ardından bu eylemi diğer tüm istemcilere yansıtabildiği ağ ortamlarına çok hoş bir şekilde uzanır kopyalanmış oyun durumlarına uygulamalarını sağlayın.

Umarım bu kısa ve yardımcı olmuştur.


4
Kısa olduğunu sanmıyorum ama yardımcı oluyor! Olumlu oy verildi.
Jay Bazuzi

Yardımcı olduğuna sevindim ... Hangi kısımlar özlü değildi? Bir açıklama düzenlemesi yapmaktan mutluluk duyarım.
Andrew Top

Şu anda sıra tabanlı bir oyun geliştiriyorum ve bu yazı gerçekten çok yardımcı oldu!
Kiv


Bu şimdiye kadar Stackoverflow'da okuduğum en iyi cevap. TEŞEKKÜRLER!
Papipo

19

Oyun motorunuzun temel yapısı Durum Modelini kullanır . Oyun kutunuzun öğeleri çeşitli sınıfların tekli öğesidir . Her bir durumun yapısı Strateji Modeli veya Şablon Yöntemini kullanabilir .

Bir fabrika , başka bir tekli oyuncu listesine eklenen oyuncuları oluşturmak için kullanılır. GUI, Gözlemci modelini kullanarak Oyun Motorunu izlemeye devam edecek ve Komut Kalıbı kullanılarak oluşturulan birkaç Komut nesnesinden birini kullanarak bununla etkileşime girecektir . Gözlemci ve Komut kullanımı, bir Pasif Görünüm bağlamında kullanılabilir, ancak tercihlerinize bağlı olarak hemen hemen her MVP / MVC modeli kullanılabilir. Oyunu kaydettiğinizde, mevcut durumuyla ilgili bir hatıra almanız gerekir

Bu sitedeki bazı kalıplara bakmanızı ve bunlardan herhangi birinin sizi başlangıç ​​noktası olarak yakalayıp yakalamadığını görmenizi öneririm . Yine oyun tahtanızın kalbi bir durum makinesi olacak. Çoğu oyun, oyun öncesi / kurulum ve gerçek oyun olmak üzere iki durumla temsil edilir. Ancak, modellediğiniz oyunun birkaç farklı oyun modu varsa daha fazla duruma sahip olabilirsiniz. Durumların sıralı olması gerekmez, örneğin savaş oyunu Axis & Battles, oyuncuların savaşları çözmek için kullanabilecekleri bir savaş tahtasına sahiptir. Yani oyun öncesi, ana kart, savaş tahtası ve oyun sürekli olarak ana tahta ve savaş tahtası arasında değişen üç durum vardır. Tabii ki, dönüş sırası bir durum makinesi ile de temsil edilebilir.


17

Polimorfizm kullanarak durum tabanlı bir oyun tasarlamayı ve uygulamayı yeni bitirdim.

GamePhaseBir önemli yöntemi olan temel soyut bir sınıf kullanmak

abstract public GamePhase turn();

Bunun anlamı, her GamePhasenesnenin oyunun mevcut durumunu tutması ve mevcut durumuna turn()bakıp bir sonrakini döndürme çağrısıdır GamePhase.

Her betonun tüm oyun durumunu GamePhasetutan kurucuları vardır . Her yöntemin içinde biraz oyun kuralları vardır. Bu, kuralları etrafa yayarken, ilgili kuralları birbirine yakın tutar. Her birinin nihai sonucu , sadece bir sonrakini yaratmak ve tam durumda bir sonraki aşamaya geçmektir.turn()turn()GamePhase

Bu turn()çok esnek olmasına izin verir . Oyununuza bağlı olarak, belirli bir durum birçok farklı aşamaya dalabilir. Bu, tüm oyun aşamalarının bir grafiğini oluşturur.

En üst düzeyde, sürmek için kod çok basittir:

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

Oyunun herhangi bir durumunu / aşamasını artık test etmek için kolayca oluşturabildiğim için bu son derece yararlı

Şimdi sorunuzun ikinci bölümünü yanıtlamak için, bu çok oyunculu modda nasıl çalışıyor? Belli dahilinde GamePhasekullanıcı girişi gerektiren s, gelen çağrı turn()akımı isteyeceğini Playeronların Strategyverilen mevcut durumu / safhası. Strategysadece Playerverilebilecek tüm olası kararların bir arayüzüdür . Bu kurulum ayrıca StrategyAI ile uygulanmasına izin verir !

Andrew Top ayrıca şunları söyledi:

Oyuncu A'nın ne kadar parası olduğu, oyuncu B'nin ne kadar parası olduğu vb. Gibi şeylerin permütasyonları nedeniyle oyununuz muhtemelen sonsuz sayıda (yakın) durumda olabilir ... Bu nedenle, istediğinizden oldukça eminim devlet makinelerinden uzak durmak.

Bu ifadenin çok yanıltıcı olduğunu düşünüyorum, birçok farklı oyun durumu olduğu doğru olsa da, sadece birkaç oyun aşaması var. Onun örneğini ele alacak olursak, tüm bunlar benim somut GamePhases'min kurucuları için bir tamsayı parametresi olacaktır .

Tekel

Bazı GamePhases örnekleri şöyle olacaktır:

  • GameStarts
  • PlayerRolls
  • PlayerLandsOnProperty (FreeParking, GoToJail, Go, vb.)
  • Oyuncu İşlemleri
  • PlayerPurchasesProperty
  • Oyuncu Satın Alım Evleri
  • PlayerPurchasesHotels
  • PlayerPaysRent
  • PlayerBankrupts
  • (Tüm Şans ve Topluluk Sandığı kartları)

Ve üssündeki bazı eyaletler GamePhase:

  • Oyuncu Listesi
  • Mevcut Oyuncu (sıra kimde?)
  • Oyuncunun Parası / Mülkü
  • Gayrimenkullerde Evler / Oteller
  • Oyuncu pozisyonu

Ve sonra bazı aşamalar gerektiği gibi kendi durumlarını kaydederdi, örneğin PlayerRolls bir oyuncunun arka arkaya çift atma sayısını kaydeder. PlayerRolls aşamasını terk ettiğimizde, art arda atılan atmalarla artık ilgilenmiyoruz.

Birçok aşama yeniden kullanılabilir ve birbirine bağlanabilir. Örneğin , mevcut durumla bir GamePhase CommunityChestAdvanceToGosonraki aşamayı oluşturur PlayerLandsOnGove onu döndürür. PlayerLandsOnGoMevcut oyuncunun kurucusunda Go'ya taşınacak ve parası 200 $ artırılacaktı.


9

Elbette bu konu hakkında çok, çok, çok, çok, çok, çok, çok kaynak var. Ama nesneleri bölmek ve kendi olaylarını / verilerini vb. Ele almalarına izin vermek için doğru yolda olduğunuzu düşünüyorum.

Döşemeli tahta oyunları yaparken, diğer özelliklerin yanı sıra, tahta dizisi ile satır / sütun ve arka arasında eşleme rutinlerine sahip olmanın hoş olduğunu göreceksiniz. Boardarray 5'ten nasıl row / col alacağımı düşündüğümde (uzun zaman önce) ilk masa oyunumu hatırlıyorum.

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

Nostalji. ;)

Her neyse, http://www.gamedev.net/ bilgi için iyi bir yerdir. http://www.gamedev.net/reference/


Neden 2 boyutlu bir dizi kullanmıyorsun? Sonra derleyici bunu sizin için halledebilir.
Jay Bazuzi

Bahanem, bunun uzun zaman önce olması. ;)
Stefan

1
gamedev'de bir sürü şey var, ama aradığımı tam olarak görmedim.
Jay Bazuzi

hangi dili kullanıyordun
zotherstupidguy

Temel, Temel, QB, QuickBasic vb. ;)
Stefan


4

Three Rings , LGPL'de Java kitaplıkları sunar. Nenya ve Vilya, oyunla ilgili şeyler için kütüphanelerdir.

Elbette, sorunuzun platform ve / veya sahip olabileceğiniz dil kısıtlamalarından bahsetmesi yardımcı olacaktır.


"Sonunda bir WPF kullanıcı arabirimi oluşturmayı bekliyorum" - yani .NET. En azından söyleyebileceğim kadarıyla.
Mark Allen

Alfabe çorbası bilmiyorum.
jmucchiello

Evet, .NET yapıyorum, ancak sorum dile veya platforma özgü değil.
Jay Bazuzi

3

Pyrolistics'in cevabına katılıyorum ve işleri yapma şeklini tercih ediyorum (yine de diğer cevapları gözden geçirdim).

Tesadüfen onun "GamePhase" adını da kullandım. Temel olarak, sıra tabanlı bir tahta oyunu durumunda yapacağım şey, GameState sınıfınızın Pyrolistical tarafından belirtildiği gibi soyut GamePhase'in bir nesnesini içermesidir.

Oyun durumlarının şöyle olduğunu söyleyelim:

  1. Rulo
  2. Hareket
  3. Satın Al / Satın Alma
  4. Hapis

Her eyalet için somut türetilmiş sınıflarınız olabilir. En azından aşağıdakiler için sanal işlevlere sahip olun:

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

StartPhase () işlevinde, bir durum için tüm başlangıç ​​değerlerini ayarlayabilirsiniz, örneğin diğer oyuncunun girişini devre dışı bırakmak vb.

Roll.EndPhase () çağrıldığında, GamePhase işaretçisinin sonraki duruma ayarlandığından emin olun.

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

Bu MovePhase :: StartPhase () 'de, örneğin aktif oyuncunun kalan hareketlerini önceki aşamada oynanan miktara ayarlarsınız.

Şimdi bu tasarım yerinde olduğunda, "3 x çift = hapishane" probleminizi Roll aşamasında çözebilirsiniz. RollPhase sınıfı kendi durumunu idare edebilir. Örneğin

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.
}

Pyrolistics'ten farklıyım, oyuncunun Topluluk sandığına inmesi de dahil olmak üzere her şey için bir aşama olması gerekiyor. Tüm bunları MovePhase'de halledecektim. Bunun nedeni, çok fazla ardışık aşamanız varsa, oyuncu büyük olasılıkla çok "yönlendirilmiş" hissedecektir. Örneğin, oyuncunun YALNIZCA mülk satın alabileceği ve sonra YALNIZCA otel satın alabileceği ve sonra YALNIZCA ev satın alabileceği bir aşama varsa, sanki özgürlük yokmuş gibi. Tüm bu parçaları tek bir BuyPhase'e çarpın ve oyuncuya istediği her şeyi satın alma özgürlüğü verin. BuyPhase sınıfı, hangi satın alımların yasal olduğunu kolayca halledebilir.

Son olarak oyun alanına değinelim. Bir 2D dizisi iyi olsa da, bir karo grafiğine sahip olmanızı tavsiye ederim (burada bir döşemenin tahtadaki bir konumdur). Monopoli durumunda, çift bağlantılı bir liste olmayı tercih eder. O zaman her karonun bir:

  1. previousTile
  2. nextTile

Bu nedenle, aşağıdaki gibi bir şey yapmak çok daha kolay olacaktır:

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

AdvanceTo işlevi, adım adım animasyonlarınızı veya istediğiniz her şeyi yönetebilir. Ve elbette kalan hareketleri de azaltın.

RS Conley'in GUI için Gözlemci Modeli Önerisi iyidir.

Daha önce pek bir şey göndermedim. Umarım bu birine yardımcı olur.


2

Yararlanabileceğim önceki teknikler var mı?

Sorunuz dile veya platforma özgü değilse. o zaman Durum, Memento, Komut vb.İçin AOP Kalıplarını düşünmenizi tavsiye ederim

AOP'nin .NET yanıtı nedir ???

Ayrıca http://www.chessbin.com gibi harika web siteleri bulmaya çalışın

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.