Project1

标题: 【含思路讲解】存读档、场所移动时保存事件位置【2.0】 [打印本页]

作者: gonglinyuan    时间: 2015-9-15 09:09
标题: 【含思路讲解】存读档、场所移动时保存事件位置【2.0】
本帖最后由 gonglinyuan 于 2015-9-17 08:52 编辑

许多人在制作游戏时会发现自己设置的事件移动在场所切换或者读档之后事件又恢复原位了。事实上我也觉得RM的有些机制还是很不符合我们的使用习惯的。但是为了存读档的效率和存档的大小又不能把所有地图的所有事件都全部存下来。不过,把事件位置和面朝方向存下来还是很方便的。(注意,这个脚本不能保存事件透明度变更和行走图变更,如果需要相关功能请自行修改

当初我写这个脚本的时候其实只参考了Oksh的魔塔样板7822。结果等我写完在站上一搜发现其实有好多前辈对这个问题都已经有所研究了,而且大家的代码都颇为相似(毕竟一共也没几行,也没办法标新立异啊)。希望见到过类似东西的朋友不要冤枉我是抄袭的……

先把代码发出来吧(插入在Main前):

RUBY 代码复制
  1. class Game_Character
  2.   attr_accessor :direction
  3. end
  4.  
  5. class Game_Event
  6.   attr_reader :page
  7. end
  8.  
  9. class Game_Map
  10.   alias mapsave_game_map_initialize initialize
  11.   def initialize
  12.     mapsave_game_map_initialize
  13.     @event_pos = {}
  14.   end
  15.  
  16.   def save_event_pos
  17.     return unless @map
  18.     @event_pos[@map_id] = {}
  19.     for i in @map.events.keys
  20.       if @events[i].page
  21.         @event_pos[@map_id][i] = [@events[i].x, @events[i].y, @events[i].direction, @events[i].page.graphic.character_name]
  22.       end
  23.     end
  24.   end
  25.  
  26.   def load_event_pos
  27.     return unless @event_pos[@map_id]
  28.     for i in @map.events.keys
  29.       if @event_pos[@map_id][i] and @events[i].page
  30.         if @events[i].page.graphic.character_name == @event_pos[@map_id][i][3]
  31.           @events[i].moveto(@event_pos[@map_id][i][0], @event_pos[@map_id][i][1])
  32.           @events[i].direction = @event_pos[@map_id][i][2]
  33.         end
  34.       end
  35.     end
  36.   end
  37.  
  38.   alias mapsave_game_map_setup setup
  39.   def setup(map_id)
  40.     save_event_pos
  41.     mapsave_game_map_setup(map_id)
  42.     load_event_pos
  43.   end
  44. end


首先,我把Game_Character中原本在类外只读的@direction变为可以读写,这样就可以直接在外部修改事件的面朝方向了。(想来想去还是这样修改最简洁优美,感谢RyanBern大神的开导)

为什么要这样“粗暴”地修改方向而不是使用turn_leftturn_right之类的方法呢?因为那几个方法中含有对@direction_fix,即【是否面向固定】的判断,如果该事件的当前事件页是默认【面向固定】的,那么读取的朝向信息就不能作用于这个事件。所以这里直接通过修改@direction来实现方向的变更。

剩下的都是对Game_Map类的修改。在RGSS1中,Game_Map类只有一个实例就是$game_map(并不是像很多人想象得那样每张地图都有一个Game_Map的实例)。initialize方法只会在标题画面【开始新游戏】的时候调用一次。平时切换到场景的时候,是直接调用setup(map_id)方法,从数据库中读取地图内的所有信息存到$game_map中;而数据库在游戏过程中一般是不会被修改、也是不允许修改的,这就是为什么事件的位置在场所切换的时候不会被保存的原因。而在存档的时候,$game_map整个实例会被写入存档中。注意,在默认的RGSS1脚本中,$game_map保存当前地图上改变的一切信息(比如事件的移动和改变朝向等等),所以事件的位置理论上是不会复原的。但是读档的时候会校验游戏有没有被修改,如果被编辑器修改过,会把整个地图重新setup一次,以方便游戏作者的调试。

有些人可能会问为什么独立开关能够在切换场景的时候保存下来呢?因为独立开关压根儿就没有存在Game_Map里,而是存在一个$game_self_switches对象当中,这个对象是全局的,每次存读档都会被保存而且场所切换的时候也不会受到任何影响。

既然$game_map对象在存档、读档的时候会被保留下来,那么在$game_map中把我们需要的信息都保存下来就可以了。于是就有了我对Game_map.initialize的修改——在最后加上了一行对于@event_pos这个hash表的初始化。这个hash表的key是地图的id——一个整数,value是一个以事件id为键、事件位置和朝向这一数组为值的hash表(不知道能否理解,具体可以查看代码)。我们的@event_pos是全局的,要对每张地图都存储事件的位置,不然就达不到我们的目的了。

不管怎么样,似乎问题都处在setup方法上。因为用setup加载一张新地图的时候,原来地图上的一切改变过的信息都销声匿迹了。我们要做的就是抢在setup加载新地图之前把原地图的所有事件位置和朝向都保存下来,然后在加载完新地图之后把以前保存下来的新地图的事件位置应用到新地图所有事件上面、恢复上次离开地图时它们的位置。这样看来,我重新定义的setup方法应该是很好理解的。

最后就是save和load两个方法。首先在save方法:

RUBY 代码复制
  1. return unless @map


等价于

RUBY 代码复制
  1. return if @map == nil


这是考虑到游戏刚开始第一次加载地图的情况。

RUBY 代码复制
  1. for i in @map.events.keys
  2.       if @events[i].page
  3.         @event_pos[@map_id][i] = [@events[i].x, @events[i].y, @events[i].direction, @events[i].page.graphic.character_name]
  4.       end
  5.     end


这很容易理解,就是清空原数据,将当前的事件位置写入。如果当前事件不满足任何一个事件页的开启条件,那么它的面朝方向和位置没有意义(虽然朝向已经被坑爹地初始化为面向下方了)。为什么要把charater_name也记录下来呢?这在后面会讲到,为了避免BUG。

然后再看load方法:

RUBY 代码复制
  1. return unless @event_pos[@map_id]


同理。如果@event_pos[@map_id] == nil说明这张地图的事件位置没有被保存过(一般来说代表这张地图是第一次加载),此时就没办法读取事件位置了。

RUBY 代码复制
  1. for i in @map.events.keys
  2.       if @event_pos[@map_id][i] and @events[i].page
  3.         if @events[i].page.graphic.character_name == @event_pos[@map_id][i][3]
  4.           @events[i].moveto(@event_pos[@map_id][i][0], @event_pos[@map_id][i][1])
  5.           @events[i].direction = @event_pos[@map_id][i][2]
  6.         end
  7.       end
  8.     end


一个一个地还原事件的位置。注意到还原事件位置的条件是保存下来的character_name和事件当前的character_name相同;如果不这样判断一下,那么当游戏进程进行导致再次进入地图时引起事件页切换、而且切换前后的行走图都不一样了(可以理解为相当于换了一个事件,这是占用了同一个位置而已),那么原来的位置信息和朝向信息就可以舍去了。当然,这里的判断条件可以自行修改,按照自己的使用习惯即可。

其实写了这么多还是为了看得懂的同学帮我查查有没有BUG什么的……毕竟对RGSS的代码钻研得不太多,这些东西都是自己一个一个发掘出来的。大家一起交流、增进讨论才能更高效率地学会这些脚本嘛!
作者: RyanBern    时间: 2015-9-15 15:22
看了一下,感觉还是不错的。不过总觉得和这里的VA脚本有些太像了https://rpg.blue/thread-374164-1-1.html
个人觉得黑掉move_to还是可以的,不过我这种情况下我还是习惯单独开一个方法修改@direction,attr_accessor :direction不定义也罢,因为我定义了另一个更改@direction的方法,这样做不过是自欺欺人罢了。如果是我自己改脚本,我会考虑下面的模式:能否添加方法->能否使用alias对原有方法扩充->对原有方法重定义。所以,能不黑掉原来的方法就尽量不黑。
已加入XP图书馆,希望这种资源能越来越多。




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1