Project1

标题: 敌人生成行动的时机是否有被明确化的可能性 [打印本页]

作者: Thylakoid    时间: 2015-2-1 21:11
标题: 敌人生成行动的时机是否有被明确化的可能性
本来想按照习惯发一个提问帖,但整理过内容之后,还是觉得发成讨论帖比较合适。因为要实现的并不是某一种有明确感性认知的特殊效果,我个人或许找不出能准确测试答案正误的手段,所以还是希望通过讨论的方式,请对XP脚本比较有研究的各位从系统和理论层面给出自己相对严密的见解。


起因是读了这篇帖子里提到的问题。https://rpg.blue/thread-375667-1-2.html
这里要先请曾参与讨论问题的几位朋友来看一下该问题的后续处理。@飞火流萤  @guoxiaomi  @永恒の未知数  

该帖子提出默认系统中对存活角色使用“附带复活效果”的回复技能时,技能无效的问题。而后通过修改Game_Battler 3 和Scene_Battle 4 里的相关内容,该问题得到解决。我在该贴的4楼补充了enemy的情况,也就是复活技能的使用者为敌人的状况下,即使敌人队伍中的每个敌人都存活,技能仍然可以发动。但当时我只测试了角色使用复活技能的情况,并没有特别去测试敌人。后来经过测试,我才发现我补充的方法还没做到位,敌人在这种状况下还是无法对存活的单体使用复活技能。究其原因,得知因为敌人行动时目标完全随机,所以在确定复活目标的时候被Game_Troop里面的这段话拦了下来。
  1. #--------------------------------------------------------------------------
  2.   # ● 对像敌人的随机确定
  3.   #     hp0 : 限制 HP 0 的敌人
  4.   #--------------------------------------------------------------------------
  5.   def random_target_enemy(hp0 = false)
  6.     # 初始化轮流
  7.     roulette = []
  8.     # 循环
  9.     for enemy in @enemies
  10.       # 条件符合的情况下
  11.       if (not hp0 and enemy.exist?) or (hp0 and enemy.hp0?)
  12.         # 添加敌人到轮流
  13.         roulette.push(enemy)
  14.       end
  15.     end
  16.     # 轮流尺寸为 0 的情况下
  17.     if roulette.size == 0
  18.       return nil
  19.     end
  20.     # 转轮盘赌,决定敌人
  21.     return roulette[rand(roulette.size)]
  22.   end
  23.   #--------------------------------------------------------------------------
  24.   # ● 对像敌人的随机确定 (HP 0)
  25.   #--------------------------------------------------------------------------
  26.   def random_target_enemy_hp0
  27.     return random_target_enemy(true)
  28.   end
复制代码
参照脚本。复活技能的使用对象是hp为0的己方单体。而敌人使用技能时是走随机确定对象的程序,因此执行random_target_enemy_hp0这个方法,而这个方法的定义是返回参数为true的random_target_enemy方法。然后,因为hp0=true,而且敌人全体存活,所以 if (not hp0 and enemy.exist?) or (hp0 and enemy.hp0?)这句判断没有满足条件的结果,以至于在敌人全员存活的情况下,每次轮到敌人发动复活技能时,由于没有对象,敌人直接发呆跳过行动。我将其修改成了如下内容,终于解决了这个问题。
  1.    #--------------------------------------------------------------------------
  2.   # ● 对像敌人的随机确定 (HP 0)
  3.   #--------------------------------------------------------------------------
  4.   def random_target_enemy_hp0
  5.     # 初始化轮流
  6.     roulette = []
  7.     # 循环
  8.     for enemy in @enemies
  9.       # 条件符合的情况下
  10.       if enemy.hp0?  # 优先以hp为0的敌人为技能对象
  11.         # 添加敌人到轮流
  12.         roulette.push(enemy)
  13.       end
  14.     end
  15.     # 轮流尺寸为 0 的情况下
  16.     if roulette.size == 0  # 如果全员存活
  17.       return random_target_enemy(false)  # 就随机决定对象以保证技能照常发动
  18.     end
  19.     # 转轮盘赌,决定敌人
  20.     return roulette[rand(roulette.size)]
  21.   end
复制代码
按照上述方法,我实现了敌方单体复活技能优先以hp为0的成员为目标,若全体存活,依然保证该技能发动而不至于跳过行动。



