上一篇《游戏脚本的设计与开发》-序中我介绍了游戏脚本的基本概念和准备工作,本篇来说说具体如何解析一个脚本

所谓解析脚本,就是按照自己定义的语法,将每一个脚本命令还原成不同的代码逻辑进行执行,比如,我规定绘制一个矩形的脚本

draw rect

和一个绘制圆的脚本

draw arc

那么,当我读取到了字符串“draw rect”的时候,就在屏幕上画一个矩形,读取到了字符串“draw arc”的时候,就在屏幕上画一个圆。

如果你已经按照上一篇序中的说明安装了xampp,那么请找到xampp中的htdocs文件夹,在里面新建一个lsharp文件夹,然后再按照下面结构新建一些文件和文件夹
lsharp
    |-script文件夹
    |-index.html
    |-Main.js
    |-lufylegend.lsharp.js
    |-lufylegend-1.7.5.js
index.html文件代码如下

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>L#</title>
</head>
<body>
<div id="mylegend">loading......</div>
<script type="text/javascript" src="./lufylegend-1.7.5.js"></script>
<script type="text/javascript" src="./lufylegend.lsharp.js"></script>
<script type="text/javascript" src="./Main.js"></script>
</body>
</html>

注意:lufylegend-1.7.5.js你需要自己下载。
解析脚本,需要建立一个解析脚本的函数analysis,每解析完一个脚本,就返回调用一次analysis函数,让解析不断的进行,直到脚本文件全部解析结束。
首先,在lufylegend.lsharp.js文件中新建一个LScript.js类,作为脚本解析对象,用来控制脚本的解析和执行。

/** LScript.js
**/
function LScript(scriptLayer,value){var self = this;LGlobal.script = self;self.scriptLayer = scriptLayer;self.dataList = new Array();var arr=[value];self.dataList.unshift(arr);self.toList(value);
}

代码解析:
LScript.js类接受两个参数,第一个参数scriptLayer是显示层,也是使用L#脚本制作游戏的最底层画板,之后的脚本命令的所有绘图都将绘制到这个显示层上,第二个参数是一个字符串脚本,也是LScript.js对象被创建后立即会被解析的脚本。

LGlobal.script = self;

LGlobal.script将脚本解析对象保存起来,方便回调脚本的解析函数。

self.scriptLayer = scriptLayer;

将显示层保存起来,方便以后使用。

self.dataList = new Array();
var arr=[value];
self.dataList.unshift(arr);

保存脚本到缓存数组。

self.toList(value);

这一行是将整个字符串脚本进行分解,得到单个的脚本命令数组。
看一下toList函数:

toList:function(ltxt){var self = this;self.lineList = ltxt.split(";");self.copyList = self.lineList.slice(0);self.analysis();
}

代码解析:

self.lineList = ltxt.split(";");

在L#中,我设定了各个的脚本指令之间是以分号来分割的,所以使用字符串的split函数对字符串进行分割,得到单个的脚本命令数组,并将其存入到脚本解析对象的lineList。

self.copyList = self.lineList.slice(0);

copyList用来保存当前所执行的lineList,当有新的脚本命令lineList被添加进来的时候,当前的lineList就会被保存起来,当新的脚本命令lineList全部解析完之后,就会对上一个未被解析完的脚本命令继续进行解析执行。

self.analysis();

开始解析脚本命令。
下面看一下关键的脚本解析函数analysis。

analysis:function(){var self = this;var lineValue = "";if(self.lineList.length == 0){self.dataList.shift();if(self.dataList.length > 0){arr=self.dataList[0];self.lineList = arr[1];self.copyList = arr[2];self.analysis();}return;}while(self.lineList.length > 0){lineValue = LMath.trim(self.lineList[0]);self.lineList.shift();}if(lineValue.length == 0){self.analysis();return;}trace("analysis lineValue = " + lineValue);var sarr = lineValue.split(".");switch(sarr[0]){default:self.analysis();}
}

代码解析:

if(self.lineList.length == 0){self.dataList.shift();if(self.dataList.length > 0){arr=self.dataList[0];self.lineList = arr[1];self.copyList = arr[2];self.analysis();}return;
}

上面代码判断lineList数组内的脚本指令是不是被解析完,如果被解析完了的话,检查一下dataList数组中是否有其他未被执行的脚本,有则继续执行。

var lineValue = "";
while(self.lineList.length > 0 && lineValue.length == 0){lineValue = LMath.trim(self.lineList[0]);self.lineList.shift();
}

上面代码用来获取一个lineList数组中的脚本指令

if(lineValue.length == 0){self.analysis();return;
}

如果当前脚本指令为空,则执行下一条指令

trace("analysis lineValue = " + lineValue);

这行代码用来debug,发布的时候可以不要

var sarr = lineValue.split(".");
switch(sarr[0]){default:self.analysis();
}

在L#中,所有被执行的脚本指令,都是[类.命令]的格式,如果你在设定脚本的时候使用了其他格式,则请根据你的格式来做相应的处理。所以使用split函数,对每一条指令进行分割,获取需要的信息,进行解析。
接着,我们在Main.js中加入以下代码

init(50,"mylegend",400,100,main);
function main(){LGlobal.setDebug(true);var sc = "aaa;Load.script(script/Main.ls);bbb;";var sp = new LSprite();addChild(sp);var script = new LScript(sp,sc);
}

那么脚本解析类会对["aaa;Load.script(script/Main.ls);bbb;"]这个脚本进行解析,里面的脚本指令是我随便写的,并非正确的指令。
运行一下代码,

你本地的执行URL为http://localhost/lsharp/index.html

看看debug函数会输出的信息,如下

图1

可以看到,解析类对脚本进行了解析,按照分号将脚本分割为了三个指令,那么接下来就需要增加对每一条指令的解析,当出现了无法解析的指令的时候,会自动跳过对下一个指令进行解析。
下面,先来进行第一个脚本指令的解析,使用这个脚本来读取一个脚本文件。
首先,我规定,在L#中读取一个脚本文件的语法如下

Load.script(script/Main.ls);

并且在script文件夹中新建一个Main.ls文件,用记事本打开Main.ls文件写入以下内容

