设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 14029|回复: 68
打印 上一主题 下一主题

[原创发布] 【教程】【不定期更新】实用型XP脚本编写小技巧#13

[复制链接]

Lv4.逐梦者 (版主)

梦石
0
星屑
9497
在线时间
5073 小时
注册时间
2013-6-21
帖子
3580

开拓者贵宾剧作品鉴家

跳转到指定楼层
1
发表于 2015-1-17 12:55:15 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
本帖最后由 RyanBern 于 2017-1-5 20:45 编辑

这个帖子记录了我在编写RMXP脚本时候学会的若干技巧,在编写脚本的时候我会把一些有价值的想法记录在这个帖子中,此帖会不定期更新。
此帖涉及到的技巧多实用性,没有特别高深的技术,应该很容易就能学会。不过,具体问题具体分析,这个教程里面的脚本一般不能直接拿过来实用,也只是提供了一个处理该类问题的思路,所以请不要问“这脚本怎么不能实现XX效果”这样的问题。
另外请大家多多提出批评意见。


技巧1:数据库内容扩展
【更新】2015.01.17
【摘要】默认的数据库提供的功能大概不能满足一些人的需要,如果你想要给一个特技定义“冷却时间”的属性,但是数据库并没有相应的设置区域,这时候该如何处理?是打开脚本编辑器直接设置固定值,还是使用外部文件存储这一设定?原则上说,这个本来是数据库的设定,只是数据库的功能不够而已,因此我们希望还可以在数据库中设置自己的自定义属性,而不是把数据编辑转移到脚本编辑器中。以下这个方法经过测试,稳定性较好,并且能满足多数需求。
【正文】



技巧2:额外信息的存储
【更新】2015.01.17
【摘要】在编写一些XP的脚本系统中,不可避免要涉及到数据的存储问题,特别是用户存档数据的存储。这类问题如果处理不好,那么写出来的脚本的兼容性会很差,而且修改也十分麻烦。这个方法利用默认脚本的一些结构,可以被用作比较简单的额外信息存储,并且实现起来也不是非常麻烦。
【正文】



技巧3:让Sprite和Window动起来
【更新】2015.01.17
【摘要】在各种场景中,我们希望我们界面中的元素运动起来,而不是静止地呆在原地。例如在菜单场景中,我们希望命令窗口从屏幕外飞入到场景中,要一个动态的效果。我们自然会想到可以通过更改坐标让它动起来。再比如我们需要一个图片淡出画面,而不是一下子从画面上消失,我们就会想通过更改不透明度来实现这个效果。原理是很简单的,但是怎么样处理才比较妥当呢?下面这个技巧通过定义了一些额外的接口,能够让Sprite和Window随心所欲地移动。
【正文】



技巧4:装备附带技能——简洁到了极致
【更新】2015.01.23
【摘要】装备附带技能是一个很久远的问题,要解决它也不是很困难。但是,实现装备附带技能的方法却非常重要。这个技巧实际上没有什么新鲜的东西,在上文中也不断提及。其核心就是“永远不要更改原始数据”。注意到了这一点,我们就能用很短的篇幅来完成装备附带技能的脚本。
【正文】



技巧5:事件和脚本结合——给地图事件做个计时器
【更新】2015.01.24
【摘要】相信一定有人想利用RMXP做一个类似于农场的游戏,希望达到的最基本效果是让地图上的某些事件随着游戏时间的变化而变化。例如,玩家在某一时刻种下一颗种子,然后游戏时间经过15分钟后,终于长出了果实,这就是一个简易的农场事件。由于这个效果涉及到了地图上的事件,因此在实现的过程中,必须要考虑地图事件的刷新机制。如果完全通过事件实现,制作起来比较麻烦,而且会带来一些效率上的问题。下面这个技巧,将事件和脚本配合使用,利用脚本制作核心的计时器功能,利用事件完成地图上的刷新,比较有参考价值。
【正文】



