赞 | 189 |
VIP | 627 |
好人卡 | 188 |
积分 | 95 |
经验 | 171230 |
最后登录 | 2024-7-3 |
在线时间 | 5073 小时 |
Lv4.逐梦者 (版主)
- 梦石
- 0
- 星屑
- 9532
- 在线时间
- 5073 小时
- 注册时间
- 2013-6-21
- 帖子
- 3580
|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本帖最后由 RyanBern 于 2014-12-12 17:33 编辑
前言
装备附带技能这一名词听起来并不陌生,顾名思义,让角色装备某特定的装备,该角色就能习得这个装备中的技能。卸下装备时,角色就遗忘这个技能。这对于一个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中进行,因此也就没有办法习得装备中的技能了。
解决了装备监视问题,我们还要解决数据设置问题。因为既然上升到了脚本的高度,那么就有必要将其规范化。虽然可以逐个设置,但是在这里并不推荐。于是我们想到了以下设置方式:
- 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的方法如下(在这里仅以武器为例):
- 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的兼容性要好一些。具体的实现过程在代码中可以体现。
[实现过程]
- #============================================================================
- # 装备附带技能
- # 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
- end
- end
- class 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
- end
- end
- class 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
- end
- end
复制代码
[反思]
1.解决了装备之间享特技的问题,即如果装备1和装备2同时附带技能1,那么装备任意一个角色就可习得此技能,脱下所有才能忘记技能。
2.解决了角色和装备特技的共享问题,即如果角色已经学会了技能1,装备1也附带技能1,那么角色不穿这个装备时,不会遗忘特技。
3.采用正则表达式+scan处理方法,能够降低冲突发生几率。
总之,装备附带技能的脚本又发展了一步。
另外看了2L的帖子,感觉2L给出的链接是目前我见过的最好的装备附带技能的脚本之一,在这儿顺便帮着做个广告吧。 |
|