实际测试时我设置了4个速度稍有差异的敌人,每个敌人只会复活技能,技能所需sp为0。效果也算不错。但还是经常存在行动滞后的问题。比如某个敌人被打倒,但其他敌人还在互相随机释放复活技能,或者某敌人已经被复活,其余敌人还一味地对着它释放复活技能。这才使我联想到了敌人生成行动时机的问题。是否是因为敌人的行动内容在该回合开始时就决定好了,等到轮到某个敌人行动的时候直接执行本回合事先决定好的动作,所以在等待的过程中,敌人的行动不会跟随场上状况的变化而改变,从而造成行动滞后呢?若是如此,是否有方法能让敌人像角色一样在轮到它行动的时候再开始决定行动内容呢?看过默认工程的战斗系统后,发现敌人的行动的确是统一生成,在Scene_Battle 4 里
  1. # 生成敌人行动
  2.     for enemy in $game_troop.enemies
  3.       enemy.make_action
  4.     end
  5.     # 生成行动顺序
  6.     make_action_orders
  7.     # 移动到步骤 1
  8.     @phase4_step = 1
复制代码
但是如何将其拆开,并且对应给当前队伍中的每一个敌人,同时还要更改生成行动的时机,这已经超出了我的能力范围。当然,也可能并没有我想象的那么难。不过,就算能修改,我一时间也想不出用什么办法能测试出修改后的结果是否奏效。毕竟战斗中出现的一些行动滞后的现象只是感官上的认知,究竟是真的滞后,还是恰好就随机成了这个样子,我也不知道该怎么区分。因此提出这样一个问题供大家讨论。看是否有可能让每个敌人生成行动的时机被确定在具体轮到它行动的时候,而不是全队统一;如果可能,该如何实现;实现后,又该用什么方法验证它确实实现了。

最后,如果真的可以把敌人生成行动的时机精确到这个程度,我觉得对于敌人AI的编写或许有一定的意义,敌人的行动可能会因此变得更靠谱一些。

@寒冷魔王 @RyanBern 望不吝指点。
作者: taroxd    时间: 2015-2-1 21:40
本帖最后由 taroxd 于 2015-2-1 21:42 编辑

楼主想要的,大概只是CP制战斗?

否则的话,敌人随时决定行动,而玩家只能在回合开始决定,是否有些不公平?
作者: RyanBern    时间: 2015-2-1 21:42
LZ前面的分析都是正确的,至于LZ提出的问题,我想可能是LZ把它想得有些困难,刚才查看了下Scene_Battle4发现不是很难改。
确实,敌人所有的行动都是在回合开始时就决定好了,如果要临时生成的话,可以将start_phase4中生成行动的相应部分去掉,把它们移动到别处。
注意到主回合(即@phase = 4)时,一共分为6个步骤,观察之后我们发现,即便是敌人决定好了如何行动,但是由于某种原因,此敌人突然中了【混乱】状态,那么他之前生成的行动将会作废,取而代之的是另外一组行动。这个行动不就是临时生成的么?我们可以以此为突破口,寻找最适合插入生成行动指令的位置。
因此,我们在update_phase4_step2里面挨个生成敌人的行动,由于这个方法依据@active_battler进行设置,只要判断@active_battler是不是敌人,如果是就生成相应行动就好。
另外提一句,敌人和角色在行动上没有区别,都是在回合开始之前把行动确定下来,唯一的不同在于角色的行动是玩家生成,而敌人的行动是电脑随机生成罢了。所以说,即便是角色,也不能在“轮到他时才确定行动内容”。这样看来,如果把敌人的AI改成这样,那么对角色来讲并不是很公平呢。
作者: Thylakoid    时间: 2015-2-2 00:39
RyanBern 发表于 2015-2-1 21:42
LZ前面的分析都是正确的,至于LZ提出的问题,我想可能是LZ把它想得有些困难,刚才查看了下Scene_Battle4发 ...

抱歉哈。可能是因为在战斗中我只是通过感性认知来判断效果的,而且设置的敌人速度并不快,用来测试的己方角色只有两个,所以才误以为默认工程下,决定了某个角色的行动内容时,这个角色就立即开始行动了吧。仔细看过脚本之后,确实正常的运算顺序是在统一决定完所有角色的行动内容后,先统一排列行动顺序,之后再等待生成行动结果的。也正如@taroxd 版主所说,CP制的角色行动是即时性的(这次这话不敢说死,但至少我手有上一个作品是这样的效果,以后慢慢研究),就是玩家下了命令,当前的角色就立刻生成行动结果了,但敌人那边仍然是统一的,这个需要进一步去修改。谢谢版主提示。


作者: 飞火流萤    时间: 2015-2-7 11:25
对的,挺不错,就是AI不好掌控
作者: Thylakoid    时间: 2015-2-22 22:14
RyanBern 发表于 2015-2-1 21:42
LZ前面的分析都是正确的,至于LZ提出的问题,我想可能是LZ把它想得有些困难,刚才查看了下Scene_Battle4发 ...