技巧6:二周目制作
【更新】2015.02.18
【摘要】二周目的设计在RPG游戏中是个很不错的设定,它增加了游戏的可玩性,适度延长游戏的时间。但是,RMXP默认的系统中不具有二周目/多周目的设定,因此我们要想办法在玩家一周目通关之后,选择“新游戏”进入游戏,就会进入二周目的剧情。一般来说,二周目的信息不会保存在存档中,因此需要借助其他文件来存储这个信息。这个技巧利用游戏原有数据库文件,巧妙地将二周目信息隐藏在其中。为我们制作游戏提供了新的思路。
【正文】



技巧7:多项选择
【更新】2015.03.19
【摘要】大家都知道,RMXP的事件指令的第二条就是“显示选择项”,当主角和NPC对话之后,NPC会让主角做个选择。但是这个事件指令的不好的地方就是它只能设置四个选择项,当选择多于这个数目时,就要想各种办法了。例如,通过反复使用“显示选择项”事件指令,牺牲2个选项槽,把这个选项改为“上一页”和“下一页”,然后再考虑多个选项之间的衔接问题。这样做不是很方便,也使得选项过于臃肿。如果单纯利用脚本,那么如何设置选择之后的分歧,也是一个问题。这个技巧运用了脚本事件相结合的办法,能够制作出使用起来比较方便的多项选择系统。
【正文】



技巧8:椭圆形状的blt
【更新】2015.06.28
【摘要】众所周知,Bitmap类的#blt方法有类似于截图传送的功能,它可以把一张bitmap的部分复制到另一个bitmap当中。这个方法很方便,但是它也有很多的拓展空间。比如说,blt方法只能截取矩形区域,而不能截取其他形状的区域。如果我们对区域有所要求的话,原始的blt就不能满足了。这个技巧为Bitmap类新定义了一个#ellipse_blt方法,它可以将传送源位图的一个椭圆区域截取到目标bitmap上。
【正文】



技巧9:地图事件数据的扩展
【更新】2015.06.29
【摘要】技巧9实际是对技巧1的进一步补充,在技巧1中,我们对数据库的功能进行了扩展。但是地图上的事件某种意义上讲也是游戏数据的一部分,那么如何在地图事件上附加额外信息呢?下面就要介绍两种附加信息的方法:利用事件的名字和事件当中的注释。
【正文】



技巧10:记录事件位置
【更新】2015.07.01
【摘要】RGSS1中,切换地图之后,重新进入该地图会导致事件的位置重置。究其原因,是因为游戏并没有储存各个地图的信息,而是在每次进入地图的时候,重新载入原始的地图数据,这导致事件归位。所以,我们必须要在主角移动之前将地图上的事件位置储存起来,这需要利用我们技巧2。当然,并不是所有事件都需要记录位置,我们利用技巧9来对特殊的事件进行设置,让RGSS系统知道哪些事件需要记住位置,哪些事件不需要。
【正文】

技巧11:另类的alias实现
【更新】2015.09.15
【摘要】在自己编写脚本中使用alias关键字,能够节省插件脚本的篇幅,还有减少脚本冲突的可能性的效果。在这个技巧里面,我则要介绍另一种alias的实现方式,利用这个方法,起到的效果和alias差不多,但是完全不用使用alias关键字,而且也不必想方设法给新方法起不同的名字了(从而达到防止Stack Level Too Deep的错误)。 注:本技巧参考了这里的帖子。
【正文】

技巧12:简单的数据库破限
【更新】2016.07.14
【摘要】众所周知 RMXP 对数据库元素的数量基本上都有 999 的上限,那么总有这个数量不够的情况。遇到这种情况我们该怎么办呢?你可能想到 DKRM,但是 DKRM 的资源不是那么好找,你也可能想到用脚本将多余的数据补上去,但是总觉得写起来很麻烦。这里介绍了一种在不改动编辑器的条件下轻松突破这个限制的一种办法,可能以前有人已经做过,不过我还是在这里更新一下吧。
【正文】


技巧13:简易召唤系统——我们当中出现了一个叛徒
【更新】2017.01.05
【摘要】这个技巧巧妙利用了敌人出现/隐身的设定,制作了一个简易的召唤系统。并且需要极少量的脚本代码,非常适合事件党学习(以及出成考场题目)。
【正文】

点评

接收更新通知。  发表于 2015-9-15 08:03
接收更新提示  发表于 2015-7-18 11:57
显示选项的一个不自然的思路可以见我的事件转译器-_-#  发表于 2015-3-19 12:50
更新时求自顶啊= = 点评接收更新通知  发表于 2015-2-8 21:10

评分

参与人数 7星屑 +525 +1 收起 理由
SixRC + 1 看完了 r7漏洞+1
gonglinyuan + 20 精品文章
余烬之中 + 200
7408 + 15 精品文章
你最珍贵 + 60
kuerlulu + 30 精品文章
taroxd + 200 永远不要更改原始数据+1

查看全部评分

Lv4.逐梦者

梦石
0
星屑
9275
在线时间
2504 小时
注册时间
2011-5-20
帖子
15389

开拓者

2
发表于 2015-1-17 13:04:35 | 只看该作者
其实用
  1. Module ML end
  2. Module ML::Skillcd
  3. Skill={
  4. 1=>2,#1号技能2回合CD
  5. }
  6. end
复制代码
  1. Module ML end
  2. Module ML::Color
  3. Skill={
  4. 10=>4,#10号技能4号颜色
  5. }
  6. end
复制代码
什么的读取的时候用ML::Color::Skill[X]就好了吧?

点评

比如我带1级红杖和5级红杖都是显示带来800伤害能量冲击的介绍0.0  发表于 2015-1-17 13:15
话说要是装备介绍会改变的呢···比如说达贡之神力附带能量冲击,一个角色身上是6格子装备可以带6红杖···红杖的介绍按照身上带的那根最高级的统一   发表于 2015-1-17 13:14
主要是觉得写介绍和名称的话还是不够0.0  发表于 2015-1-17 13:13
我在一开始就说了尽量不要再脚本编辑器里设置  发表于 2015-1-17 13:11
好歹认真看一下摘要吧……  发表于 2015-1-17 13:10
[img]http://service.t.sina.com.cn/widget/qmd/5339802982/c02e16bd/7.png
回复 支持 反对

使用道具 举报

Lv3.寻梦者 (版主)

…あたしは天使なんかじゃないわ

梦石
0
星屑
2207
在线时间
4033 小时
注册时间
2010-10-4
帖子
10779

开拓者贵宾

3
发表于 2015-1-17 13:10:02 | 只看该作者
本帖最后由 taroxd 于 2015-1-17 15:03 编辑

<del>以下内容均为卖萌扯淡</del>

没记错的话 XP 修改内部脚本的话是需要 F12 guard 的吧

所以不管怎么说,我现在连 alias 关键字都完全不用了

RUBY 代码复制
  1. class Module
  2.  
  3.   new_name = :alias_method_without_F12_guard
  4.   old_name = :alias_method
  5.  
  6.   unless method_defined? new_name
  7.     alias_method new_name, old_name
  8.   end
  9.  
  10.   def alias_method(new_name, old_name)
  11.     unless method_defined? new_name
  12.       alias_method_without_F12_guard new_name, old_name
  13.     end
  14.   end
  15. end


---

看到 merge_party 好亲切~ 咳咳

https://rpg.blue/thread-367044-1-1.html

你的代码里也是考虑到了存档里不存在的情况(@reserved_parties ||= {}),不过这个东西实在是不需要每次都写啊- -
甚至根本不需要重定义 initialize。可以直接定义一个(可以私有)的方法 reserved_parties。

class Game_System
  private
  def reserved_parties
    @reserved_parties ||= {}
  end
