`
923723914
  • 浏览: 636521 次
文章分类
社区版块
存档分类
最新评论

装饰模式:英雄装配装备

 
阅读更多

在角色扮演类游戏,常常涉及到某个英雄装配各种装备,例如大掌门,我叫MT这样的游戏中,角色都可以装配一些装备,如,武器,护甲等。

当角色装配各种装备后,会使角色的某些属性增加,例如,金蛇郎君装备金蛇剑后,他的攻击力可以增加500,再给他搞个软猬甲穿上,他的防御力直接增加400。如果机缘巧合,他学会如来神掌,那他的攻击力直接飙升到1000.当角色装配某种“道具”后,他的属性都会相应的发生改变。

在游戏中,一般情况下,角色可以随意搭配装备。还是那金蛇郎君举例,给他 装配了 金蛇剑, 软猬甲,如来神掌,他在数据库存储的记录可能类似于这样:

name: 金蛇郎君

weapon_id: 003 (对应金蛇剑)

armor_id: 004 (对应软猬甲)

kongfu_id: 005 (对应如来神掌)


我们计算金蛇郎君的整体实力的时候 代码可能是这样:

function getUserPower($user)
    {
        // 配置 武器
        if ($user['weapon_id']) {
            if ($weapon = $this->_user->weapon->getOneWeapon($user['captain_id'])) {
                // 金蛇郎君的战斗力增加
                $user['attack'] += $captain['attack'];

                // 金蛇郎君的防御力增加
                $user['defend'] += $weapon['defend'];
            } else {
                // 不存在则强行卸载
                $this->unloadItem($user, 'weapon');
                unset($weapon['weapon']);
            }
        }

        // 配置 护甲
        if ($user['armor_id']) {
            if ($armor = $this->_user->armor->getOneArmor($user['armor_id'])) {
              // 金蛇郎君的战斗力增加
                $user['attack'] += $captain['attack'];

                // 金蛇郎君的防御力增加
                $user['defend'] += $weapon['defend'];
            } else {
                // 不存在则强行卸载
                $this->unloadItem($user, 'armor_id');
                unset($user['armor_id']);
            }
        }

样的代码,似乎可以完美的搞定这个需求,但是,某一天,坑爹的产品再次放出话来:夏雪宜装配金蛇剑后,金蛇剑不仅可以“增加”夏雪宜的攻击力,还能“增加”他的防御力,除此之外,装备对角色属性影响的加成算法发生了变化。


遇到这样的产品,我们仰天长叹之后,还得充满辛酸的修改角色类中getUserPower()方法。改就改吧,这又有什么关系呢,问题就在这里:由于金蛇剑这个装备发生了变化,导致我不得不去改角色类里面的方法!!如果哪一天,我增加了一个装备类型,或者某个装备的加成方式发生了变动,那我的角色类中的getUserPower()函数要改的面目全非。

这样,角色模块和装备模块耦合太高,装备的改动易导致角色模块出现错误的风险增加。有没有一种好的设计模式能解决这个问题,当装备发生变动的时候,我只需要改装备类里面的方法,而尽量少改动角色类里面的getUserPower()方法。

这个时候,我们可以使用装饰模式 。装饰模式动态的把责任附加到对象上,若要扩展功能,装饰者比继承提供更有弹性的替代方案。在装饰对象的时候,装饰模式要求被装饰者和装饰者需要继承同一个超类。

这是装饰者模式的类图:


这里,我们需要把焦点定格在装饰这个词汇上,所谓装饰,就是那一样东西就装饰另一样东西,在不改变另一样东西的前提下,修改它的某些特征,或者给他添加的新得职能。我们这里,就可以参考以上的类图形式,使用 金蛇剑类,软猬甲类,武功类分别装饰金蛇郎君。

类关系图如下:

我们就是用装备类来装饰角色类,此游戏的装饰模式其实和定义的装饰模式有点区别,装饰模式要求被装饰者和装饰者是继承同一个类,而我们的装备类与角色类并不是继承同一个父类。类图如下:

每个装备类,都必须实现一个equip(Model_User $user)方法,这个方法的参数是一个角色对象:

/**
 * 装备-武器模型
 *
 * $Id: weapon.php 1953 2013-04-10 06:33:38Z jiangjian $
 */

class Model_Item_Weapon extends Model_Item_Abstract
{
    /**
     * 装配到角色上
     *
     * @param Model_User $user
     * @return void
     */
    public function equip(Model_User $user)
    {
        // 装备属性赋值
        $user['weapon'] = $this;

        // 增加 攻击力
        $user['offense']   += $this->_prop['offense'];

        // 增加 射速
        $user['fire_rate'] += $this->_prop['fire_rate'];

        // 增加 命中率
        $user['hit_odds']  += $this->_prop['hit_odds'] ;
    }
}


在角色类中,我们需要一个方法:

class Model_User
{
    /**
     * 装饰、配置我的角色(即计算装备属性增益)
     *
     * @param Model_User $user
     * @param array $userInfo 我的配置
     * @return void
     */
    private function _decorateUser(Model_User $user, &$userInfo)
    {
        // 配置 武器
        if ($userInfo['weapon_id']) {
            if ($weapon = $this->_user->Weapon->getOneWeapon($userInfo['weapon_id'])) {

                // 实现对角色的装饰
                $weapon->equipUser($user);
            } else {
                // 不存在则强行卸载
                $this->unloadItem($userInfo, 'weapon');
                unset($userInfo['weapon_id']);
            } 
        }

        // 配置 护甲
        if ($userInfo['armor_id']) {

        }

        // 配置 功夫
        if ($userInfo['kongfu_id']) {

        }
  
    }

这样做的好处是,到时候如果需要修改装备时,我们只需要修改装备类中的方法。


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics