查看: 84|回复: 0

[转载发布] 【教程】装备附带技能实现过程

[复制链接]
  • TA的每日心情
    开心
    8 小时前
  • 签到天数: 37 天

    连续签到: 3 天

    [LV.5]常住居民I

    2028

    主题

    32

    回帖

    7260

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    VIP
    0
    卡币
    5184
    OK点
    16
    积分
    7260
    发表于 同元一千年八月五日(秋) | 显示全部楼层 |阅读模式
    前言
    装备附带技能这一名词听起来并不陌生,顾名思义,让角色装备某特定的装备,该角色就能习得这个装备中的技能。卸下装备时,角色就遗忘这个技能。这对于一个RPG游戏来说,是一个很不错的自定义设定。但是很遗憾,我们的RM并没有“自带”这一功能的实现方法(至少RMXP是这样,不知道VX和VA是不是有),因此大家便采取各种方式来实现这个设定。我想,因为这个设定容易被大多数人想到,实现起来又不是很困难,又是一个很值得加入RPG游戏的一个小插件,现在6R的论坛上已经有各种各样的“装备附带技能脚本”,或者“装备附带技能的解决方法”,总会有一个合适的方法可以实现这个功能。
    那么既然如此,为什么还要提起这件事情呢?我们可以看到,虽然实现的效果基本相同,但是从实现过程来看,各种方法却能分得出优劣来。在这里,优劣是有评判规则的,我们说一个方法好还是不好,可以从以下几个方面来看:方法的适应性如何,对异常情况的处理如何,对特殊情况考虑是否周全,运行效率如何,使用起来是否方便,和其他的方法是否冲突(主要是脚本)等等。这就激发出我们“不满现状”的欲望,看到解决的办法有瑕疵,就要反思其解决过程,然后不断优化解决方案。这不但是方法的转变,更是编程思想的转变,反思此过程很有意义。
    装备附带技能的问题,Ryan在很久以前(大概是五年前?)就已经尝试着解决,到现在,一共进行了几次反思,共有三种不同的解决方案,而且个人感觉后面经过反思得到的新方案远远高于旧的方案。而且我感觉每个人解决这个问题的思路应该跟我的差不多。不说废话了,开始正文。

    方案一:事件法
    这是我最初想到的事件来处理装备附带技能问题的方法,对于当年年少无知的Ryan,不懂脚本,用事件也是被逼无奈。不管怎么说,勉强实现了这一过程。
    [思路]
    利用并行处理的事件,不断对角色当前的装备状态进行检查。如果发现有特定的装备就为其增加技能,否则移除技能。
    [实现过程]
    事件类型:公共事件
    事件开始条件:并行处理
    事件条件开关:开关[0001]为ON
    事件内容:
    条件分歧 [角色1] 为 [武器1] 装备中
           增减特技:[角色1], +[特技1]
        除此以外的场合:
           增减特技:[角色1], -[特技1]
    分歧结束

    如果需要多个就不断添加条件分歧即可。

    [反思]
    1.这种方法容易想到,而且对于不会脚本的人来说,已经足够了。
    2.由于事件只能存在于地图上,因此切换到别的场景的时候,事件不起作用。例如在装备场景更换装备后,不回到地图,立即打开特技场景查看特技,你会发现装备附带的技能并没有加上,只有你退出到地图然后再去特技场景查看特技,装备附带的特技才会显现出来。
    3.并行处理的事件在满足条件的情况下反复执行,这就意味着游戏地图会变卡,特别是地图里面并行比较多的时候。实际上,角色在地图上是没有办法更换装备的,就更不用谈监视装备变化了。角色实际上是在装备界面上更改装备,但是却到地图画面上才监视,增减特技。这样的处理方法可以说是莫名其妙。
    4.此方法无法处理复杂情况,即如果不同的装备附带同一个特技,这么判断肯定是要出麻烦的。

    总之,事件法处理装备附带技能问题的思路很简单,操作起来很容易也很好理解,但是我们可以看出事件法处理问题的种种弊端。因为说到底,事件就是把一些常用的命令拿出来,它们经过组合,虽然可以处理很多的问题,但是还有很多的问题在组合的范围之外。因此,我们不得不把注意力放到脚本上。

    方案二:装备同步学习技能法(脚本)
    在初步掌握脚本之后,就有拿脚本来替代事件来实现这个功能的想法。这是一个脚本方法,也是最容易想到的脚本方法。用它来代替事件,可以说是一大进步。
    [思路]
    既然更换装备的时候就已经决定了角色应该习得哪些技能,那么就不应该再拖到地图场景判断。应该在装备的同时,就完成技能的同步。这就是所谓的“装备同步学习技能”。具体的方法就是找到负责更换装备的方法(应该是Game_Actor里面的equip方法),在里面插入类似于学习技能的语句。穿上一个装备就习得其中的技能,卸下一个装备就遗忘其中的技能,这些内容都在equip方法里面添加。
    刚才搜了一下6R的帖子,晨露曾经问过这样的问题,当时的解决方案是在Scene_Equip上面改动,虽然装备过程是在Scene_Equip内完成,但是说到底,实现装备过程还是equip方法的功劳,那样改其实是有问题的,因为无法对初期装备的技能进行学习。因为初期装备的设置不在Scene_Equip中进行,因此也就没有办法习得装备中的技能了。
    解决了装备监视问题,我们还要解决数据设置问题。因为既然上升到了脚本的高度,那么就有必要将其规范化。虽然可以逐个设置,但是在这里并不推荐。于是我们想到了以下设置方式:
    1. EQUIP_SKILLS = {[0,1]=>[1,2,3],[1,1]=>[4],[1,2]=>[5,6]}复制代码
    复制代码
    在这里采用了Hash表进行数据设置,说白了就是这事Hash常量,将装备和技能对应起来。当然,这种东西只有自己才能看懂,因此给人用的时候必须加以详细说明。
    例如这里,EQUIP_SKILLS的主键是一个二元的数组[a,b],a表示装备的类型,0为武器,1为防具;b表示表示装备的ID。EQUIP_SKILLS中主键对应的值是技能ID的数组,这里考虑到一个装备可能对应多个技能。因此[0,1]=>[1,2,3]的意思就是1号武器附带ID为1-3的技能。
    [实现过程]
    根据以上的分析,可以修改equip的方法如下(在这里仅以武器为例):
    1. def equip(equip_type, id)    case equip_type    when 0  # 武器      if id == 0 or $game_party.weapon_number(id) > 0        $game_party.gain_weapon(@weapon_id, 1)        if id == 0 # 卸下武器          if EQUIP_SKILLS[[0,@weapon_id]] != nil            for i in EQUIP_SKILLS[[0,@weapon_id]]              forget_skill(i)            end          end        else # 装备武器          if EQUIP_SKILLS[[0,@weapon_id]] != nil            for i in EQUIP_SKILLS[[0,@weapon_id]]              learn_skill(i)            end          end        end        @weapon_id = id        $game_party.lose_weapon(id, 1)      end    end  end复制代码
    复制代码

    [反思]
    1.相对于事件法,这种方法把执行的过程放到了变更装备的方法里面。相比较而言,思路变得自然很多。因此,事件法处理的一些弊端在这个方法里面得到了解决。
    2.设置过程虽然比较简洁(事件法需要不断做判断,这将会带来大量的复制粘贴,而这恰恰是难以维护的地方),但是却很抽象,在某些条件下也是很恼人的。试想在设置的时候,要不断在数据库——脚本编辑器中切换,换成谁都会略微不爽的。
    3.此方法也只能处理简单的情形,即多个装备不能共享相同的技能,也不能处理角色和装备共享技能的情况(即装备中附带的技能该角色已经通过其他方式(如升级)等学习到,按理说这是不能被覆盖的)。虽然通过处理,可以处理多装备共享技能的问题,例如换装备时,可以先脱下所有装备,然后再穿上新的装备,但是这样的处理十分不自然。

    总之,这个方法对于事件法来说,已经有很多改进的地方,但是仍然是比较初等的解决方案,维护起来不容易,而且遗留了很多问题。这就促使我们想出下面的方法。

    方案三:改进的脚本法
    抱歉实在不知道应该给这个方法起什么名字,就凑合听吧。其实这个方法是对脚本法的改进。说是改进,实际上却是抛弃了原有思路,从另一个全新的角度解决问题。解决了目前发现的所有问题。
    另外,这个方案下方的脚本是成熟的,可以直接复制到Main前来实现功能。
    [思路]
    我们在描述一个角色时,不关心他“有什么特技”,而是关心他“学没学会某个特技”。前者是从整体来说的,后者是从局部来说的。上一个脚本的处理方法着重于整体,那么这个脚本的处理方法更有局部处理的特色。我们并不需要让角色在更换装备的时候学习一个特技,而是需要能正确判断角色是否学会了某个特技。方案二脚本的重大缺陷在于,它没有有效地区分“自己领悟的特技”和“装备中习得的特技”,因此导致没有办法处理特技共享的问题。我们知道,多一个实变量就多一分维护的难度,因此我们应该把注意力放到方法的处理过程中去。这样说起来有些别扭,实际上就是重写skill_learn?方法和learn_skill方法。
    另外,我们还有必要说一下数据库设置问题。方案二用了一个Hash表来设置,但是这样做对于数据编辑很不方便,首先是对应关系不直观,然后就是设置起来非常麻烦。因此我们有必要采取另外的设置方式:说明(名称)扩充法。其实这种方法已经很普及,例如在黑暗圣剑传说中,采取“特技名,数值”的方法设置特技的最大伤害;装备颜色脚本中,采取“说明,@颜色编号”来设置装备的颜色。而去更改RPG模块相应类的name和description方法,采用split来处理字符串。这个懂的人应该知道。但是split你也用,他也用,难免会造成冲突。在这里我们采取另外一种方式:scan替换法。个人感觉这个比split的兼容性要好一些。具体的实现过程在代码中可以体现。
    [实现过程]
    1. #============================================================================# 装备附带技能#   By:RyanBern#----------------------------------------------------------------------------# 设置方法:#   在数据库中进行设置,在装备的说明中输入%w特技ID,即可完成一个特技的关联#   位置可以任意,但是最好在结尾。多个特技关联直接连写就行。# 例:将1号武器(铜剑)关联1号技能和2号技能#   在说明中更改:青铜制造的剑。战士可以装备。%w1%w2#============================================================================module RPG  class Weapon    alias ryan_des description    def description      return ryan_des.gsub(/%w\d+/,"")    end    def skills      if @skills.nil?        @skills = []        @description.scan(/%w([0-9]+)/){|s| @skills.push(s[0].to_i)}      end      return @skills    end  end  class Armor    alias ryan_des description    def description      return ryan_des.gsub(/%w\d+/,"")    end    def skills      if @skills.nil?        @skills = []        @description.scan(/%w([0-9]+)/){|s| @skills.push(s[0].to_i)}      end      return @skills    end  endendclass Game_Actor  # 重写 learn_skill  def learn_skill(skill_id)    if skill_id > 0 and not @skills.include?(skill_id)      @skills.push(skill_id)    end  end  # 重写 skill_learn?  def skill_learn?(skill_id)    result = @skills.include?(skill_id)    if @weapon_id != 0      result ||= $data_weapons[@weapon_id].skills.include?(skill_id)    end    if @armor1_id != 0      result ||= $data_armors[@armor1_id].skills.include?(skill_id)    end    if @armor2_id != 0      result ||= $data_armors[@armor2_id].skills.include?(skill_id)    end    if @armor3_id != 0      result ||= $data_armors[@armor3_id].skills.include?(skill_id)    end    if @armor4_id != 0      result ||= $data_armors[@armor4_id].skills.include?(skill_id)    end    return result  endendclass Window_Skill  # 重写 refresh  def refresh    if self.contents != nil      self.contents.dispose      self.contents = nil    end    @data = []    for i in 1...$data_skills.size      skill = $data_skills[i]      if @actor.skill_learn?(i)        @data.push(skill)      end    end    # 如果项目数不是 0 就生成位图、重新描绘全部项目    @item_max = @data.size    if @item_max > 0      self.contents = Bitmap.new(width - 32, row_max * 32)      for i in 0...@item_max        draw_item(i)      end    end  endend复制代码
    复制代码

    [反思]
    1.解决了装备之间享特技的问题,即如果装备1和装备2同时附带技能1,那么装备任意一个角色就可习得此技能,脱下所有才能忘记技能。
    2.解决了角色和装备特技的共享问题,即如果角色已经学会了技能1,装备1也附带技能1,那么角色不穿这个装备时,不会遗忘特技。
    3.采用正则表达式+scan处理方法,能够降低冲突发生几率。

    总之,装备附带技能的脚本又发展了一步。

    另外看了2L的帖子,感觉2L给出的链接是目前我见过的最好的装备附带技能的脚本之一,在这儿顺便帮着做个广告吧。
                 本帖来自P1论坛作者英顺的马甲,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:https://rpg.blue/forum.php?mod=viewthread&tid=360130  若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。
    天天去同能,天天有童年!
    回复 论坛版权

    使用道具 举报

    ahome_bigavatar:guest
    ahome_bigavatar:welcomelogin
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|同能RPG制作大师 ( 沪ICP备12027754号-3 )

    GMT+8, 2024-5-10 18:14 , Processed in 0.064689 second(s), 43 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.

    快速回复 返回顶部 返回列表