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

Project1

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

[讨论] 和我一起读脚本--Sprite_Battler和Game_Battler剪不断理还乱。

[复制链接]

Lv4.逐梦者

梦石
0
星屑
14616
在线时间
2196 小时
注册时间
2019-1-24
帖子
1123

R考场第七期纪念奖

跳转到指定楼层
1
发表于 2022-2-26 22:01:15 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

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

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

x
本帖最后由 miantouchi 于 2022-2-26 22:04 编辑

虽然XP我已经前前后后用了3年多,但是还是有许多地方是死记硬背,有很多基础的概念还是混淆的。
比如Sprite_Battler和Game_Battler的关系。用着用着自己就迷茫了。带着这个疑问,
我来根据自己理解去尝试的分析下。


一。首先先来梳理下基础关系。
Sprite_Battler 表示战斗者精灵(用来显示精灵用的)、Game_Battler表示战斗者(战斗者的一些数值)
Game_Battler 还有两个子类Game_Actor和Game_Enemy。
Sprite_Battler 的父类是RPG::Sprite

二。说到Sprite_Battler ,我们得看他究竟用在什么地方?
通过全局搜索Sprite_Battler

我们发现Sprite_Battler的实例都是在Spriteset_Battle里面使用的。
Spriteset_Battle则是在战斗场景中使用的。

三。接下来我们看看Spriteset_Battle是如何使用Sprite_Battler 的?



大家可以看到
敌人的活动类传入的是battler是队伍中每一个敌人enemy,
而角色的活动类只传入的是@viewport2,后面的battler没有传入,
Sprite_Battler 初始化里面如果没有传入则battler默认是nil

四。那角色的battler是什么时候传入的呢?
请看下图


第一个红框,也就是在Spriteset_Battle的update里面 把队伍成员赋值给 actor_sprites这个数组里面
第二个红框,就是启用了Sprite_Battler类当中的update刷新功能。
这样就达成了通过Spriteset_Battle类当中实例化所需要的敌人和角色的Sprite_Battler类,
实例和刷新Sprite_Battler的同时注入了队伍当中Game_Battler 角色数据。
结合这个结论大家仔细看看Sprite_Battler类是不是使用了@battler.xxxx,
达到用判断@battler.xxxx战斗者数值去设置self.xxx显示精灵类的效果。
也就是感觉到精灵类包含了战斗者,多了个插件的感觉。


所以可以使用
@spriteset.actor_sprites             #队伍的角色精灵
@spriteset.actor_sprites.battler  #队伍的战斗者

根据菜刀大大的提示 如果队伍不足4人会,会对应的传入nil,容易引起bug。
另外这些也不是本人想出来的,有之前KB大大的解读。

欢迎大家参与讨论,下次准备讨论Game_Actors和Game_Party


点评

哈哈,我正在研究猫大下面发的脚本解读。  发表于 2022-2-28 21:38

评分

参与人数 2+2 收起 理由
flashfox + 1 同约
taeckle + 1 大神下次什么时候读脚本,我们要预约!.

查看全部评分

Lv5.捕梦者 (版主)

梦石
1
星屑
23994
在线时间
3339 小时
注册时间
2011-7-8
帖子
3926

开拓者

2
发表于 2022-2-26 23:07:17 | 只看该作者
Game_XXX只是负责存储游戏数据,也提供修改自身属性的方法。而在不同的场合要如何渲染这些数据,就需要配合Sprite_XXX类。个人认为,这里使用了组合的方式引入@battler对象,在如今“组合优于继承”的大环境下看,是一个很好的设计。

评分

参与人数 1+1 收起 理由
miantouchi + 1 我很赞同

查看全部评分

熟悉rgss和ruby,xp区版主~
正在填坑:《膜拜组传奇》讲述膜拜组和学霸们的故事。
已上steam:与TXBD合作的Reformers《变革者》
* 战斗调用公共事件 *
* RGSOS 网络脚本 *
回复 支持 反对

使用道具 举报

Lv5.捕梦者 (版主)

遠航の猫咪

梦石
3
星屑
23191
在线时间
2387 小时
注册时间
2005-10-15
帖子
1166

开拓者

3
发表于 2022-2-27 08:24:56 | 只看该作者
本帖最后由 SailCat 于 2022-2-27 08:40 编辑
@spriteset.actor_sprites             #队伍的角色精灵
@spriteset.actor_sprites.battler  #队伍的战斗者


首先更正一下,这样做可不行,因为@actor_sprite没有暴露给外面,要么用反射去取,要么自己定义actor_sprites方法或者attr_reader

内部的脚本我也琢磨的差不多了,当初写SEP core的时候真的是把每一个类(包括RPG模块的内部类)掰下来看,我的心得是,看一个类需要看它的父类,但是不需要看它的子类
首先sprite_battler里面给battler传的是nil这也没什么问题,nil的战斗者没有图像,透明度就是默认的0,而0透明度是不会执行任何效果操作的
默认的Spriteset_Battle中对于同伴战斗者是锁死了4个人,但对于敌方战斗者实际上是用循环插入的,不到8人计为实际人数,超过8人的扩展也很方便,直接对Game_Troop动刀子就可以了
为什么锁死,首先编辑器里方方面面都不允许你超过4个人,其次战斗中有战斗解释器,允许你替换队员(这里主要是指加入)如果不锁死4个,而是像敌方那样用循环插入,不改解释器就没法实现战斗中加入新队员
如果要实现3人制战斗,自主换人战斗,5人制战斗,人数没有上限的战斗,就并不是只改那个连续4行的Sprite_Battler.new(...)就可以了的,需要连锁修改很多东西。若没有修改好,那是会有bug。

至于Game_Battler,那就是个数据栏,没有任何画面表现的部分,只是为屏幕上的内容提供画面表现所需的数据,和Game_Screen性质一样。
这两个类都是可以存到存档里的,而众所周知Bitmap类和Sprite类是没法dump的。因此一个简单的结论就是Game_XX并不会包含画面表现。

SEP的战斗类扩展插件中分成了“战斗静态增强”和“战斗动态增强”两个部分,分野就是凡是对Game_Battler及其衍申类(Game_Actor, Game_Enemy, Game_Party, Game_Troop, Game_Actors, Game_BattleAction)的都是静态增强,动态增强则必须要去动Sprite_Battler和Spriteset_Battle
所以静态类增强插件中也可能包括一些大幅度修改Scene_Battle的内容,比如攻击范围拓展,几乎重写了这个类里涉及phase3的所有部分,但因为这过程中没有对战斗者精灵做任何处理,所以依然算成静态增强。

再说一下继承的事情
Game_Battler是一个基本类,但这个类不在战斗中实际使用,实际使用的都是Game_Actor和Game_Enemy,你对战斗中的任何一个战斗者调用is_a?(Game_Actor)和is_a?(Game_Enemy)总会有一个返回true
如果说后作的话,像VA更是只定了一个纯接口,连处理功能都欠的Game_BattlerBase(其实我并不知道这样做的意义是什么)
而这种设计的好处,就是你可以将那些公共的处理放在Game_Battler里面,只把很少数的敌我有区分的处理拿出来放到两个继承类当中
贴一段这两天弄的自动状态(XP特有功能)的代码
RUBY 代码复制
  1. #--------------------------------------------------------------------------
  2.   # ● 获取额外状态(非附身但有效)
  3.   #--------------------------------------------------------------------------
  4.   def extra_states
  5.     # 自身来源特性数据的自动状态
  6.     set = features(feature_list(true), [], :auto_state_set)
  7.     # 自身习得特技的自动状态
  8.     set.concat(feature(learned_skills, [], :auto_state_set))
  9.     # 自己的非排除性光环
  10.     set.concat(aura_states(0, 2))
  11.     # 本方其他人的光环
  12.     set.concat(feature(friends_unit.members - [self], []) {|battler|
  13.       battler.aura_states(0, 1, 2, 3)})
  14.     # 本方全队的光环
  15.     set.concat(friends_unit.aura_states(0, 2))
  16.     # 对方战斗者作用于本方的光环
  17.     set.concat(feature(opponents_unit.members, []) {|battler|
  18.       battler.aura_states(2, 3, 4)})
  19.     # 对方全队作用于本方的光环
  20.     set.concat(opponents_unit.aura_states(2, 4))
  21.     # 地图生效的光环(继承对象定义该方法)
  22.     set.concat(active_map_auras)
  23.     # 开关生效的光环(继承对象定义该方法)
  24.     set.concat(active_switch_auras)
  25.     # 数据单项化,排除无效数据
  26.     set.uniq!
  27.     set.reject! {|i| $data_states[i].nil?}
  28.     # 连锁 [+] 状态的处理
  29.     loop do
  30.       last_size = set.size
  31.       set = set.inject(set) {|a, i| a | $data_states[i].plus_state_set}
  32.       break if last_size == set.size
  33.     end
  34.     # 排除 HP 0 的状态后返回
  35.     set.reject {|i| $data_states[i].zero_hp}
  36.   end