end

这样之后每次访问都调用 reserved_parties 方法就可以了

点评

如果这样的话initialize确实是多余的。不过||=在一个方法里面执行一次就够了,定义成私有方法的话每次引用都要执行一次(虽然效率影响不大)  发表于 2015-1-17 15:08
其实我是通过AC的回帖找到你这个帖子的,当时想都没想果断用这个当方法名  发表于 2015-1-17 14:25
后面的代码也没必要判断。Game_Party 也不是内部类啦  发表于 2015-1-17 14:18
method_defined? 是接受*符号*参数的方法,不是 alias 那样的关键字啦……(因为是方法所以才更灵活)  发表于 2015-1-17 14:16
第一个已经修改,不过后面的代码为了省事就不再判断了,留给写脚本的人自己控制好了  发表于 2015-1-17 14:14

评分

参与人数 1星屑 +60 收起 理由
RyanBern + 60 来吃糖

查看全部评分

回复 支持 反对

使用道具 举报

Lv2.观梦者

故九江太守

梦石
0
星屑
384
在线时间
2098 小时
注册时间
2012-12-5
帖子
4420
4
发表于 2015-1-18 19:45:06 | 只看该作者
希望有个怎么把脚本写得简便短小精悍的教程

点评

元编程  发表于 2015-1-18 21:40
翻 Ruby 文档。没有更好的办法  发表于 2015-1-18 19:54
回复 支持 反对

使用道具 举报

Lv3.寻梦者 (版主)

…あたしは天使なんかじゃないわ

梦石
0
星屑
2207
在线时间
4033 小时
注册时间
2010-10-4
帖子
10779

开拓者贵宾

5
发表于 2015-1-18 19:50:04 | 只看该作者
本帖最后由 taroxd 于 2015-1-18 19:59 编辑

关于窗口精灵的运动,@余烬之中 写过一个更加通用的:
https://github.com/ShadowMomo/Sm ... ter/Smomo%20Core.rb 中的 transition 方法

我也写过一个不那么通用的(没有上面那个通用,需要手工 update):
https://rpg.blue/thread-365971-1-1.html 中的 Taroxd::Transition 类。

总之,这个变化的模式不应该局限于 X 坐标和 Y 坐标。

---
另外,我记得坐标是不需要变成整数的。就算赋值为整数似乎也会在内部转成浮点数的。

点评

实践是jQuery的easing和其他类似的,既然你提到了推广到通用所以我就更推广一下  发表于 2015-1-19 17:52
嗯,有想过,但是毕竟没怎么用到,所以没有付诸实践  发表于 2015-1-19 17:17
更通用的是移动的tween方式写成公式,和update分离  发表于 2015-1-19 01:10
回复 支持 反对

使用道具 举报

Lv1.梦旅人

路人党员

梦石
0
星屑
51
在线时间
2276 小时
注册时间
2010-12-30
帖子
3225
6
发表于 2015-1-19 10:18:07 | 只看该作者
本帖最后由 英顺的马甲 于 2015-1-19 10:51 编辑

很多时候与其重定义method不如重定义变量

