哪些设计模式可以帮助模拟行为动态变化的对象,或者具有许多可选行为的对象?

时间:2016-01-31 05:05:08

标签: oop design-patterns game-engine

很抱歉这些冗长的例子,但我最近遇到过这种类型的软件设计问题,并且一直在思考它。不确定是否有一个术语,所以我将给出两个一般的例子来提出一个想法:

前1:你正在制作一个RPG游戏,你有一个主要角色的课程。角色对游戏世界的反应会根据你穿着/持有的技能,分配的技能,基本上是对象的内部状态而改变。

假设你在游戏中有以下项目:

  • 再生之戒:让你的角色随着时间的推移重生健康
  • 偷偷摸摸的运动鞋:增加偷偷摸摸
  • 魔镜:反映传入伤害的百分比
  • Doofy眼镜:突出手工艺资源

Ex 2:你有一辆丰田卡罗拉。它有不同的配置,但它们都是丰田花冠。 ABS和牵引力控制是您可以添加到基线模型的可选功能(行为)。

  • 基线模型在运行时不会做任何额外的事情。
  • 使用ABS时,汽车检查并响应汽车行驶时的突然停车
  • 通过牵引力控制,汽车检查并响应汽车行驶时的牵引力损失
  • 显然,当你有一辆同时拥有两辆车时,你会在汽车行驶时做两种行为。

两个例子的共同属性:

  • 重要性类有一个具体的空白石板,它可以从
  • 开始
  • 可选项/部件可以为该对象添加能力或额外行为;每场比赛的额外费用/运行时
  • 当项目/行为被添加或取消时,
  • 可能会或可能不会改变对象(穿上运动鞋时会增加潜行,然后将其减掉)

潜在的解决方案(但不充分):

if statement:

if ch.isWearing 'doofy glasses':
    render with doofy shader
else if ch.isWearing ...

不起作用。需要为每个部分添加一个子句。班级可以非常快速地变得大而复杂。

策略模式:

class TractionControlStrategy
class ABSstrategy

class Toyota:
    TractionControlStrategy tcs
    ABSstrategy abs
    run():
        if tcs not null:
            tcs.run()
        if abs not null:
            abs.run()

carWithTCS = new Toyota(new TractionControlStrategy())

没有比之前的解决方案好多了,因为你仍然有很长的if语句列表

带子类的策略:

class Toyota:
    run():
        // does nothing

class ToyotaWithABS : Toyota
    ABSstrategy abs = new ABSstrategy()
    run():
        abs.run

class ToyotaWithTCS : Toyota ...

我认为满足开放/封闭原则。可能比前一个好吗?但是现在你必须为每个配置组合创建一个类。如果你后来发现还有其他可选功能,那么你需要实现的每个功能的类数都会增加一倍...

如何使用OOP对这些类型的交互和行为进行建模?什么设计模式或设计模式的组合促进了这种事情?

真的不确定这是一个好问题,还是我清楚我要问的是什么,因为我从来没有真正实践过良好的软件设计。

我正在学习OpenGL,正在研究我的3D网格/模型类。这个问题是相关的,因为在我的渲染器中,索引和纹理对于网格是可选的。因此,网格可以只是顶点,索引顶点,顶点和纹理,或全部3.另外,我无法预见未来我可能想要添加哪些功能,因为我不知道我将学习什么一个月下来,所以我需要这个类灵活,可扩展

2 个答案:

答案 0 :(得分:1)

你是对的,你需要描述所有可能组合的任何选项(无论是通过switch / if还是通过类层次结构)都不好。

一种方法是使用decorator pattern来包装主类并添加动态属性。 或者您可以将单独的Stats类作为主类中的字段并使用其他项目进行装饰。

class Thing

    BasicStats stats

    constructor() 
        this->stats = new BasicStats()

    addItem(Item item)
        // Decorate current stats with new stats
        item->setComponent(this->stats)
        this->stats = item
        return this

    int getHealth()
        return this->stats->getHealth()

你可以像这样使用它:

 thing = new Thing() // has basic stats
 thing->addItem(new MagicMirror)->addItem(new SilverBullet)
 // will go through the chain of decorators to get the value
 health = thing->getHealth()

另一种方法是在主类中列出动态选项(或项目):

class Thing

    Stats stats
    ItemList items

    updateStats() 
        for item in this->items
            item->updateStats(this->stats)
            // OR if we want to not disclose the stats to items
            //    we can pass this and items should use 
            //    character's methods to change stats
            item->updateStats(this)

    add(Item item)
        this->items->append(item)
        return this

可以这样使用:

thing = new Thing()
thing->add(new MagicMirror())->add(new RingOfRegeneration)->updateStats()
如果您不想直接更改角色统计数据,

Memento也很有用。例如,如果您有一些“比较”功能,用户(或玩家)可以组合不同的项目集来查看影响,然后“应用”它们。

同时查看chain-of-responsibility,类似于统计列表选项 - 您可以创建一个“项目”链并从此链请求统计信息。就像你可以从传递基础健康值开始,然后由每个项目转换,你将在链的末尾获得“升级”值。

更新:还有一个想法,visitor也很有用:

# Base class for stats, Element
class Stat

   abstract accept(StatVisitor visitor)

class Health extends Stat

   private int health

   accept(StatVisitor visitor)
       # or you can have visitor->visitHealth(this)
       visitor->visit(this)

   multiply(int mult)
      this->health = this->health * mult

class Strength extends Stat     

   private int strength

   accept(StatVisitor visitor)
       visitor->visit(this)

   add(int strength)
      this->strength = this->strength + strength

统计数据是访客模式的“元素”。 Thing类代表“客户”:

# Thing contains stats, this is Client
class Thing

   StatsList stats

   accept(StatsVisitor visitor)
      for stat in this->stats
          stat->visit(visitor)

“访客”是我们的项目,可以修改统计数据:

# Base visitor class
class StatVisitor

   abstract visit(Health health)
   abstract visit(Strength strength)

class MagicMirror

   # magic mirror multiplies health by 10
   visit(Health health)
      health->multiply(10)

   # magic mirror increases strength +5
   visit(Strength strength)
      strength->add(5)

现在你可以这样做:

thing = new Thing()
item = MagicMirror()
# now update all the stats with MagicMirror
thing->accept(item)

答案 1 :(得分:0)

不要过分复杂化:

  1. 再生之戒:让你的角色随着时间的推移重生健康

    这根本不应该修改你的对象。例如。打开戒指可以添加一个试图增加实体健康状况的计时器(即佩戴者)并取消戒指再次移除该计时器。

  2. 偷偷摸摸的运动鞋:增加潜行

    这可以建模为统计数据的修改:

    class Entity
    
        Map<Skill, SortedList<Modifier>> skillModifiers
    
        getSneakChance()
            sneakChance = .. // compute base value from attributes
            for each mod in skillModifiers[SneakChance]
                sneakChance = mod.apply(sneakChance)
            return sneakChance
    

    地图避免了装饰责任链的两个缺点:并非每个修饰符都会为评估添加另一个间接,您可以使用更简单的基于值的修饰符(将值增加x%)而不是推动修改统计/技能的逻辑或如何将其访问到修改器中。

    一个难点在于决定应用修饰符的顺序:如果你首先在你的攻击伤害上加20,然后再增加5%或者反过来,则会失去1点伤害点?某些修饰符是否仅适用于基值,即您是否需要同时传递mod.apply中的累计值和基值?

  3. 魔镜:反映传入伤害的百分比

    与2.相同。此时间图修饰符和对特定事件的反应(即受到伤害)。这允许将相同的效果应用于多个实体,如光环或其他一些aoe法术。

  4. Doofy眼镜:突出制作资源

    这又是一个不会影响实体的东西。 gui-controller需要这种信息,因此只需向玩家查询视觉修饰符(除非游戏中的每个实体都需要操纵你的gui的行为和外观)。