krkr进阶教程1

 

从Initialize.tjs开始

进阶教程面向受众是已经基本弄明白了kag怎么用,此时开始出现各种各样只用kag无法实现的需求的同学们……

一般来说有编程基础的人自己扒拉扒拉帮助文档和system文件夹应该也就弄明白了,但是因为还存在着广大没有编程基础也弄不明白system文件夹的妹子们……所以我觉得大概还是有必要写这个东西的……

该教程为水螅制作,未经允许,请勿转载

首先说一下tjs和kag的关系,虽然玩krkr的人大部分都知道但是不排除有少部分人不知道的情况所以我再啰嗦一下……krkr指的是那个exe,system文件夹里那些tjs文件才是搭建起基础教程中kag部分的代码,kag的几乎全部指令都是在那些tjs文件中被定义的。所以下载krkr的时候会看到他的版本是krkr2kag3,就是说exe内核是krkr2,system文件夹里的那一套文件是kag3……内核和system版本乱搭配的话有时候是会出点问题的,比如有段时间kagexpress的某个版本会开始运行时先出来一个小黑块……那个就是内核和kag版本的匹配上有点问题导致的……

在这个教程里我就是主要讲解system文件夹里那些东西……了解这个结构的话,就可以随意的搭建起来各种需求的系统了(当然也要考虑到krkr的计算效率……)

以下内容皆以kagexpress为准可能和其他版本略有区别不过应该不会妨碍理解……(之所以用旧版的kagexpress举例是因为新版kag3ex2的结构已经改了很多而且我还没怎么看过……||||||当然这不妨碍看这篇的人一边用着kag3ex2一边理解system……)这个教程主要就是讲讲kag的结构,不会把system里的全部文件都讲一遍,主要也就是讲下到底应该怎么看这些tjs而已……开头会详细一些,后边就会跳跃性大一点或者直接坑掉(死)

另外以下语法都是tjs,和kag语法无关。我在讲解时默认看这教程的人都看过了那个叫做tjsdoc的帮助文档并且已经了解了tjs的语法……所以教程中基本不会出现对语法的解释了。
啊顺便说句,tjs的语法和js、as之流是很像的,某种意义上简直可以通用……(经常看到不懂行的人误导别人说tjs和java很像,我只想说“像你妹啊”,当然如果你会java的话想要上手tjs这种弱类型脚本语言那应该是极快的……)

在kag教程里我已经说过了,krkr.exe启动时首先读取的是startup.tjs,把这文件用任意文本编辑软件打开(强推notepad2……),如果是旧版的kagexpress的话它里边只有一行

Scripts.execStorage("system/Initialize.tjs");

这个Scripts.execStorage就是读取脚本文件并执行的意思,容我偷懒一下直接copy日版帮助的内容……后边的部分我就连日版帮助都不copy了直接大致用中文解释下意义,需要了解详细用法的同学请自行查阅kr2doc

Scripts.execStorage

機能/意味
ストレージ上のスクリプトの実行
タイプ
Scriptsクラスのメソッド
構文
execStorage(storage)
引数
storage 実行するストレージを指定します。
戻り値
スクリプトを実行した結果が戻ります。
説明
 指定されたストレージを読み込み、その内容を TJS2 スクリプトとして実行します。
 スクリプトを実行中に発生した例外はこのメソッド内では捕捉されませんので、このメソッドの 呼び出し側で捕捉することができます。

总而言之,这个startup.tjs只有一个功能,就是对exe说‘去system这个文件夹下找Initialize.tjs这个文件读取吧~’

另外,这里值得注意的是,system/Initialize.tjs这个路径指的不是游戏根目录下,而是游戏根目录下data文件夹里……表问我为什么会这样问exe去……

那么我们就跟着去打开Initialize.tjs,之前在kag教程里我们已经打开过他一次了……现在再重新来调戏他一遍……

首先看到的是

var kagVersion = "3.27-dev.20060219";

这个就是赋值版本号,如果你想知道自己用的哪个版本,在脚本里写入dm(kagVersion)他就会在控制台里打出来这个……

接下来就是这个dm是什么东西的定义……

var dm = Debug.message;

Debug.message是什么呢……他就是Debug的一个方法……用法就是 Debug.message('要打印出来的文字'),在你的脚本里写入这样一句的话,执行到这里时就会把‘要打印出来的文字’输出到控制台上,比如你写了个变量值递增循环,可以在循环里写入Debug.message(那个变量),就可以在控制台上看到那个变量在循环中每一次变化的值了,但是在这里,脚本把Debug.message赋值给了dm,也就是说直接写dm('要打印出来的文字')就可以了,不用写Debug.message这么长……(是的,这一句就只是简化了一下这个输出指令而已远目……)

