Project1

标题: 关于sailcat大人的《跟我学脚本2》的小小疑问 [打印本页]

作者: madao776    时间: 2009-5-21 07:22
标题: 关于sailcat大人的《跟我学脚本2》的小小疑问
新人报到~

刚才看完了sailcat大人的《跟我从头学脚本2》的五章内容,基本理清了思路,但是有两个地方一直是不太明白。

这两个地方都出现在第4章。

一:原文——事件页:出现条件[无],执行:
       脚本:$game_switches[1]=not $game_switches[1]
   从前面一章的教程中,很容易看出,这条脚本的意思是:将开关1的值取相反值(true变为false, false变为true)后,再赋值给开关1。
   这不就实现了我们的要求——调查一次开启开关,再调查一次关闭开关吗?


疑问——这个理论上是完全理解的,然后我做了这样的尝试,我在把一块草皮设置了开关1 ON时战斗处理,还有在一边设置了一个类似矿洞里的摇杆开关。我在摇杆开关的事件页写入了这样的脚本:$game_switches[1]=not $game_switches[1]。
理论上,当我对着开关按键时应该开启开关1,这时走到草皮会进行战斗处理。
但是当我按键时却出现了SyntaxError  囧。。。。。于是我把脚本修正为:
$game_switches[1]=! $game_switches[1]
仅仅是把“not”改成了“!”,这次成功了,当我“2”次按键后开关变成on了,并且在草皮上进行了战斗处理,没错,是2次按键。。囧。。。然后,当我再次按键时开关没有恢复成OFF,多次尝试无果。。。


二:原文——条件分歧:$game_player.moving?
     变量操作:[0001]=角色的地形标记
     变量操作:[0002]=步数
     变量操作:[0002]%=10
     条件分歧:变量[0001]==4
       条件分歧:变量[0002]==0
         战斗处理
       分歧结束
     分歧结束
   分歧结束
   这样,只有在角色移动时,才会判断是不是要进行战斗处理。$game_player.moving?是判断主角是否在移动中的脚本,返回一个true或false的值,当该值为true时,执行分歧里面的操作。

疑问——这个也是理解的,因为战斗处理结束后step还是7,而地图标记还是4,应该会重复处理战斗事件。。但是依旧没有发生。。。。
我是这样设置的。同样是一块草皮,把它的地图标记设为4,然后从初始位置到草皮位置数了10格。脚本为:$game_player.terrain_tag == 4 and $game_party.steps % 10 == 0
            分歧结束
         分歧结束
注意,我没加上$game_player.moving?。当我运行游戏后,第一次走到草皮进行了战斗处理,但是结束后并没有重复循环。。。绕了一圈,算好了7的倍数再次来到草皮时又一次战斗处理,就是说,我没有加上“$game_player.moving?”却达到了想要的效果,这是为什么呢?


很小白的问题,人笨,希望大家帮帮忙,小女这边先谢谢了。

作者: madao776    时间: 2009-5-21 07:33
那个,再问一个问题。。。RM能实现字幕滚动么?
就是现在像影片结尾一样,让字幕从下至上翻过去的感觉。移动图片?
作者: redant    时间: 2009-5-21 07:55
是滑动字幕么? 事件很好解决只要移动图片即可

脚本就是循环移动坐标 比如@sprite.y -= 2
作者: 紫苏    时间: 2009-5-21 13:11
问题一:
语法问题,如果想把 not、and 和 or 用在并非判断条件分歧的地方,也就是单独当作表达式的时候,必须用括号把表达式包起来:(not something),而与之相对应的单目运算符 “!” 就不需要~
所以一般习惯了 C、Java 语法风格的人都不会去用 not、and 和 or,而直接用标准的逻辑运算符了

我不知道为什么会出现你说的按两次才打开开关的问题,因为理论上来说你在事件中这样写,第一次按会立刻打开开关,之后就再怎么按也无法关闭这个开关了,为啥呢?其实这是 RMXP 一个很老的 BUG 了~

首先要知道,事件中写的脚本是利用 Ruby 的 eval 这个全局函数,产生一个子解释器来执行的,那我们就来看看脚本中调用这个函数来解释脚本语句的地方——Interpreter 7

在 Interpreter 7 的最后一个函数 command_355 中,事件脚本中的语句被一行一行地读取到了 script 这个局部变量中,然后有这样一段脚本:
    # 评价
    result = eval(script)
    # 返回值为 false 的情况下
    if result == false
      # 结束
      return false
    end