额...好吧,就放个例子
https://rpg.blue/forum.php?mod=r ... 990&pid=2575524
与其
RUBY 代码复制
  1. class Game_Party
  2.   def item_number(id)
  3.     @items[@actors[0].id] ? (@items[@actors[0].id].include?(id) ? @items[@actors[0].id][id] : 0) : 0
  4.   end
  5.   def weapon_number(id)
  6.     @weapons[@actors[0].id] ? (@weapons[@actors[0].id].include?(id) ? @weapons[@actors[0].id][id] : 0) : 0
  7.   end
  8.   def armor_number(id)
  9.     @armors[@actors[0].id] ? (@armors[@actors[0].id].include?(id) ? @armors[@actors[0].id][id] : 0) : 0
  10.   end
  11.   def gain_item(id, n)
  12.     @items[@actors[0].id] = {} unless @items[@actors[0].id]
  13.     if item_id > 0
  14.       @items[@actors[0].id][id] = [[item_number(id) + n, 0].max, 99].min
  15.     end
  16.   end
  17.   def gain_weapon(id, n)
  18.     @weapons[@actors[0].id] = {} unless @weapons[@actors[0].id]
  19.     if id > 0
  20.       @weapons[@actors[0].id][id] = [[weapon_number(id) + n, 0].max, 99].min
  21.     end
  22.   end
  23.   def gain_armor(id, n)
  24.     @armors[@actors[0].id] = {} unless @armors[@actors[0].id]
  25.     if id > 0
  26.       @armors[@actors[0].id][id] = [[armor_number(id) + n, 0].max, 99].min
  27.     end
  28.   end
  29. end

不如
RUBY 代码复制
  1. class Bag
  2.   def initialize
  3.     @data = {}
  4.   end
  5.   def method_missing(name, *args, &block)
  6.     @data[$game_party.actors[0].id] ||= {}
  7.     begin
  8.       @data[$game_party.actors[0].id].send(name, *args, &block)
  9.     rescue Exception
  10.       raise($!.class, $!.message, caller)
  11.     end
  12.   end
  13. end
  14. class Game_Party
  15.   alias multi_bag_init initialize unless defined?(multi_bag_init)
  16.   def initialize
  17.     multi_bag_init
  18.     @items = Bag.new
  19.     @weapons = Bag.new
  20.     @armor = Bag.new
  21.   end
  22. end

点评

就三个方法就上 method_missing 大法啊= = 话说 @armors 少写了一个 s。另外 Game_Actor 没有定义 hash 方法,所以可以直接拿来做哈希表的键(少打几个字-_-)  发表于 2015-2-8 21:15
第二种方法还能把冲突降到最低  发表于 2015-1-19 20:32
第一种方法实际上更改了原有背包信息的储存方式,所以它改了方法和变量结构。第二种方法是直接引入新的结构,自然要比第一种要好。  发表于 2015-1-19 20:00
其实我想要说的是多背包脚本...  发表于 2015-1-19 10:38
其实就是你的那个“装备附带技能”脚本让我坚信了改脚本要改method的想法。话说我还想把你那篇帖子引用到这里呢  发表于 2015-1-19 10:20

评分

参与人数 1星屑 +60 收起 理由
RyanBern + 60 吃糖了

查看全部评分

本人擅长XP,如果有脚本或者Ruby方面的问题欢迎发电邮到[email protected]咨询,本人很少检查电邮所以不一定会及时回复,本人不会直接出手解决问题只会提供一个方向,所以谢绝伸手党
回复 支持 反对

使用道具 举报

Lv3.寻梦者 (版主)

…あたしは天使なんかじゃないわ

梦石
0
星屑
2207
在线时间
4033 小时
注册时间
2010-10-4
帖子
10779

开拓者贵宾

7
发表于 2015-2-8 21:45:13 | 只看该作者
本帖最后由 taroxd 于 2015-2-9 07:46 编辑

第四个,其实 VA 的 skill_learn? 并没有考虑附加的技能。
这里有我稍稍做的改动(让怪物也有技能):http://taroxd.github.io/rgss/Tar ... AE%BE%E7%BD%AE.html

