【问题标题】:Card game, architecture for cards纸牌游戏,纸牌建筑
【发布时间】:2013-12-11 16:39:58
【问题描述】:

我仍在学习 C# 的来龙去脉,来自 Python 背景。

考虑到这一点,假设我正在制作一款游戏。为简单起见,让我们以万智牌或任何其他流行的收藏卡牌游戏为基础。在这样的游戏中,可以玩多张牌。这些卡会有不同的类型和效果。例如,可能有一张生物牌,也可能有一张法术牌。

这些类型的卡片具有不同的属性。例如,一个生物将具有attack 属性和health 属性,而法术可能具有effect 属性。但是,这些不同的类型仍然是卡片,并且有多个共同点,例如 cost 属性。这些不同的卡可能由Manager 类管理。此类游戏的基本代码示例可能如下所示:

public class Manager
{
    public CreatureCard card1 = new CreatureCard();
    public SpellCard card2 = new SpellCard();
}

public class Card
{
    public int cost;
}

public class CreatureCard : Card
{
    public int attack;
    public int health;
}

public class SpellCard : Card
{
    public SpellEffect effect;
}

我基本上想要做的是创建卡片并将它们存储在经理类中,而无需事先知道它们是咒语还是生物。经理类可能看起来像这样:

public class Manager
{
    public Card card1;
    public Card card2;
}

卡片会从其他地方分配。

现在,这真的行不通。例如,将无法访问卡的health 属性。这是非常合乎逻辑的,因为卡片甚至可能没有 health 属性。 这就是为什么我正在寻找一种确实有效的方法,同时保持结构大致相同。所以会有一个Card 类,里面有很多东西,以及从它派生的其他类(CreatureCard,SpellCard)。

有没有办法让它工作?我来自这里的 Python,所以这种方法在 C# 中实际上可能是完全错误的。如果是这样,请告诉我可以使用哪种方法来代替我现在尝试开始工作的方法。

【问题讨论】:

  • 你能举一个例子来说明你将如何通过 manager 对象理想地使用卡片吗?换句话说,你怎么知道是访问卡片的health还是effect字段?
  • 我基本上会使用臭名昭著的鸭子打字。例如:这张卡被攻击了(所以它的生命值必须被修改),所以它必须是一个生物。当然有代码可以防止玩家攻击咒语卡。
  • 只是在这里添加一个想法。对我来说,这听起来像是策略设计模式的好机会。您将在界面中定义一个“战斗”方法并注入不同的战斗行为类

标签: c# polymorphism playing-cards


【解决方案1】:

您可以通过适当地转换 Card 实例来做到这一点:

public class Manager
{
    public Card card1;
    public Card card2;
}

...
var manager = new Manager 
{
    card1 = new CreatureCard();
    card2 = new SpellCard();
}
if (manager.card1 is CreatureCard)
{
    var health = ((CreatureCard)manager.card1).Health;
    Console.WriteLine(health);
}
if (manager.card2 is SpellCard)
{
    var effect = ((SpellCard)manager.card2).effect;
    Console.WriteLine(effect);
}

或者像这样:

var creatureCard = manager.card1 as CreatureCard;
var spellCard = manager.card2 as SpellCard;
if (creatureCard != null)
{
    Console.WriteLine(creatureCard.health);
}
if (spellCard != null)
{
    Console.WriteLine(spellCard.effect);
}

对此进行扩展:

  • 要测试卡是否属于指定类型,请使用is operator:

    bool isCardType = myCard is CardType;
    
  • 使用(CardType)到cast / convert的卡到指定类型:

    CardType specificCardType = (CardType)myCard;
    
  • 要安全地将卡片转换为特定类型或返回 null(如果它不是该类型),请使用 as operator:

    CardType specificCardType = myCard as CardType;
    if (specificCardType != null) {
        ...
    }
    

【讨论】:

  • 我还要提一下,如果可能有 多个 类具有您关心的特定属性的卡片,您可能希望以这种方式提取,您会想要为这些属性(或相关属性集)创建接口,让您的类实现这些接口,然后使用与上述相同的逻辑,但检查对象是否是接口,如果是,则直接转换为接口,而不是检查对于个别课程。不过,如果每个属性只有一个类,那就没那么必要了。
  • 谢谢,这基本上就是我想要的。不过,它看起来并不漂亮。有没有更好的写法? @neminem 我已经在这样做了,我只是不想让这个例子更复杂。不过感谢您的建议:)
  • @Lokkij C#(通常)是静态类型的,因此您需要向编译器提供一些关于如何使用对象的提示。从理论上讲,您可以使用dynamic,这会导致在运行时解决所有成员访问,但这既低效又不好。你到底想到了什么?
  • @p.s.w.g 我的意思是((SpellCard)manager.card2).Effect 而不是manager.card2.effect 会使代码看起来更复杂,可读性更低,而且我总是希望我的代码尽可能干净和可读。
  • @Lokkij 使用as 是一个常见的技巧,因为它安全且看起来更好一些。有关示例,请参阅我的更新答案。当然,你必须检查null。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-11
  • 2013-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多