Torque脚本启动调用流程

经常有朋友问如何学习TGE脚本(TGEA的脚本大致与TGE相同).一方面是看书和官网的文档,另外最有效最重要的就是看引擎所带的那几个范例了.

初学者看到那么一大堆脚本文件,肯定会有疑问,为什么某些代码要添加在特定的地方呢?各级子目录中有 许多脚本文件,他们是按什么顺序执行的呢?最终又是如何进入游戏场景的呢?

为了解答这些问题,需要研究TGE脚本的运行流程。了解了脚本流程后,更加利于研究example下自带的例子的代码,从中吸取经验,学习。

这是以前写的一篇文档,引擎版本为TGE1.5.2.整理了放在blog上,希望对大家有点用.

在介绍Torque脚本流程之前.先介绍一些基本知识。Torque Script是Torque自带的一个脚本语言,C/C++风格,面向对象,语法规范和C/C++类似,解释执行。

脚本中一个很重要的命令的exec,这个命令的作用是执行脚本文件。如下:

A遇到语句则执行。

B遇到对象或是函数定义则加载(这个命名是我自己这么称呼的,不准确-。-!)。也就是你要使用你定义的函数或是对象,必须先用exec将包含相应定义的文件载入,然后才可以使用。

我把脚本的流程分为两个部分. 一是引擎(TGE.exe)启动后,会加载根目录下的main.cs,执行,然后根据main.cs开始加载执行整个游戏的脚本代码----脚本初始化流程

二是当进入游戏窗口时,出现主界面,点击确定后,载入地形,进入游戏场景,开始游戏逻辑等----游戏逻辑启动


一 根目录下的”main.cs”开始,脚本初始化流程

我们从根目录的main.cs开始分析。因为当TGE.exe启动时,会自动加载根目录下的“main.cs”,所以根目录下的“main.cs”是脚本的起始点。


开始都是一些变量的赋值与函数定义,跳过。比较重要的变量赋值语句如下:

$defaultGame = "tutorial.base";

$userMods = "creator;" @ $defaultGame;

此时$userMods的值为"creator;tutorial.base "


然后loadMods($userMods);这个语句开始加载mod,重要。

见函数定义:


function
 loadMods(%modPath)
{
   
%modPath = nextToken(%modPath, token, ";");
    if (%modPath !$= "")
     loadMods(
%modPath);
   
if(exec(%token @ "/main.cs") != true)
    {
      
error("Error: Unable to find specified mod: " @ %token );
        $modcount
--;
    }
}

nextToken的定义如下:


Tokens

nextToken( tokenList , tokenVar , delimeter )

Purpose

Use the nextToken function to get the first token found in tokenList, where tokens are

separated by the character(s) specified in delimeter. The token itself is stored in a

variable whose name is specified in tokenVar. This function provides complex power in a

simple package. Please read the notes below, they are very important.

Syntax

tokenList  The string containing token(s).

tokenVar  The 'name' of the variable to store the token in.

delimeter  The character(s) to use as a delimeter. A delimeter may be a single

character, or a sequence of characters.

Returns

Returns a copy of tokenList, less the first token and the first delimiter. If there are

no more tokens, a NULL string is returned.

因此递归调用loadMods,逆序执行$userMods中的各个mod,即先执行“tutorial.base”,后执行“creator”,又见“tutorial.base”的main.cs,发现开头有:

loadDir("common");

    加载任何一个mod都会先加载common。

因此各个mod中的main.cs的调用顺序如下

Exec common  main.cs

Exec tutorial.base main.cn

Exec common main.cs

Exec creator main.cs

在这里我们要介绍一下package。

注意区别mod 和package。mod是一个目录,每个mod目录下都会有一个main.cs,为了方便起见,我们把根目录称为根mod(虚mod)。每个mod的main.cs中都定义有一个Package,它是一个结构,可以认为是一堆函数定义的集合。如common的main.cs中有:

package Common {

function displayHelp() {}

function parseArgs(){}

function onStart(){}

    function onExit(){}

};

又如tutorial.base的main.cs

package ttb

{

function onStart(){}

function onExit(){}

};

    Package的工作原理类似栈。但使用ActivatePackage命令激活一个包时,就会将这个包压入栈。例如你调用onStrat()函数时,调用的就是栈顶的那个包中的onStrat()函数定义。

可以看到,不同mod下的包的函数名都是相同的。所以我认为package可以提供函数的“继承”“和多态”。通过ActivatePackage与deactivatePackage来控制当前栈顶的package.同时也可以通过Parent::来调用栈中上一级包中定义的同名函数。

看完了包之后,我们继续回到脚本流程。各个mod的main.cs的执行顺序如上,但是加载mod(实质是指加载package)顺序不是这样的。