第五个,我会更倾向于这么做(删去了并行事件,效率更高,事件页更简洁美观):
RUBY 代码复制
  1. # 事件脚本的调用方式:this_event.set_timer(3600, 'B')
  2. # this_event 容易实现,因此不再赘述
  3. # 其实我本来想用这种更优雅的方式:
  4. #     this_event.set_timer(3600) do
  5. #       self_switch.b = true
  6. #     end
  7. # 但考虑到 block 不便存档,于是还是算了。
  8.  
  9. class Game_Event
  10.  
  11.   # *args: 帧数, 独立开关对应的字母
  12.   def set_timer(*args)
  13.     self_switch['timer'] = args # self_switch 对象很容易实现,见 [url]http://taroxd.github.io/rgss/%E4%BA%8B%E4%BB%B6%E8%84%9A%E6%9C%AC%E6%89%A9%E5%B1%95.html[/url]
  14.   end
  15.  
  16.   alias_method :timer_update, :update
  17.   def update
  18.     timer_update
  19.     args = self_switch['timer']
  20.     return unless args
  21.     if args[0] > 0
  22.       args[0] -= 1
  23.     else
  24.       self_switch[args[1]] = true
  25.       self_switch['timer'] = nil
  26.     end
  27.   end
  28. end

点评

即使 Game_SelfSwitches 没有自带刷新,这个刷新也可以包装在 SelfSwitch 对象里面。总之这种细节不是这个脚本的事情  发表于 2015-2-9 09:25
VA 里的 Game_SelfSwitches#[] 自带刷新  发表于 2015-2-9 09:23
去掉并行事件是个关键的想法,放在脚本里面刷新不错。貌似XP还要在独立开关打开后加一句$game_map.need_refresh = true  发表于 2015-2-9 09:09

评分

参与人数 1星屑 +60 收起 理由
RyanBern + 60 精品文章

查看全部评分

回复 支持 反对

使用道具 举报

Lv3.寻梦者 (版主)

…あたしは天使なんかじゃないわ

梦石
0
星屑
2207
在线时间
4033 小时
注册时间
2010-10-4
帖子
10779

开拓者贵宾

8
发表于 2015-2-18 11:44:46 | 只看该作者
技巧6里面,自己测试游戏的时候保存一下游戏就没有了吧……还不如保存在另一个文件里呢。

点评

不会没有啊,而且实际测试的时候,二周目信息已经转成开关或者变量了。测试二周目可以调开关啊。  发表于 2015-2-18 11:50
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
60
在线时间
705 小时
注册时间
2007-12-23
帖子
874
9
发表于 2015-2-19 16:32:57 | 只看该作者
很有营养的东西。。

不过对于技巧6最好能够提供把二周目存档放在另一个文件的做法(类似Global_save之类的),不然不仅影响了严谨性(把item拿来做存档),而且也不方便转移存档呢(比如把存档拷到另一台电脑发现二周目又变回了一周目)

点评

生成独立文件是个好方法,这里其实只是记录了一个比较“另类”的方法。我是觉得两种方法各有特色,而且实现起来的难度都差不多。  发表于 2015-2-25 10:25
还是直接删档简单吧……这个真的,放在一个 Global_save 之类的存档里比较好  发表于 2015-2-25 10:08
只有当玩家选择“二周目”时才打开相应开关  发表于 2015-2-25 09:56
在选择“新游戏”后,如果二周目打通可以设置一个选项,让玩家选择是玩几周目。因为在实际游戏中负责表示几周目的是开关或者变量  发表于 2015-2-25 09:56
那么,如果玩家想要把存档删掉,回到一周目重新玩呢?  发表于 2015-2-25 08:47
买了正版RMMV的同学进来看一下,谢谢~
https://rpg.blue/thread-393237-1-1.html
回复 支持 反对

使用道具 举报

Lv4.逐梦者 (版主)

梦石
0
星屑
9497
在线时间
5073 小时
注册时间
2013-6-21
帖子
3580

开拓者贵宾剧作品鉴家

10
 楼主| 发表于 2015-3-19 15:22:50 | 只看该作者
更新技巧6、7,自顶。
技巧6是二周目的制作思路,虽然少用但是值得借鉴一下。

技巧7是一个多项选择插件的简易实现版,接入后再结合事件编辑器可以达到不错的效果,而且没有违和感。
借助这个思路,或许可以做出道具选择插件(即NPC向主角索要一个道具,主角选择道具之后对话继续)之类的效果。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-4-20 00:52

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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