`
252190908
  • 浏览: 228291 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多


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

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

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!

欢迎大家参与

分享到:
评论

相关推荐

    JAVA上百实例源码以及开源项目

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...

    java源码包---java 源码 大量 实例

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...

    asp.net知识库

    与DotNet数据对象结合的自定义数据对象设计 (一) 数据对象与DataRow ASP.NET中大结果集的分页[翻译] .net 2.0 访问Oracle --与Sql Server的差异,注意事项,常见异常 Ado.net 与NHibernate的关系? 动态创建数据库...

    JAVA上百实例源码以及开源项目源代码

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Java从网络取得文件 1个目标文件 简单 Java从压缩包中提取文件 1个目标文件 简单 Java存储与读取对象 1个目标文件 如题 Java调色板面板源代码 1个目标文件 摘要:Java源码,窗体界面,调色板 使用Java语言编写的一款...

    成百上千个Java 源码DEMO 3(1-4是独立压缩包)

    Java从网络取得文件 1个目标文件 简单 Java从压缩包中提取文件 1个目标文件 简单 Java存储与读取对象 1个目标文件 如题 Java调色板面板源代码 1个目标文件 摘要:Java源码,窗体界面,调色板 使用Java语言编写的一款...

    java源码包2

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...

    易语言程序免安装版下载

    修改扩展界面支持库三,解决高级选择夹会导致所在窗口的收不到“首次激活”事件的BUG,相应地修改了核心库和开发环境。 10. 为所有支持库文件统一添加了版本信息。 ------------------------------------------...

    PHP和MySQL Web开发第4版pdf以及源码

    2.7.5 读取一个字符:fgetc() 2.7.6 读取任意长度:fread() 2.8 使用其他有用的文件函数 2.8.1 查看文件是否存在:file_exists() 2.8.2 确定文件大小:filesize() 2.8.3 删除一个文件:unlink() 2.8.4 在文件...

    java源码包3

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...

    java源码包4

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...

    cmd操作命令和linux命令大全收集

    arp 查看和处理ARP缓存,ARP是名字解析的意思,负责把一个IP解析成一个物理性的MAC地址。arp -a将显示出全部信息 start 程序名或命令 /max 或/min 新开一个新窗口并最大化(最小化)运行某程序或命令 mem 查看cpu...

    java开源包1

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包10

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    JavaScript王者归来part.1 总数2

     10.5.3 构造新的文法--一个在JSVM中实现JSVM2解析器的例子   10.6 高级用法   10.7 用正则表达式处理文本   10.7.1 创建一个计价公式编辑器   10.7.1.1 需求分析--什么是计价公式编辑器   10.7.1.2 系统...

    PHP和MySQL WEB开发(第4版)

    2.7.5 读取一个字符:fgetc() 2.7.6 读取任意长度:fread() 2.8 使用其他有用的文件函数 2.8.1 查看文件是否存在:file_exists() 2.8.2 确定文件大小:filesize() 2.8.3 删除一个文件:unlink() 2.8.4 在文件中定位...

    Java核心技术II(第8版)

    第一章 流与文件 1.1 流 1.1.1 读入和写出字节 1.1.2 完整的流家族 1.1.3 组合流过滤器 1.2 文本输入与输出 1.2.1 如何写出文本输出 1.2.2 如何读入文本输入 1.2.3 以文本格式存储对象 1.2.4 字符集 1.3 读入和写出...

    java开源包11

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

Global site tag (gtag.js) - Google Analytics