多人卡牌游戏的游戏架构和设计策略

9
我是相对新手的游戏开发者,所以我决定从头开始创建一个业余项目,以获取经验和娱乐。具体的游戏类似于扑克,称为Three Card Brag。这个游戏在电影《大阴谋》中出现过。
我一直在阅读有关游戏开发的一些主题,尤其是this question。这有助于改进我最初创建对象的方式。
我遇到的一个特别问题是定义游戏状态。我的最初方法是将所有内容分开(例如,在Player类中保留筹码堆栈),但在阅读之前提到的问题的回复后,似乎应该在一个GameState对象中维护游戏的所有可能状态。我的解决方案基本上是这样的:
abstract class CardGameState
{
    protected List<Player> _Players;
    protected Player _CurrentPlayer;
    protected Dictionary<Player, int> _Chips;
    protected Dictionary<Player, Hand> _CurrentHand;
    protected Dictionary<Player, PlayerStatuses> _PlayerStatus; // PlayerStatuses.InHand, PlayerStatuses.Folded, PlayerStatuses.SittingOut, etc.
    /* etc. */

每个 CardGameState 都会被某些操作修改:

public interface IAction
{
    string Name { get; }

    CardGameState Apply(CardGameState state);
    bool IsLegal(CardGameState state);
}

现在我非常强烈地感到这违背了面向对象编程的宗旨,因为与玩家相关的数据(在本例中是筹码堆、手牌和当前状态)没有被封装在Player对象中。
另一方面,如果一个玩家要加注,我将创建一个实现IActionRaiseAction,但是IAction接口只接受当前游戏状态,如果筹码堆存储在Player类中,我认为这并不理想。
基本上,我的问题是:我能否在保留给定对象内所有与游戏状态特定相关的数据的同时拥有游戏状态的精确表示?

1
嗨,John,我刚刚在回顾这个问题时意识到,您也可以通过使用访问者模式将IAction.Apply替换为IState.Apply,从而兼得两全其美。现在可能太晚了,但无论如何,它都可能成为未来的参考。 - Robert Gould
2个回答

6
在在线游戏中,使用命令模式(您的IAction)是标准且经过验证的方法。从玩家的角度来看,它不是面向对象的,但是行动是面向对象的,因此从纯理论的角度来看,它是一种可靠的设计模式。实践证明,这是每个成功的在线游戏都采用的方式,但请注意,动作游戏通常使用非常小的离散动作/数据包,直到它实际上成为某种流。
编辑:
很久以后我回到这里,意识到解决这个问题的另一个方法是将GameState的Players、Decks等实现为从一个IState类派生出来,并具有一个Apply(IAction action)成员。这样,对象会对自己应用操作,而不是让应用程序对对象应用操作,这将把操作和状态映射到访问者模式,而不是命令模式。任何一种解决方案都可以工作,其中访问者具有更大的开销和更多的封装性,而命令是更容易的解决方案,封装性较少。

所以,我想问题归结为可变性,例如,玩家的名称将存储在Player类中,因为它不会受到任何游戏操作的影响。这意味着任何可能受到游戏内任何操作影响的内容都直接存储在游戏状态中。 - John Rasch
是的,以某种方式(名称也可能会更改)。但是玩家状态是游戏状态的子部分。 - Robert Gould

1

看起来你可能为了面向对象而面向对象...

看起来像是Bob Martin经典的保龄球游戏问题。

编辑:-摘要-
这是一篇长篇阅读,但基本上,通过TDD和重构,一个保龄球计分应用程序从一个有很多类和多态性的庞大集群变成了20或30行优美的代码。为什么?因为它们一开始就不需要存在。


好的阅读 - 尽管我尝试过不使用面向对象的方法编写此应用程序,但我能挽救的唯一东西是用于手牌比较的类。其余部分是对控制游戏状态的过程性尝试,很容易迷失每个玩家操作如何修改它的轨迹。 - John Rasch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接