再往下看,是一个function

 

System.exceptionHandler = function (e)
{
// どこにも捕捉されない例外がシステム側で捕捉された場合、この関数が
// 呼ばれる。e は例外オブジェクト。
if(e instanceof "ConductorException")
{
   // コンダクタの投げた例外の場合
   Debug.logAsError(); // ログのファイルへの書き出し動作の開始など
   var event_disabled = System.eventDisabled;
   System.eventDisabled = true;
    // エラーの理由を表示させている間にイベントが発生すると
    // やっかいなのでいったんイベント発生を停止させる
   System.inform(e.message);
   System.eventDisabled = event_disabled;
    // イベントを発生するかどうかを元の状態に
   return true; // true を返すと本体側で例外の処理は行わなくなる
}
else
{
   return false; // false を返すと通常の例外処理
}
};

 

为了防止有些人对这东西感到晕于是我稍微解释一下结构吧……System.exceptionHandler是一个System的属性,就如它的名字一样……它就是在游戏执行中报错的接口……值是true或者false,后边那个function (e)的结构就是返回一个true或者false的值

这一段是除错用的,当出现bug的时候就会开始记录要写在log文件里的东西,以及弹出来“xxx地方出错了”这样的窗口……(大家应该对这个不陌生……)

krkr遇到出错时会抛出一个Exception类的实体,在被conductor捕捉到后会被处理下然后conductor再抛出一个ConductorException类的实体,这东西被System.exceptionHandler捕捉到后就会执行那些弹出报错窗口啦写入log文件啦……

继续看下边的

 

 

 

function useArchiveIfExists(name)
{
// name が存在していたらそのアーカイブを使う
var arcname;
if(Storages.isExistentStorage(arcname = System.exePath + name))
   Storages.addAutoPath(arcname + ">");
}

 

这个函数的用处就是判断下传入的文件名的文件存不存在于根目录下,如果存在就将文件名加入文件检索路径,主要就是针对xp3文件的。

Storages.addAutoPath就是一个添加文件检索路径的方法,具体请查阅kr2doc

下边一直到

delete useArchiveIfExists;

之前都是在添加文件检索路径,添加完了之后就把这个函数delete了……

再往后是

Debug.notice("OS : " + System.osName + " (" + System.platformName + ")");
Debug.notice("KAG : " + kagVersion);
Debug.notice("Kirikiri : " + System.versionString);

Debug.notice就是把后边的字符串写入你在游戏里按ctrl+F12唤出的版权信息页面里边

再往后是

var parseStartTick = System.getTickCount();

这个是用来计算整个创建窗体用了多少时间的,System.getTickCount()是取得当前的毫秒数,现在取得一下,等下创建完再取得一下,用后者减去前者来得到创建窗体消耗的时间。

接下来是

function KAGLoadScript(name)
{
var start = System.getTickCount();
Scripts.execStorage(name);
dm("读取了" + name + " ,消耗时间(" + (System.getTickCount() - start) + "ms)");
}

这个function应该很好看懂,就是读取脚本文件然后顺便dm出来读取用的时间

var loaded_scripts = %[];

function KAGLoadScriptOnce(name)
{
// 指定したスクリプトを読み込むが、一回しか読み込まない
if(global.loaded_scripts[name] === true) return; // 既に読み込んでいる
global.KAGLoadScript(name);
global.loaded_scripts[name] = true;
}

这个function是传入的文件名如果在loaded_scripts里没有这个key就读取它然后把它的名字加入loaded_scripts这个字典里,如果loaded_scripts字典里已经有这个key了,那就跳过

接下来就开始具体的读取了

if(Storages.isExistentStorage("Config.tjs"))
{
KAGLoadScript("Config.tjs");
}
else
{
throw new Exception("找不到Config.tjs。");
}

好了,这里就是上边所说的出错时抛出Exception实体的例子了。

这段的意义不用多解释,很好懂,就是寻找有没有config.tjs,如果有就读取它,如果没有就报错

if(typeof global.config_version == "undefined" || config_version != kagVersion)
{
KAGLoadScript("UpdateConfig.tjs");
}

如果有config_version这个变量而且还和kagVersion的值不一致就读取UpdateConfig.tjs,config_version这个全局变量在kagexpress的config.tjs里被赋值为'',所以一定会读取UpdateConfig.tjs……

if(!System.createAppLock(System.exePath.replace(/[^A-Za-z]/g, '_')))
{
// すでに起動している
System.inform(System.title + "已经启动了。");
System.exit();
}

检测下如果已经启动了这个项目就弹出对话框然后关闭这个进程

