赞 | 342 |
VIP | 10 |
好人卡 | 8 |
积分 | 262 |
经验 | 235776 |
最后登录 | 2024-9-23 |
在线时间 | 2387 小时 |
Lv5.捕梦者 (版主) 遠航の猫咪
- 梦石
- 3
- 星屑
- 23206
- 在线时间
- 2387 小时
- 注册时间
- 2005-10-15
- 帖子
- 1166
|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本帖最后由 SailCat 于 2010-11-27 15:29 编辑
第六章 因为出众,所以不同
第一节 游戏类的功能
在之前的讲解中,我们从事件脚本开始,逐步扩展游戏所能实现的功能,直到上一讲,我们终于深入了游戏内核脚本的世界,面对着上万行代码的脚本编辑器,迈出了我们自定义个性化游戏功能的第一步。
然而,仅仅对系统的默认方法做出零星的修改,说起来多少还是有点小家子气的感觉。想要自己做的游戏有吸引力,在系统上和别人的“大路货”有区别,光是用修改系统默认方法的做法,是不行的。你需要了解更多,了解各个游戏类的功能,了解你所关注的游戏类里面各项属性、方法的功能。当然最重要的是了解这一点——你所想要实现的系统功能,需要对哪些游戏类的什么属性和方法作出改动。
每个游戏类所负责的功能并不复杂,RGSS默认脚本的结构非常好,每个类的实现有极高的专一性。在系统帮助的“脚本入门”之“解读篇”中,对游戏默认脚本的功能做了简单的描述。但是这个描述恐怕太简单了,两三个字的说明可能会让你看得云里雾里。不过没关系,当你打开F11的脚本编辑器窗口之后,把每一段脚本都逐个打开,阅读头几行的注释,你就会对不同游戏类的功能有一个大概的了解。这里,我也简单说一下:
游戏实时对象类:Game_xxxx,共11个,它们的共同特点是,拥有一个全局的$game_xxxx(同名小写)的实例化变量(少数内部类、公共类没有)。这些游戏对象类,随着游戏的进程不断地发生变化,是推动游戏进行的核心所在。几乎所有的自定义系统都会修改它们。
游戏精灵类:Sprite_xxxx与Spriteset_xxxx,共5个,它们通常在场景中调用,拥有自己的显示、表现等方法,是游戏界面的组成部分之一。
游戏窗口类:Window_xxxx,共31个,它们也是在场景中调用,拥有自己的显示、表现等方法,是游戏界面的组成部分之二。
光标箭头类:Arrow_Base, Arrow_Enemy, Arror_Actor这3个,它们只在战斗中使用,一般的修改也动不到它们头上。
事件解释器类:Interpreter,只有1个,这个类总管对事件处理的解释功能——怎么说呢,属性就别动了,方法也不用增删了,小打小闹地改点解释方式倒是可以,不过也是高级技巧了。
游戏场景类:Scene_xxxx,共16个,共享一个全局实例变量$scene,几乎所有的场景表现和用户交互的处理都在这里,是游戏与用户互动的基础。大部分自定义系统会修改它们,不同插件脚本的冲突也一大半来自这里。
好了,介绍完了。最后那个Main不是一个类,是游戏的主处理进程,去掉注释只有12行代码,也从来没见有人改过这个东西……
第二节 属性的改变
和前面所说的小敲小打不同的是,对于属性和方法的修改,关系到游戏脚本的结构层面,因此,在介绍下一步的修改内容之前,有必要提一下RGSS脚本编辑器的一些功能:
Ctrl+X, Ctrl+C, Ctrl+V不解释,文抄公都是这么过来的。
Ctrl+F是在页面内搜索,Ctrl+Shift+F是全文搜索,当你增加或者删除一些脚本中的方法之后,要多用这个功能,全文通查通改。
Ctrl+G是行号定位,其他不表。
首先我们来看一下属性的增添,还是拿Game_Temp这个类来说吧(这个类是所有游戏类中最简单的一个)
如果要给Game_Temp类增加一个属性,只要依葫芦画瓢,在那一长串attr_accessor之后,再添加一个你自己的attr_accessor :标识符,就可以了,比如
attr_accessor :time_elapsed # 本次装载后的游戏执行时间
定义属性的标记有三种:attr_accessor, attr_reader, attr_writer
使用attr_accessor定义的属性,为可读可写的属性;
使用attr_reader定义的属性,为可读不可写的属性(只读属性);
使用attr_writer定义的属性,为可写不可读的属性(只写属性),罕用。
有人可能会问,比如我明明在Game_Battler里看到了attr_reader :hp的字样,为什么我在其他地方也看到了actor.hp = xxxx的用法呢?这里解释一下,出现这种情况,是因为在Game_Battler类中额外定义了一个hp=的赋值方法,这个在下面一节的方法里会详细再说。
如果你要删除原来的某个属性,只要把那行注释掉或删掉就可以了。当然,你需要用Ctrl+Shift+F,把游戏中所有出现这个属性的地方都处理一下,这就是为什么我说,对于现成的系统来讲,删除比增加难得多的原因。我们还是从常见的增加着手。
当你新增一个属性之后,第一节事情,是找到initialize这个方法,在里面加上对属性值的初始化处理,比如:
@time_elapsed = 0
这个做法不是必须的,但如果你不这么做,这个值的初始化就是nil(前面提到过),后面你要用它计算时,可能会有一些问题。
这里特别需要说明的一点是,除了Game_Temp这种不随存档而保存的属性可以随意增添以外,其他的Game_xxxx类,在增加属性之后,读取原先的存档会有一些问题,主要就是新增的属性全部变成了nil,然后运算时一堆错误。这是因为读档时是不会去执行各个类的initialize方法的。要解决这个问题,一种办法是从头开始玩,呵呵,另一种办法是,在你对脚本做出修改之前,先写一个公共存档事件,触发条件是某个开关自动执行,在游戏中用F9(或事件)打开这个开关,并用它存一个档:
◆ 呼叫存档画面
◆ 注释:新增属性的初始化处理
◆ 脚本:# 你的初始化属性代码
◆ 开关:[触发开关]=OFF
如此这类。然后你去修改属性,并且记得把属性的初始化加在事件脚本中。然后当你改完脚本,回到游戏中的时候,读取这个存档,在读取之后,程序在初始化各个对象后,会继续执行事件中的未完成内容,将这些属性初始化。这个例子,可以参见本章的范例工程,这个工程在Game_Party类中加了一个test属性用于测试。为什么要用公共事件呢?因为这个东西不会随存档保存,就这么简单。如果你定义的并不是属性,而只是成员变量,你也可以先增加这样一个属性,如此操作处理完你的存档之后,再把属性去掉。
言归正传,当你定义好一个属性之后,你就可以到游戏类的各个地方,用@标识符来操作这个属性了,也可以到游戏类之外的其他地方,用游戏对象(如$game_xxxx).标识符的方法,来操作这个属性的改变。
但是,多一半的情况下,一个为了系统新功能增加的属性,应该会有自己专属的一些方法。
第三节 定义方法
方法的定义很简单,只要在class......end这段代码之间,插入一个def......end的程序段,就定义了一个方法。
什么情况下我需要定义方法?
情况很多,常见的大概有这么一些:
一是当你定义了一个新的只读或只写属性,你几乎没有理由不定义一个对应的写(或读)方法。
二是当你的系统有一长段全新的处理时,你应当把它包装成一个方法,然后在系统默认流程中调用这个方法。
三是当你定义了一个新的游戏类,所有的方法你都得自己写(好像是废话)。
还是从属性开始吧,比如你定义了一个attr_reader :cp的只读属性,那么至少你应该义务性地定义这样一个方法
def cp=(value)
@cp=value
end
当然,如果这个方法只是这么简单的话,那你不如写成attr_accessor :cp算了。有兴趣的人,不妨仔细看看Game_Battler这个类中对于HP和SP的读写处理,非常典型。顺便体会一下,当使用只读属性,并定义了相应的写方法时,在本类中调用self.hp=xxx和@hp=xxx有什么区别——后一种是仅操作成员变量,前一种是调用方法,包含更多的逻辑处理。
第二种情况,在本章的实例中,会有说明,此处就不表了。而最后一种情况,算是相当高级的技巧了,在以后的章节中,会有详细的涉及。敬请期待。
第四节 让阿尔西斯更出众
又到了用实例说话的阶段。那我们就再拿可怜的阿尔西斯说事吧。这人是RPGXP默认的游戏主角,即然是主角,多少该有点主角的样子吧。下面我们不妨按照一个英雄的标准来重新打造一下阿尔西斯:
1. 圣炎:“十字斩”对不死系怪物能造成额外2倍的伤害。
2. 吸精:每杀死一个敌人可以回复50点SP。
3. 浴血:每累计杀死十个敌人可以在战斗结束时额外获得100点经验。
4. 后勤:在地图上每走行1步可以回复1点HP。
好了,然后我们来思考一下,实现这些功能需要对哪些类的属性和方法作出修改。
1. 战斗中即时生效的效果,考虑修改Game_Battler类;
2. 一种方法修改同1,但杀死敌人有普攻、技能、道具三种方法,也就是要修改三处,还有一种方法就是在Scene_Battle控制角色行动那里修改,因为角色行动了才能杀死敌人;
3. 注意系统默认不统计每个人的杀敌数目,因此需要新增属性。本条是在战斗结束时获得,参考上一章的“双倍金钱”,应该是修改Scene_Battle类;
4. 在地图上的效果,关系步数,应该在Game_Player类中修改,并且需要新增方法(本条当然也可以用公共事件实现,但是有诸多不便之处)。
然后我们就进入RGSS的世界,来实现这些效果:
1. 定位到Game_Battler 3这段,找到应用特技效果的方法skill_effect。在第157行“第二命中判定”之前,插入对阿尔西斯的特殊关照:
# 如果技能是十字斩,使用者是阿尔西斯,受术者是不死系
if skill.id==57 and user.is_a?(Game_Actor) and user.id==1 and self.element_rate(9)>100
self.damage += self.damage * 2 # 额外2倍的伤害
end
怎么样,很简单吧。其实这个系统并不复杂,然而,要写出这样的代码,你必须充分的了解每一个类的各项属性和它们的作用。例如,判定十字斩和阿尔西斯,都用到了id属性,因为这个属性是从游戏数据库中直接读取,一般不会改变,即使你在游戏中把阿尔西斯改名成了阿妮茜丝(呃,变性了么……)或者把他转职成了牧师,他的id依然是1。而判定敌人是什么系怪物,用element_rate这个方法再精准不过。
2. 在Scene_Battle 4这段,找到角色行动效果的方法update_phase4_step2,在行动完成之后(第173行之前),照例关注一下我们的阿尔西斯:
# 如果当前行动的人是阿尔西斯
if @active_battler.is_a?(Game_Actor) and @active_battler.id==1
# 循环所有目标敌人,每挂掉一个,加50SP
for t in @target_battlers
if t.hp==0
@active_battler.sp += 50
end
end
end
好像也没什么难的,恩。
3. 这里需要先定义杀敌数,如果只需要统计阿尔西斯一个人的,可以用变量,当然也可以用属性,属性可以用于多个角色,将来你哪天突发奇想要看看特萝西杀了多少人,也可以用程序记录一下。我们先在Game_Actor类中增加一个普通属性:
attr_accessor :kills #角色的杀敌数
然后在initialize方法中加上初始化:
@kills = 0
然后是累计杀敌数,在第2条中修改那一段的@active_battle.sp += 50后面加上:
@active_battler.kills += 1
最后是给奖励,轻车熟路了,Scene_Battle 2第173行之前:
# 阿尔西斯的经验奖励
if actor.id == 1 and actor.kills >= 10
actor.exp += 100
actor.kills -= 10
end
搞定。
4. 我们先找到在地图上行走增加步数的处理,这个是在Game_Player的57-72行。但是Game_Player中并不包含具体角色的HP,SP等数值,怎么办呢?大家知道,中毒后在地图上行走会掉血,这个是怎么处理的呢?看一下这一行代码就明白了:
$game_party.check_map_slip_damage
这就是在Game_Player的处理中,通过全局实例化对象的方式,调用了另一个类的方法,我们也可以如法炮制。
首先是在Game_Party类中给阿尔西斯开开小灶:
def move_hp_up
# 循环队伍中所有角色
for actor in @actors
# 是阿尔西斯的话,加1点HP
if actor.id == 1
actor.hp += 1
end
end
end
然后在Game_Player中调用这个方法,把增加步数的方法改成下面这个样子
def increase_steps
super
# 不是强制移动路线的场合
unless @move_route_forcing
# 增加步数
$game_party.increase_steps
# 步数是偶数的情况下
if $game_party.steps % 2 == 0
# 检查连续伤害
$game_party.check_map_slip_damage
end
# 给阿尔西斯移动回血
$game_party.move_hp_up
end
end
大功告成,现在你可以按F12测试这个游戏的实际效果了。
这时候,身为队伍中魔法师的西露达不高兴了。凭什么就你阿尔西斯有这么多天赋,你是男主你了不起啊,不行,我是女主,我也要天赋,队伍中的每一个人都要有天赋,比如,我在队中的时候遇敌率下降一半。好吧好吧,要实现这个,就得对游戏中各个类之间的关系有比较清楚的了解,我们也要探寻到面象对象编程的核心思想了。请看下一章:继承与重载。
(暂时无法上传附件,范例工程欠奉) |
评分
-
查看全部评分
|