中国虚拟军事网(VME)- 专注于武装突袭系列虚拟军事游戏

 找回密码
 加入VME

QQ登录

只需一步,快速开始

搜索
查看: 2341|回复: 4

[转帖] 【渣翻译】KillKid zone博客文章-----。sqf和.sqs还有调度器

[复制链接]
发表于 2015-3-26 07:25:46 | 显示全部楼层 |阅读模式
        渣翻译,本人无任何编程经验,所以很多基本术语纯脑补直翻,若有任何错误请诸位指正。

阿妈脚程:.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用起来将很方便。

接下来是我用来测试的代码:
  1. [] spawn {
  2.     while {true} do {
  3.         allMissionObjects "";
  4.     };
  5. };
  6. player addAction ["VM", "vm.sqf"];
  7. player addAction ["nonVM", "nonvm.sqs"];

  8. //vm.sqf
  9. _frameNo = diag_frameNo;
  10. _tickTime = diag_tickTime;
  11. for "_i" from 1 to 100 do {
  12.     allMissionObjects "";
  13. };
  14. hint str [diag_frameNo - _frameNo, diag_tickTime - _tickTime];

  15. //nonvm.sqs
  16. _frameNo = diag_frameNo
  17. _tickTime = diag_tickTime
  18. for "_i" from 1 to 100 do {allMissionObjects ""}
  19. 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还快!
发表于 2015-3-26 09:12:26 | 显示全部楼层
其实只要用call来调用预编译过的脚本,都是超快的。
这也是为啥《疯狂戴夫和他的重量里》强烈地鼓励大家把常用的脚本都扔进CfgFunctions里,从而在游戏开始\任务载入开始时编译好了再call。这样比execVM减少了读盘、再编译的麻烦。比如:
  1. //在 config.cpp 中
  2. class CfgFunctions
  3. {
  4.         class AGM_Armour
  5.         {
  6.                 class AGM_Armour
  7.                 {
  8.                         file = "AGM_Armour\functions";
  9.                         class cookOff;
  10.                         class getHitPointBySelection;
  11.                         class handleDamage;
  12.                         class init;
  13.                 };
  14.         };
  15. };
复制代码

另一种的方法是把自己的函数写进在某一段别的脚本里,这样当那段脚本被执行时,你的函数就会在那是被编译进内存。缺点是这样会延长该脚本的执行时间。比如
  1. //在 init.sqf 中
  2. //......
  3. //......
  4. my_fnc_helloWorld =
  5. {
  6.         player sideChat "HelloWorld";
  7. };
  8. //......
  9. //......
复制代码

你的红字的一段翻译的不对。
原文:
So this is what I did. I wrote my function elsewhere in normal SQF and in .sqs file I put a single line:

_this call KK_sqfFunctionIWrote

What it does is it forces your SQF script behave like SQS script. Works like a charm and faster than SQS!

实际上它的意思是:KK先把他的sqf函数写进了某地(即确保他的函数已经经过了编译,留在了内存里),然后再在sqs中call调用他的函数。只要是用sqs调用的函数,那么不论函数本身是什么语言写的(经过了编译都变成了字节码),运行起来都会有sqs一撸到底、不执行完就霸着cpu不休的特点(你是不希望在里面扔几千个循环或者一堆延时的),同样的,只要是用sqf调用的函数,那么论函数本身是什么语言写的,执行起来都会有分时执行的特性,你可以放心地spawn一坨延时和循环,不必太担心会卡得别的脚本都动不了。
 楼主| 发表于 2015-3-26 12:27:33 | 显示全部楼层
gpgpgpgp 发表于 2015-3-26 09:12
其实只要用call来调用预编译过的脚本,都是超快的。
这也是为啥《疯狂戴夫和他的重量里》强烈地鼓励大家把 ...

感谢提醒,其实这种东西应该你们这种内行人来搞。很多东西直译不通,意译我的表述能力差劲。不过大体上KK讲啥希望看过的人脑子里有个概念,足以。
发表于 2015-3-26 13:25:58 | 显示全部楼层
chaoduichen 发表于 2015-3-26 12:27
感谢提醒,其实这种东西应该你们这种内行人来搞。很多东西直译不通,意译我的表述能力差劲。不过大体上KK ...

理论上是这么说不错。但是实际上内行的人都已经知道该怎么搞,小白就算看了也没用,而介于小白和内行之间的中等水平,翻译对他们有零碎的帮助,但是整体而言,不懂英文注定难翻过这道坎。所以在现在中等水平人群比较少的情况下,很少有人去翻译技术文章,有的也都去翻译小白教程了。:P
 楼主| 发表于 2015-3-26 18:05:02 | 显示全部楼层
gpgpgpgp 发表于 2015-3-26 13:25
理论上是这么说不错。但是实际上内行的人都已经知道该怎么搞,小白就算看了也没用,而介于小白和内行之间 ...

恩,看来下次翻点小白教程更好,入门后自己学起来应该更容易把。如果有大神写个全景指导,类似Mr.Murry搞的那种,或许对所有人都有帮助。零零碎碎的东西实在麻烦。
您需要登录后才可以回帖 登录 | 加入VME

本版积分规则

小黑屋|中国虚拟军事网

GMT+8, 2024-4-23 14:03

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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