property askYesNo { getter() { KAGLoadScript("YesNoDialog.tjs"); return global.askYesNo; } }
property CheckBoxLayer { getter() { KAGLoadScript("CheckBoxLayer.tjs"); return global.CheckBoxLayer; } }
//property ButtonLayer { getter() { KAGLoadScript("ButtonLayer.tjs"); return global.ButtonLayer; } }
property EditLayer { getter() { KAGLoadScript("EditLayer.tjs"); return global.EditLayer; } }
property SliderLayer { getter() { KAGLoadScript("SliderLayer.tjs"); return global.SliderLayer; } }
property KAGPlugin { getter() { KAGLoadScript("Plugin.tjs"); return global.KAGPlugin; } }
property execDialog { getter() { KAGLoadScript("ExecDialog.tjs"); return global.execDialog; } }

 

 

将常用的几个功能封装了一下便于调用,不过其实很少用到|||||||||

下边就是单纯的在load各个文件,顺便dm了下load用的时间之类的……

dm("KAG System 正在读取中...");

// add by GoWatanabe
KAGLoadScript("LayerEx.tjs");

KAGLoadScript("Utils.tjs");
KAGLoadScript("KAGLayer.tjs");

KAGLoadScript("ButtonLayer.tjs");

KAGLoadScript("SelectLayer.tjs");
KAGLoadScript("MapSelectLayer.tjs");
KAGLoadScript("DialogLayer.tjs");
KAGLoadScript("HistoryLayer.tjs");
KAGLoadScript("BGM.tjs");
KAGLoadScript("SE.tjs");
KAGLoadScript("Movie.tjs");
KAGLoadScript("Conductor.tjs");

// add by GoWatanabe
// AnimationLayer の下層に AffineLayer/ActionLayer を仕込む

KAGLoadScript("AffineLayer.tjs");
KAGLoadScript("ActionLayer.tjs");

KAGLoadScript("AnimationLayer.tjs");
KAGLoadScript("GraphicLayer.tjs");
KAGLoadScript("MessageLayer.tjs");
KAGLoadScript("Menus.tjs");
KAGLoadScript("DefaultMover.tjs");
KAGLoadScript("MainWindow.tjs");

if(Storages.isExistentStorage("Override.tjs"))
KAGLoadScript("Override.tjs");
if(Storages.isExistentStorage(System.exePath + "Override2.tjs"))
KAGLoadScript(System.exePath + "Override2.tjs");


/*
( デバッグ ) 時間計測
*/
dm("脚本读取消耗了 " + (System.getTickCount() - parseStartTick) + "ms 的时间");
parseStartTick = System.getTickCount();

然后,才是真正的创建了我们平时看到的窗口

global.kag = new KAGWindow() if typeof global.kag == "undefined";

就是他了……kag……这就是主窗口的名字……

因为有人问到为什么这个kag可以不用var就直接用,所以我也在这里再解释一遍

krkr对变量声明要求的不严格,就是说如果你var aa这样声明了一个全局变量,那么你可以用global.aa调用它,但是如果你没有声明aa,直接写global.aa=1,那么他会像字典一样自己创立这个项,就是说不声明也可以,其实global就是个字典…… 

var f = kag.flags;   // ユーザ変数 (フラグ)
var sf = kag.sflags; // システム変数 (システム)
var tf = kag.tflags; // 一時変数 (一時フラグ)

property mp
{
getter { return kag.conductor.macroParams; }
}

创建了f、sf、tf三个字典用来容纳游戏变量,封装了mp这个属性,这些应该对用kag的人来说很亲切了……

dm("KAGMainWindow 的创建消耗了 " + (System.getTickCount() - parseStartTick) + "ms 的时间");
delete parseStartTick;

这里就是单纯dm了下窗口创建用的时间

if(Storages.isExistentStorage("AfterInit.tjs"))
KAGLoadScript("AfterInit.tjs");
if(Storages.isExistentStorage(System.exePath + "AfterInit2.tjs"))
KAGLoadScript(System.exePath + "AfterInit2.tjs");

有AfterInit.tjs和AfterInit2.tjs这两个文件的话就load他们一下……

{
var ovr = System.getArgument('-ovr');
if(ovr !== void && ovr != 'yes') Scripts.eval(ovr);
}

ovr有东西的话就执行一下……

kag.process("func.ks");

跳转到func.ks这个文件,这句之后就是kag的领域了,func.ks的最后写着跳转到first.ks

到此Initialize.tjs就结束了。这个文件的主要作用除了载入了一遍其他文件外,就是建立了一个叫做kag的KAGWindow类的实体。而这个KAGWindow类到底是什么样的东西……这些全部写在那个叫做mainwindow.tjs的文件里……



TOP

访客数: 3390405
aa