Text.label(-,txt,测试a,0,0,30,#000000);
Text.label(-,txt,测试b,0,0,30,#000000);

接着,修改解析函数中的switch部分,如下

switch(sarr[0]){case "Load":ScriptLoad.analysis(lineValue);break;default:self.analysis();
}

所以,现在需要一个静态类ScriptLoad,来对Load指令进行解析,新建一个ScriptLoad类,如下

/*
* ScriptLoad.js
**/
var ScriptLoad = function (){};
ScriptLoad.data = "";
ScriptLoad.urlloader = null;
ScriptLoad.analysis = function (value){var start = value.indexOf("(");var end = value.indexOf(")");ScriptLoad.data = value.substring(start+1,end).split(",");switch(LMath.trim(value.substr(0,start))){case "Load.script":ScriptLoad.loadScript();break;default:LGlobal.script.analysis();}
};

ScriptLoad类的解析函数analysis中,首先将脚本中括号外和括号内的内容[Load.script]和[script/Main.ls]分解出来,然后再利用switch函数进一步解析。当遇到字符串“Load.script”的时候,调用loadScript函数来读取一个脚本文件。
看下面的代码

ScriptLoad.loadScript = function (){ScriptLoad.urlloader = new LURLLoader();ScriptLoad.urlloader.addEventListener(LEvent.COMPLETE,ScriptLoad.loadScriptOver);ScriptLoad.urlloader.load(ScriptLoad.data[0],"text");
};
ScriptLoad.loadScriptOver = function (event){var script = LGlobal.script;var data = event.target.data;ScriptLoad.urlloader.die();ScriptLoad.urlloader = null;script.saveList();script.dataList.unshift([data]);script.toList(data);
};

代码解析
loadScript函数比较简单,就是使用LURLLoader对象来读取一个文件,读取完之后调用loadScriptOver函数。
loadScriptOver函数中利用下面三行代码,将读取进来的内容存入脚本解析类的数组中,进行下一步解析。

script.saveList();
script.dataList.unshift([data]);
script.toList(data);

其中的saveList函数如下

saveList:function(){var self = this;var arr=self.dataList[0];if(arr){arr[1]=self.lineList;arr[2]=self.copyList;}
}

所做的处理就是前面所说的,将当前正在执行的脚本数组保存起来

运行代码,debug输出以下信息

图2

可以看到,Main.ls脚本文件中的脚本指令都被解析了出来,只是Text.label这个脚本我们还没有对其进行解析,所以只是将其跳过了。
其实,我们还可以在脚本文件中再读取脚本文件,比如将Main.ls中的内容换成下面:

Text.label(-,txt,测试a,0,0,30,#000000);
Load.script(script/test.ls);
Text.label(-,txt,测试b,0,0,30,#000000);

并且,再添加一个test.ls脚本文件,test.ls中的内容如下

Text.label(-,txt,测试c,0,0,30,#000000);

运行一下程序,看debug输出如下信息:

图3

可以看到,test.ls中的脚本指令也被读取出来了。

测试连接

http://lufylegend.com/demo/test/lsharp/01/index.html

关于源码
目前代码比较少,我就直接贴出来了
Main.js
init(50,"mylegend",400,100,main);
function main(){LGlobal.setDebug(true);var sc = "Load.script(script/Main.ls);";var sp = new LSprite();addChild(sp);var script = new LScript(sp,sc);
}

lufylegend.lsharp.js

/*
* LScript.js
**/
function LScript(scriptLayer,value){var self = this;LGlobal.script = self;self.scriptLayer = scriptLayer;self.dataList = new Array();var arr=[value];self.dataList.unshift(arr);self.toList(value);
}
LScript.prototype = {toList:function(ltxt){var self = this;self.lineList = ltxt.split(";");self.copyList = self.lineList.slice(0);self.analysis();},saveList:function(){var self = this;var arr=self.dataList[0];if(arr){arr[1]=self.lineList;arr[2]=self.copyList;}},analysis:function(){var self = this;var arr;if(self.lineList.length == 0){self.dataList.shift();if(self.dataList.length > 0){arr=self.dataList[0];self.lineList = arr[1];self.copyList = arr[2];self.analysis();}return;}var lineValue = "";while(self.lineList.length > 0 && lineValue.length == 0){lineValue = LMath.trim(self.lineList[0]);self.lineList.shift();}if(lineValue.length == 0){self.analysis();return;}trace("analysis lineValue = " + lineValue);var sarr = lineValue.split(".");switch(sarr[0]){case "Load":ScriptLoad.analysis(lineValue);break;default:self.analysis();}}
};
/*
* ScriptLoad.js
**/
var ScriptLoad = function (){};
ScriptLoad.data = "";
ScriptLoad.urlloader = null;
ScriptLoad.analysis = function (value){var start = value.indexOf("(");var end = value.indexOf(")");ScriptLoad.data = value.substring(start+1,end).split(",");switch(LMath.trim(value.substr(0,start))){case "Load.script":ScriptLoad.loadScript();break;default:LGlobal.script.analysis();}
};
ScriptLoad.loadScript = function (){ScriptLoad.urlloader = new LURLLoader();ScriptLoad.urlloader.addEventListener(LEvent.COMPLETE,ScriptLoad.loadScriptOver);ScriptLoad.urlloader.load(ScriptLoad.data[0],"text");
};
ScriptLoad.loadScriptOver = function (event){var script = LGlobal.script;var data = event.target.data;ScriptLoad.urlloader.die();ScriptLoad.urlloader = null;script.saveList();script.dataList.unshift([data]);script.toList(data);
};

本章就讲到这里,下一章我们真正的进入正题,一起来hello world!


最后做个广告,有奖征集活动系列——【HTML5游戏编程之旅】
欢迎大家参与
http://blog.csdn.net/blogdevteam/article/details/8899926

《游戏脚本的设计与开发》-1.1 读取和解析一个脚本文件相关推荐

  1. 《游戏脚本的设计与开发》-(RPG部分)3.8 通过脚本来自由控制游戏(一)

    注意:本系列教程为长篇连载无底洞,半路杀进来的朋友,如果看不懂的话,请从第一章开始看起,文章目录请点击下面链接. http://blog.csdn.net/lufy_legend/article/de ...

  2. 《游戏脚本的设计与开发》-目录序

    本系列文章目录 章节 标题 连接 序 游戏脚本简介 http://blog.csdn.net/lufy_legend/article/details/8888787 第一章 基本功能 1.1 读取和解 ...

  3. 《游戏脚本的设计与开发》-第一部分总结 文字脚本的功能扩展和一个游戏测试...

    脚本系列文章写了好几篇了,大家可能都不清楚这些脚本有什么用,游戏中如何能应用到这些东西.当然,目前所介绍的内容还只是个简单的开头,说到做游戏还远远不够.不过,本次就使用前几章所介绍的内容,先来尝试一下 ...

  4. 《游戏脚本的设计与开发》-(RPG部分)3.6 队员列表和人物属性

    注意:本系列教程为长篇连载无底洞,半路杀进来的朋友,如果看不懂的话,请从第一章开始看起,文章目录请点击下面链接. http://blog.csdn.net/lufy_legend/article/de ...

  5. 《游戏脚本的设计与开发》-(RPG部分)3.1 RPG地图到底怎么做?

    http://blog.csdn.net/lufy_legend/article/details/17417085 话说好久没有更新博客了,其实这段时间主要是工作忙,没时间.那又是什么刺激了我呢,为什 ...

  6. 《游戏脚本的设计与开发》-(RPG部分)3.5 游戏背包和任务系统

    注意:本系列教程为长篇连载无底洞,半路杀进来的朋友,如果看不懂的话,请从第一章开始看起,文章目录请点击下面链接. http://blog.csdn.net/lufy_legend/article/de ...

  7. 《游戏脚本的设计与开发》-第一章总结 文字脚本的功能扩展和一个游戏测试

    脚本系列文章写了好几篇了,大家可能都不清楚这些脚本有什么用,游戏中如何能应用到这些东西.当然,目前所介绍的内容还只是个简单的开头,说到做游戏还远远不够.不过,本次就使用前几章所介绍的内容,先来尝试一下 ...

  8. 《游戏脚本的设计与开发》-(RPG部分)3.4 地图跳转

    注意:本系列教程为长篇连载无底洞,半路杀进来的朋友,如果看不懂的话,请从第一章开始看起,文章目录请点击下面链接. http://blog.csdn.net/lufy_legend/article/de ...

  9. 《游戏脚本的设计与开发》-1.6 按钮,脚本的暂停和标签

    按钮 按钮在任何程序中都是必不可少的,本次先来看看如何脚本来实现按钮的各种功能.文章中要实现的几个脚本如下. /* 游戏脚本的设计与开发 第六章 */ //添加按钮 Button.add(layer0 ...

最新文章

  1. 自动驾驶高清地图问题的研究
  2. matplotlib之plot
  3. 关于STM32与SD卡通信的一些思考与总结
  4. java环境安装之不能安装exe文件
  5. mysql设置user_name唯一_mysql如何设置唯一性
  6. 线程死锁 解决共享资源问题
  7. ssh报错java.lang.ClassCastException: com.sun.proxy.$Proxy6 cannot be cast to org.service.impl.EmpServi
  8. fiddler修改支付金额_不容忽视的记账工具:支付宝记账
  9. 【youcans 的 OpenCV 例程 200 篇】103. 陷波带阻滤波器消除周期噪声干扰
  10. my-innodb-heavy-4G.cnf 配置文件参数介绍
  11. c语言一维数组定义可用字母吗,C语言一维数组的定义和引用
  12. 7-2 定义日期类 (28 分)
  13. Ubuntu Server 16.04服务器版配置图解教程14 - 安装PHP7.1.11(源码包安装)
  14. 计算机机房运行环境条件要求,机房环境都有哪些要求
  15. 微信域名如何防封?微信域名被封了怎么办?微信域名被封能够恢复吗?_如何微信防封域名拦截检测
  16. 【每天更新】2022年最新WordPress主题下载(2022-5-12)
  17. (SEED-Lab) TCP/IP Attack Lab
  18. Java生鲜电商平台-订单架构实战
  19. 【BUAA_CO_LAB】计组p2碎碎念
  20. 从SOD到OOD(MotorMCU模块)

热门文章

  1. 有什么好用的拼图软件?这几种方法都可以轻松拼图
  2. html5穿插动图,视频中间插播广告 在视频中的任意位置添加广告图片,视频中穿插图片的方法...
  3. 广汽本田定价混乱,让消费者对于凌派和飞度无所适从
  4. mysql 中的包含函数
  5. Adobe Flash CS3无法安装的解决方法
  6. C语言有符号数和无符号数的判断方法
  7. B3402 [Usaco2009 Open]Hide and Seek 捉迷藏 最短路
  8. 保姆级从Web of sciense下载论文并导入到Endnote
  9. 逛 CSDN论坛的发现
  10. Linux双系统硬盘识别不了,Linux和win8双系统磁盘错误问题