eval 的返回值是解释到的最后一个语句的返回值,比如 eval 这两个语句就会返回 false:
eval("a = false")
而在 command_355 中, eval 的返回值被保存到了 result 中,如果 result 是 false,那么 command_355 就会返回 false;返回 false 之后,调用 command_355 的 execute_command 函数(Interpreter 2)就会接着返回 command_355 的返回值,也就是 false;而在调用 execute_command 的函数 update 中(Interpreter 1)有这样一段:
      # 尝试执行事件列表、返回值为 false 的情况下
      if execute_command == false
        return
      end
      # 推进索引
      @index += 1
当 execute_command 返回了 false 之后,整个 update 会直接结束(返回),这样的话 @index += 1 就没有被执行~
@index 是事件指令的索引,事件页中一行就是一个指令。当事件脚本框中只有一句脚本,且返回值是 false 时,@index 会一直保持在当前这句脚本命令行所对应的索引值,这是关键~
接下来场景继续刷新,再次调用了这个事件的 Interpreter 的 update,再次调用了 execute_command, execute_command 会根据 @index 的值来执行索引所对应的那条事件指令。可是由于前一次调用 execute_command 时它返回了 false,导致 @index 没有递增 1,所以这时 @index 还是对应之前 command_355 返回 false 时的那一句脚本,于是 command_355 再次被调用,这一句脚本再次被执行……可是,eval 这句脚本之后,它还是返回 false{/ll}!于是上述的步骤再次重复,形成了一个无限循环……

以上就是 RMXP 事件脚本中存在的 BUG,当你直接在事件脚本中写一句“false”,整个事件就会循环被执行,除非这个事件是并行处理,否则你的人物会无法做出任何行动,因为你触发的事件一直在执行~

回过头再来看楼主的这一句脚本:
$game_switches[1] = !$game_switches[1]
按上面所说的这个流程来推导,就不难发现这句脚本会导致的问题

首先,所有开关初始为 false;!$game_switches[1] 这个表达式相当于 !false,也就是 true;于是这条语句变成了:$game_switches[1] = true,eval 的返回值这时是 true,事件脚本执行结束,开关被打开,一切都没有问题~

当你再次触发这个事件时,!$game_switches[1] 就相当于 !true 了,所以这条语句就变成了:$game_switches[1] = false,这时 eval 返回了 false,上述所说的糟糕情况发生了{/kl},@index 没有被增加,导致事件解释器重新刷新时这条语句再次被执行……

但是,和上面提到的无限循环不同,这次的语句是一个逻辑取反的过程,当这条语句第二次被执行时,开关已经被改为了 false,于是取反后变为了 true,eval 也就返回 true,事件脚本结束。也就是说,在第一次你触发事件后,开关由 false 变为了 true;之后每再触发这个事件,开关就会由 true 变为 false,但马上又会被紧跟着的第二次刷新变回为 true,导致了之后你无论怎么按该开关都无法将开关变回 false 的情况~{/fn}

解决办法一:
问题的根本在于 eval 返回了 false,我们可以让在事件中写下的脚本永远能返回 true,于是我们把这个开关操作的语句改为:
$game_switches[1] = !$game_switches[1]
true
这样一来,脚本的最后一条语句是 true,eval 也就永远返回 true 了~

解决办法二:
问题的根本还在于返回 false 的事件脚本语句只有一行——第一行脚本的事件代码(355)和非第一行脚本的事件代码(655)是不同的,只有 @index 对应的事件代码是 355 的时候,execute_command 才会被调用~所以 @index 保持在第一行脚本命令的索引的话,触发一次事件就会多次调用 command_355 ……我们可以在 $game_switches[1] = !$game_switches[1] 后面按一下回车,让事件脚本变成两行(虽然第二行只是空行),这样一来在 command_355 按行循环添加事件脚本语句的时候 @index 就会增加,接着索引所对应的事件代码变为了 655,execute_command 会判断这个事件代码,发现它并不是第一行事件脚本的代码,于是就会直接返回 true~{/ka}这时 eval 虽然返回的还是 false,但 @index 已经增加到达了事件页的最大索引,于是事件结束了……撒花!

问题二:
你的条件分歧语句是放在并行处理的事件中的吗?如果是放在并行事件中判断的,那么战斗结束后是应该再次开始战斗的,如果可能的话可以上传一个工程~
作者: madao776    时间: 2009-5-21 18:00
学习了>_<,谢谢楼上的前辈,解释的太详细了~
第二个问题是我没有把分歧放在并行事件,没认真看教程的结果OMG。。。
再次感谢前辈~




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