|
渣翻译,本人无任何编程经验,所以很多基本术语纯脑补直翻,若有任何错误请诸位指正。
阿妈脚程:.SQF,让我们从头开始。在阿妈中有三种脚本概念:
脚本-从OFP时代就使用的古老工具。更广为人知的是.sqs脚本。介绍引擎的一种方式就是通过使用exec命令和.sqs文件所在路径来执行你的脚本。
VM脚本-脚本语言的主要使用方法。VM脚本既可以通过execVM命令和.sqf文件路径来执行,也可以用spawn命令,后一种情况下代码可以内联(可以写在
语块里?)。
FSM脚本-结合了.sqf代码和可视化开发算法的一种脚本。搞这个需要FSM编辑器。FSM脚本需要execFSM命令和FSM文件的路径。
今天我主要讲解.sqs为何是“陈旧但金贵的”。但首先请允许我提醒你脚本调度器如何运作。之前在我的博客里提到过,但回忆一下旧东西无伤大雅。事实上我先前说的东西是与引擎如何处理VM脚本相关的。
那么让我们来搞一个spawn过的脚本的分支吧。引擎需要管理整个框架。这里没有多线程或者并行执行,所以所有脚本都需要序列化执行。但是如果你在一个脚本后执行另一个脚本,需要等到前一个脚本执行完毕,尤其当前一个脚本有某些要求操作。
为了保持对其他后载入的脚本的公平,需要更加平等的分配优先级,这里我们需要用到调度器,每个脚本都需要载入调度器。这也是为何脚本是时序化的。调度器本身很简单。所有脚本被放入队列然后每个脚本获得时间戳。之后按照他们在队列中的位置依次执行。
但这里有个问题,每个脚本只有3毫秒或者更少的执行时间。如果在分配时间内执行完毕,它便从队列中移除。如果没搞完,运行就会被暂停,时间戳更新,队列中下一个脚本开始执行。脚本模拟到达最后时,队列重新排序,最早的脚本被放在队列顶端,然后进程继续。
这样可以保证没有脚本执行延迟并且均平等对待。然而运行时序化脚本会有点小问题。一个特定脚本多快完成是未知的。并且,所有非时序事件都比时序脚本的优先级高,这会延缓整个调度器队列直到非时序脚本执行完毕。
另一个弊端是如果你有两个时序化脚本共享同一个全局变量,你无法知道哪个会先改变它,甚至在脚本执行期间它会被改变多次。考虑到大多数脚本运行在时序化环境中,你要有个强劲的CPU,这样才能尽快运行脚本,不出现延迟。事实上,用好的CPU比用好的显卡能使阿妈运行的更好。
那么如果脚本从头到尾无中断的运行对你来说很重要的话,你该怎么办?很明显,你需要在非时序化环境中运行。所有事件处理器脚本都是非时序的。你还可以在时序化环境中运行一个FSM脚本,用它来执行你的非时序脚本。又或者,你可以用.sqs来写你自己的脚本。
不同于.sqf脚本,.sqs脚本一旦载入,从开始到结束就不会被中断(除非你在明确地添加了暂停代码 )。对比.sqf或者.fsm来说,.sqs或许载入时间更长,但一旦开始执行,它就可以终止。一方面,这是个好消息,另一个方面,如果你的.sqs脚本有超级多的操作需要花很长时间完成,那么这会降低游戏表现,因为它会让其他脚本等待。尽管如此,这还是一个有用的功能。
拿addAction举一个例子。这个命令需要两个参数ss。这个脚本在时序化境中运行。当addAction编译完后,编辑器会检查你脚本中传递了什么东西。如果是指向.sqf文件的字符引用或者只是代码{。。。},该脚本会被当成VM脚本,如果是指向.sqs的字符引用,,它仅仅被当做脚本处理。
http://www.youtube.com/embed/cxjYVDfi_PI(请自行翻墙谷歌)
为了证明这个概念,我做了个小对比,一个addAction调用.sqf文件,另一个调用.sqs(非VM)。我同样确定我把很多操作放进了调度器,然后同时显示[frames,time]。正如你从视频中看到的,.sqf脚本被中断了很多次,用了200帧超过6秒才完成。而.sqs脚本仅仅在一帧中花了不到一秒。显而易见,如果你的addAction里有重要代码,.sqs用起来将很方便。
接下来是我用来测试的代码:
- [] spawn {
- while {true} do {
- allMissionObjects "";
- };
- };
- player addAction ["VM", "vm.sqf"];
- player addAction ["nonVM", "nonvm.sqs"];
- //vm.sqf
- _frameNo = diag_frameNo;
- _tickTime = diag_tickTime;
- for "_i" from 1 to 100 do {
- allMissionObjects "";
- };
- hint str [diag_frameNo - _frameNo, diag_tickTime - _tickTime];
- //nonvm.sqs
- _frameNo = diag_frameNo
- _tickTime = diag_tickTime
- for "_i" from 1 to 100 do {allMissionObjects ""}
- hint str [diag_frameNo - _frameNo, diag_tickTime - _tickTime]
复制代码
看到了吧,.sqs代码与.sqf略有区别,这里我稍微解释一下。使得.sqs不同的是,它一行仅有一句代码。同一行无法用;(分号)来分隔表达式,因为分号被用来做注释。基本说来,你要在一行上处理好的你的表达式,如果做不到,你就需要使用.sqs命令,比如goto然后它就悲剧了。这里不能用sleep或者waiUntil命令。你要用~或者@代替。记住,你加入这些中断命令,这个脚本就无法一次完成。如果想加入某些等待,还是最好使用常规的.sqf文件。
此外,.sqs脚本有独特的变量_time,看起来是储存时间的,显示了.sqs脚本在开始运行前需要等待多久。我确定一定以及坑定这个适合用于性能分析。无论如何,关于两个脚本的区别,请看
https://community.bistudio.com/wiki/SQS_to_SQF_conversion
Edit:当我想用.sqs写些复杂的东西,我相信2件事:
1.对我来说.sqs限制太多,不适合我用。
2.你没必要非要写.sqs,但可以使用.sqs从一而终的功能
这就是我要搞的。在普通的SQF里和.sqs文件里我输入一行:
_this call KK_sqfFunctionIWrote
这个强迫你的sqf像sqs一样运行。它工作起来棒极了而且比SQS还快! |
|