关键在于那个ActivatePackage,当激活一个package时,这个package就被压入栈,但是如果栈中已有同名的package时,则不会将同名的package压入栈。

所以common包只加载一次。最终栈中的package的顺序如下,creator在栈顶:

commontutorial.basecreator


继续看代码:

if ($displayHelp)
{
   enableWinConsole(
true);
   displayHelp();
   quit();
}
else 
{
   onStart();   
//各个mod的起点
   echo(
"Engine initialized");
}


   表面上看此处的onStart()调用的是creator包中的onStart(),因为creator位于栈的顶端。但是并不是这样的,见creator包中的onStart()定义:

function onStart()
{
    Parent::onStart();
    …
}

通过Parent::onStart(),它会先调用栈中上一级包中的onStart(),即tutorial.base包中的onStart。而tutorial.base中的onStart的定义也有Parent::onStart();...

所以实际上各个mod的的onStart()的调用顺序为:

根mod的onStart()--commontutorial.basecreator

而具体每个包的流程从他们的onstrar开始~~~~

    各个mod的onStart()大家可以自己去看,多为加载脚本,初始化,并没有什么特别的功能函数。这里注意下tutorial.base的onStart()

    onStart()

      -->initClient()

    加载GUI文件的语句就放在这里。


二 游戏逻辑启动

在开始之前,再总结一下TGE脚本的重载。有两种,一种是利用package的函数重载。多个package中定义的同名函数,调用的是当时激活的package的函数。但是也可以通过parent来调父级的函数。二是依靠exec的顺序。如果两个同名的函数先后被exec,那么后被exec的函数有效。例如:common/server/ game.cs  tutorial.base/server/game.cs中均有onServerCread(),则执行的是tutorial.base下的。因为tutorial.base下的脚本文件后被exec.

这个流程的启动是由主界面上的按钮enter调用loadMyMission()函数开始的,我们从这个函数开始:tutorial.base/main.cs

function loadMyMission()
{
   
// make sure we are not connected to a server already
   disconnect();

   
// Create the server and load the mission
   createServer(
"SinglePlayer", expandFilename("./data/missions/flat.mis"));

   
// Make a local connection
   
%conn = new GameConnection(ServerConnection);
   RootGroup.add(ServerConnection);
   
%conn.setConnectArgs("Player");
   
%conn.setJoinPassword("None");
   
%conn.connectLocal();
}

通过调用这个函数,开始创建服务器,创建客户机,并让客户机连接服务器,开始游戏.

(1) 下面开始分析,侧重control包,common包对于初学者来说,并不需要投入太多精力去研究,仅供参考.

(2) 初学者并不要求对整个流程都了解,只需了解需要加的功能的脚本,和插入点即可.

(3) 另有些函数在common与T中均有定义,重载.若T中有定义则执行T,若无则执行common中的.这种函数的重载与package的重载不同,package可通过parent::来调用父级的函数定义.


createServer("SinglePlayer",expandFilename("./data/missions/flat.mis"));


createrserver()     //位于Serve: common/server/server.cs

-->destroyServer()    //先销毁服务器

-->onServerCread()  //两处重载: common/server/ game.cs  tutorial.base/server/game.cs

                    //执行的是tutorial.base下的.作用是加载游戏逻辑的脚本(exec)。(在tutorial.base中看不出来,因为它是最简的,可以参starter.fps中的例子)

-->loadMission()    //服务器加载地形,位于Server: common/server/missionload.cs

 -->endmission()   //先结束

  -->将mission信息发送给客户端

  -->loadMissionStage2()

    -->onMissionLoaded() //两处:common/sever/game.cs,game/sever/game.cs 

                         //在tutorial.base重要,可放置游戏逻辑脚本(参fps或别的游戏的例子),调用GameMgr启动,逻辑代码启动.


2

%conn = new GameConnection(ServerConnection);

RootGroup.add(ServerConnection);

%conn.setConnectArgs("Player");

%conn.setJoinPassword("None");

%conn.connectLocal();

创建一个客户端的连接,并将这个连接连到服务端上,开始下载mission到客户机上,开始游戏。

这个函数会引起引擎执行一系列函数。最后回调脚本的GameConnection::onConnectRequest()----从这里开始下载mission到客户机。

    暂略:涉及到common及引擎源码层,较复杂,以后有空再说吧


最终到达:

Server: common/server/missionload.cs


serverCmdMissionStartPhase3Ack()

-->GameConnection::startMission()  位于Server:common/server/clientconection.cs

-->GameConnection::OnClientEnterGame()

位于Server:game/server/game.cs中,重要,用于新建玩家,摄像机等.

 

posted on 2009-02-01 20:05  chsaov  阅读(984)  评论(0编辑  收藏  举报

导航