试了一下,还是不行。我先把phase4里面敌人生成行动的部分注释掉了。就是以下内容
  1.     # 生成敌人行动
  2. #    for enemy in $game_troop.enemies
  3. #      enemy.make_action
  4. #    end
复制代码
然后把第二步刷新的内容改成了这样。
  1.   #--------------------------------------------------------------------------
  2.   # ● 刷新画面 (主回合步骤 2 : 开始行动)
  3.   #--------------------------------------------------------------------------
  4.   def update_phase4_step2
  5.     # 如果不是强制行动
  6.     unless @active_battler.current_action.forcing
  7.       # 限制为 [敌人为普通攻击] 或 [我方为普通攻击] 的情况下
  8.       if @active_battler.restriction == 2 or @active_battler.restriction == 3
  9.         # 设置行动为攻击
  10.         @active_battler.current_action.kind = 0
  11.         @active_battler.current_action.basic = 0
  12.       
  13.       # 限制为 [不能行动] 的情况下
  14.       elsif @active_battler.restriction == 4
  15.         # 清除行动强制对像的战斗者
  16.         $game_temp.forcing_battler = nil
  17.         # 移至步骤 1
  18.         @phase4_step = 1
  19.         return
  20.         
  21.       # 没有任何限制的情况下,正常生成行动。
  22.       elsif @active_battler.is_a?(Game_Enemy) #如果当前开始行动者为敌人
  23.         @active_battler.make_action   #生成当前行动者的行动
  24.       end
  25.     end
  26.     # 清除对像战斗者
  27.     @target_battlers = []
  28.     # 行动种类分支
  29.     case @active_battler.current_action.kind
  30.     when 0  # 基本
  31.       make_basic_action_result
  32.     when 1  # 特技
  33.       make_skill_action_result
  34.     when 2  # 物品
  35.       make_item_action_result
  36.     end
  37.     # 移至步骤 3
  38.     if @phase4_step == 2
  39.       @phase4_step = 3
  40.     end
  41.   end
复制代码
结果测试的时候,敌人永远跳过行动,也就是行动没有被生成。
之后又做了其他尝试。发现即使把前面注释掉的生成敌人行动复原,或者干脆把那部分的脚本直接搬到步骤2中都没有效果。也就是说,无论之前是否生成了敌人的行动,只要在步骤2里写生成行动的语句,行动就不能生成。为什么呢?
作者: RyanBern    时间: 2015-2-23 17:22
Thylakoid 发表于 2015-2-22 22:14
试了一下,还是不行。我先把phase4里面敌人生成行动的部分注释掉了。就是以下内容然后把第二步刷新的内容 ...

RUBY 代码复制
  1. def update_phase4_step2
  2.     # 如果不是强制行动
  3.     unless @active_battler.current_action.forcing
  4.       @active_battler.make_action if @active_battler.is_a?(Game_Enemy)
  5.       # 限制为 [敌人为普通攻击] 或 [我方为普通攻击] 的情况下
  6.       if @active_battler.restriction == 2 or @active_battler.restriction == 3
  7.         # 设置行动为攻击
  8.         @active_battler.current_action.kind = 0
  9.         @active_battler.current_action.basic = 0
  10.       end
  11.       # 限制为 [不能行动] 的情况下
  12.       if @active_battler.restriction == 4
  13.         # 清除行动强制对像的战斗者
  14.         $game_temp.forcing_battler = nil
  15.         # 移至步骤 1
  16.         @phase4_step = 1
  17.         return
  18.       end
  19.     end
  20.     # 清除对像战斗者
  21.     @target_battlers = []
  22.     # 行动种类分支
  23.     case @active_battler.current_action.kind
  24.     when 0  # 基本
  25.       make_basic_action_result
  26.     when 1  # 特技
  27.       make_skill_action_result
  28.     when 2  # 物品
  29.       make_item_action_result
  30.     end
  31.     # 移至步骤 3
  32.     if @phase4_step == 2
  33.       @phase4_step = 3
  34.     end
  35.   end

我改成了这样,然后注释掉前面的部分,测试没问题啊
作者: Thylakoid    时间: 2015-2-23 21:33
RyanBern 发表于 2015-2-23 17:22
def update_phase4_step2
    # 如果不是强制行动
    unless @active_battler.current_action.forcing ...

看来是脚本冲突的问题了。我这次测试用的不是默认工程而是一个成品工程,用的CP制的战斗脚本。换成默认工程后就正常了。有待进一步整合吧。这个问题应该可以结束了。
作者: y967    时间: 2015-3-9 16:12
敌人的AI是让人头疼的事啊

敌人普遍都很傻很笨




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