这段代码写在Game_Battler当中,在Game_Actor和Game_Enemy中根本就不再重定义,直接调就完了。但是对于自动状态中,地图和开关生效的效果我方和敌方的判定确实是有区别的,那把这两块切出来定义到Game_Actor和Game_Enemy里面就完事了。
但这样做确实是会存在一个问题,我的注释里写了“继承对象定义该方法”,表明在父类中这是一个虚方法(C#叫abstract关键字)但实际使用的对象肯定定义了,那就意味着如果你再从Game_Battler分支出其他类,也必须定义这个方法,否则父类这个方法运行会报NoMethodError
所以VA的Game_BattlerBase的意义,其实就是把这些虚方法全部都写了默认接口,让它返回一个东西,以免报错。但如果确有必要,我在XP中,搁Game_Battler里写也可以。

最后说一说Game_Battler还能扩展什么,其实很简单
Game_Actor:数据源是RPG::Actor,阵营属于我方,接受Game_Party的控制
Game_Enemy:数据源是RPG::Enemy,阵营属于敌方,接受Game_Troop的控制
然后你顺着这个思路下去:
召唤兽系统:Game_Summon:数据源是RPG::Actor,阵营属于我方,不接受Game_Party的控制,自主行动
宠物系统:Game_Pet: 数据源是RPG::Enemy,阵营属于我方,接受Game_Party的控制
被囚禁带到战场上的队员:Game_Prisoner:数据源是RPG::Actor,阵营属于敌方,接受Game_Troop的控制(你可以给他加行动限制等)
养成怪系统:Game_Minion:数据源是RPG::Class,阵营属于我方,接受Game_Party的控制
器魂系统:Game_Soul:数据源是RPG::Weapon/RPG::Armor,阵营属于你随便吧……

评分

参与人数 3+3 收起 理由
miantouchi + 1 精品文章
guoxiaomi + 1 Prisoner有意思
KB.Driver + 1 精品文章

查看全部评分

SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
回复 支持 反对

使用道具 举报

Lv4.逐梦者

梦石
0
星屑
14616
在线时间
2196 小时
注册时间
2019-1-24
帖子
1123

R考场第七期纪念奖

4
 楼主| 发表于 2022-3-1 20:14:14 | 只看该作者
SailCat 发表于 2022-2-27 08:24
首先更正一下,这样做可不行,因为@actor_sprite没有暴露给外面,要么用反射去取,要么自己定义actor_spr ...

感谢猫大细心讨论。
1.要么用反射去取,能具体介绍下,这个是什么方法吗?
2.猫大没说之前真没发现敌人上限是8个,我通过数据库队伍测试了下,但是代码里面并没有相关的设定。
3.dump、uniq!、reject!等等,如concat连接数组的用法,都没有用过,我的基础还不太行。不过看了这个代码,感觉还是应该把SEP插件下载下来,读一读,总的来说还得继续读读猫大代码,然后看看自己能理解多少,问问题感觉都问不过来。。。
4.猫大思路的扩展来说,让我突然又发现了XP代码的无限可能,估计这也是猫大热衷于探索XP这个老家伙的动力吧。

点评

反射是@spriteset.instance_variable_get(:@actor_sprites)  发表于 2022-3-1 20:17
回复 支持 反对

使用道具 举报

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

本版积分规则

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

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

GMT+8, 2024-11-25 14:05

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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