《游戏脚本的设计与开发》-1.3 基础语法(注释,变量,函数,条件语句)

本章来解析一些无论在任何语言中都必不可少的脚本语法,分别是注释,变量,函数,条件语句,脚本格式如下。

/*
游戏脚本的设计与开发 第三章
*/
//设定变量num的值为5
Var.set(num,5);
//条件语句脚本测试
if(@num>10);
	Var.set(name,lufy);
elseif(@num>4);
	if(@num==5);
		Var.set(name,你好);
	else;
		Var.set(name,hellow);
	endif;
else;
	Var.set(name,legend);
endif;
Text.label(-,txt01,变量测试:Hello @name,0,0,30,#000000);
//声明test函数
function test(value);
	Text.label(-,txt02,函数测试:@value,0,50,30,#000000);
endfunction;
Call.test(参数测试);
下面我们就来一个一个的实现这些脚本的解析。

1,注释

在脚本中难免会用到注释,而一般的注释方式有以下两种。

//Text.label(-,txt01,Hello World,0,0,30,#000000);
/*Text.label(-,txt01,Hello World,0,0,30,#000000);*/
将这些注释移除的方法,就是找到两种注释的开始和结束的位置,然后将其移除。

removeComment:function(str){
	var self = this;
	var sIndex;
	var eIndex;
	var sStr;
	var eStr;
	sIndex = str.indexOf("/*");
	while(sIndex >=0){
		eIndex = str.indexOf("*/",sIndex + 2);
		sStr = str.substr(0,sIndex);
		eStr = str.substr(eIndex + 2);
		str = sStr + eStr;
		sIndex = str.indexOf("/*");
	}
	sIndex = str.indexOf("//");
	while(sIndex >=0){
		eIndex = str.indexOf("\n",sIndex);
		if(eIndex >= 0){
			sStr = str.substr(0,sIndex);
			eStr = str.substr(eIndex);
			str = sStr + eStr;
			sIndex = str.indexOf("//");
		}else{
			sStr = str.substr(0,sIndex);
			str = sStr;
			sIndex = -1;
		}
	}
	return str;
}
然后在每次读取脚本文件的时候,用上面这个函数过滤一下,就可以将里面的注释删除了。

2,变量

对于任何一种语言来说,变量都是不可缺少的,脚本中也是一样,比如游戏中有很多分支选择,游戏要根据玩家做出的不同的选择,来进行不同的剧情发展,这些选择我们必须使用大量的变量来保存。

先来扩展一下LScriptArray类,如下

function LScriptArray(){
	var self = this;
	self.textList = new Array();
	self.layerList = new Array();
	self.varList = new Array();
	self.funList = new Array();
}
varList用来保存变量,funList用来保存函数,这个讲完变量后接着就会讲。

先来规定一下变量的语法

//设定变量
Var.set(name,lufy);
//使用变量 @+变量名
Text.label(-,txt01,变量测试:Hello @name,0,0,30,#000000);

先来实现一下变量的设定,修改一下解析函数的switch部分。

switch(sarr[0]){
	case "Load":
		ScriptLoad.analysis(lineValue);
		break;
	case "Text":
		ScriptText.analysis(lineValue);
		break;
	case "Var":
		ScriptVarlable.analysis(lineValue);
		break;
	default:
		self.analysis();
}
ScriptVarlable类代码如下

/*
* ScriptVarlable.js
**/
var ScriptVarlable = function (){};
ScriptVarlable.analysis = function (value){
	var start = value.indexOf("(");
	var end = value.indexOf(")");
	switch(value.substr(0,start)){
		case "Var.set":
			ScriptVarlable.setVarlable(value,start,end);
			break;
		default:
			LGlobal.script.analysis();
	}
};
ScriptVarlable.setVarlable = function (value,start,end){
	var script = LGlobal.script;
	var lArr = value.substring(start+1,end).split(",");
	script.scriptArray.varList[lArr[0]] = lArr[1];
	script.analysis();
};
这个不难看懂,在setVarlable函数中,将Var.set(name,lufy);括号中的内容以分号隔开,作为变量的名字和值存到了varList数组中。

变量在使用的时候,我定义的规则是@+变量名,在解析每一行脚本的时候,找到@符号看看它后面的变量名是否存在即可,实现方法如下。

ScriptVarlable.getVarlable = function (str){
	var script = LGlobal.script;
	var iIndex = 0;
	var sIndex;
	var eIndex;
	var sStr;
	var eStr;
	var vStr;
	var result = "";
	var r=/^([a-z]|[A-Z]|_)+$/;
	sIndex = str.indexOf("@");
	while(sIndex >=0){
		eIndex = str.indexOf("@",sIndex+1);
		if(sIndex + 1 == eIndex){
			sStr = str.substr(iIndex,sIndex);
			vStr = "@";
			eStr = str.substr(eIndex + 1);
			iIndex = eIndex + 1;
		}else{
			sStr = str.substring(iIndex,sIndex);
			vStr = "";
			sIndex++;
			while(r.exec(str.charAt(sIndex))){
				vStr += str.charAt(sIndex);
				sIndex++;
			}
			vStr = script.scriptArray.varList[vStr];
			eStr = str.substr(sIndex);
			iIndex = sIndex;
		};
		result += (sStr + vStr);
		sIndex = str.indexOf("@",iIndex);
	}
	result += str.substr(iIndex);
	return result;
};
只要在解析每一行的脚本之前,调用此函数的话,就会将此行脚本中的变量部分替换成变量的值,达到使用变量的目的。
将Main.ls的内容修改如下

Var.set(name,lufy);
Text.label(-,txt01,变量测试:Hello @name,0,0,30,#000000);
测试一下,得到效果如下

图1
可以看到,第二行中显示文字的时候,“@name”已经被替换成了“lufy”。

3,函数

首先规定好定义函数的脚本如下

function test(value);
	Text.label(-,txt02,函数测试:@value,0,50,30,#000000);
endfunction;
解析上面的脚本的话,可以通过查找关键字function和endfunction来实现,

首先找到包含function关键字的一行脚本,表示声明函数开始,然后再从这一行中找到空格,左括号和右括号,将函数名和参数分离出来。

然后从下一行脚本开始寻找endfunction关键字,如果没有,则表示该行脚本属于函数体内部,如果有,则表示声明函数结束,然后按照函数名,将函数体和参数都保存起来。

具体实现方法如下。

/*
* ScriptFunction.js
**/
var ScriptFunction = function (){};
ScriptFunction.setFunction = function (value){
	var script = LGlobal.script;
	var startNameIndex = value.indexOf(" ");
	var child;
	var funArr = new Array();
	var start = value.indexOf("(");
	var end = value.indexOf(")");
	var strParam = value.substring(start + 1,end);
	var param = strParam.split(",");
	funArr["param"] = new Array();
	var i;
	for(i=0;i<param.length;i++){
		param[i] = LMath.trim(param[i]);
		if(param[i].length > 0)	{
			funArr["param"].push("param_" + param[i]);
		}
	}
	funArr["name"] = LMath.trim(value.substring(startNameIndex + 1,start));
	
	var funLineArr = new Array();
	while(script.lineList[0].indexOf("endfunction") < 0){
		child = script.lineList.shift();
		for(i=0;i<param.length;i++){
			if(param[i].length > 0)	child = child.replace("@"+param[i],"@param_"+param[i]);
		}
		funLineArr.push(child);
	}
	script.lineList.shift();
	funArr["function"] = funLineArr;
	script.scriptArray.funList[funArr["name"]] = funArr;
	script.analysis();
};
既然已经可以声明函数了,那接下来就是如何调用这个函数了,调用函数的脚本语法如下

Call.test(参数测试);
这行脚本表示调用函数test,并传入“参数测试”这个字符串作为函数的参数。

如何找到test这个函数并运行这个函数呢,如下。

ScriptFunction.analysis = function (value){
	var script = LGlobal.script;
	var point = value.indexOf(".");
	var start = value.indexOf("(");
	var end = value.indexOf(")");
	var name = value.substring(point + 1,start);
	var funArr = script.scriptArray.funList[name];
	if(funArr == null){
		script.analysis();
		return;
	}
	_data = value.substring(start+1,end).split(",");
	var param = funArr["param"];
	var i;
	for(i=0;i<param.length;i++){
		script.scriptArray.varList[param[i]] = _data[i];
	}
	var funLineArr = funArr["function"];
	for(i=funLineArr.length-1;i>=0;i--)script.lineList.unshift(funLineArr[i]);
	script.analysis();
	
};
其实就是通过“test”这个函数名,从funList数组中找到相应的函数,然后将保存在函数体内的脚本添加到脚本解析队列lineList里,这样解析函数就会自动解析这些脚本,从而达到调用函数的目的。

最后修改解析函数的switch部分,如下

switch(sarr[0]){
	case "Load":
		ScriptLoad.analysis(lineValue);
		break;
	case "Text":
		ScriptText.analysis(lineValue);
		break;
	case "Var":
		ScriptVarlable.analysis(lineValue);
		break;
	case "Call":
		ScriptFunction.analysis(lineValue);
		break;
	default:
		if(lineValue.indexOf("function") >= 0){
			ScriptFunction.setFunction(lineValue);
		}else{
			self.analysis();
		}
}
接着类测试一下函数的脚本吧,修改Main.ls文件如下
function test(value);
	Text.label(-,txt02,函数测试:@value,0,50,30,#000000);
endfunction;
Call.test(参数测试);
运行程序,得到效果如图

图2

函数“test”已经成功被调用了,并且参数也成功的传了进去。

4,条件语句

条件语句就是if...else...了,这个相对复杂一些,因为涉及到多层嵌套,if里面还有if。

条件语句的定义如下

if(条件);
	脚本
else if(条件);
	脚本
else;
	脚本
endif;
和函数一样,解析这些脚本关键在于“if”,“else if”,“else”,“endif”这些关键字,只要正确查找到这些关键字,就能解析这些脚本。
if条件语句的解析函数如下

/*
* ScriptIF.js
**/
var ScriptIF = function (){};
ScriptIF.getIF = function (value){
	var script = LGlobal.script;
	var startifIndex = 0;
	var endifIndex = 0;
	var ifArr;
	var childArray = new Array();
	var start = value.indexOf("(");
	var end = value.indexOf(")");
	var str = value.substring(start + 1,end);
	ifArr = str.split("&&");
	var ifvalue = ScriptIF.checkCondition(ifArr);
	var ifvalueend = false;
	var sCount = 0;
	var eCount = 0;
	
	while(startifIndex<script.lineList.length){
		sCount = 0;
		
		if(script.lineList[startifIndex].indexOf("elseif") >= 0){
			if(ifvalue){
				ifvalueend = true;
				startifIndex++;
				continue;
			}
			start = script.lineList[startifIndex].indexOf("(");
			end = script.lineList[startifIndex].indexOf(")");
			str = script.lineList[startifIndex].substring(start + 1,end);
			str = ScriptVarlable.getVarlable(str);
			ifArr = str.split("&&");
			ifvalue = ScriptIF.checkCondition(ifArr);
			startifIndex++;
			continue;
		}else if(script.lineList[startifIndex].indexOf("else") >= 0){
			if(ifvalue){
				ifvalueend = true;
				startifIndex++;
				continue;
			}
			ifvalue = true;
			endifIndex = startifIndex;
			startifIndex++;
			continue;
		}else if(script.lineList[startifIndex].indexOf("endif") >= 0){
			startifIndex++;
			break;
		}else if(script.lineList[startifIndex].indexOf("if") >= 0){
			if(ifvalue && !ifvalueend){
				childArray.push(script.lineList[startifIndex]);
			}
			startifIndex++;
			sCount = 1;
			eCount = 0;
			while(sCount > eCount){
				if(script.lineList[startifIndex].indexOf("if") >= 0 && 
					script.lineList[startifIndex].indexOf("else") < 0 && 
					script.lineList[startifIndex].indexOf("end") < 0){
					sCount++;
				}else if(script.lineList[startifIndex].indexOf("endif") >= 0){
					eCount++;
				}
				if(ifvalue && !ifvalueend){
					childArray.push(script.lineList[startifIndex]);
				}
				startifIndex++;
			}
		}
		if(sCount==0){
			if(ifvalue && !ifvalueend){
				childArray.push(script.lineList[startifIndex]);
			}
			startifIndex++;
		}
	}
	script.lineList.splice(0,startifIndex);
	
	for(var i=childArray.length - 1;i>=0;i--){
		script.lineList.unshift(childArray[i]);
	}
	
	script.analysis();
};
ScriptIF.checkCondition = function (arr){
	for(var i = 0;i<arr.length;i++){
		if(!ScriptIF.condition(arr[i])){
			return false;
		}	
	}
	return true;
};
ScriptIF.condition = function (value){
	var arr;
	if(value.indexOf("===") >= 0){
		//===
		arr=ScriptIF.getCheckStr(value,"===");
		return arr[0] == arr[1];
	}else if(value.indexOf("!==") >= 0){
		//!==
		arr=ScriptIF.getCheckStr(value,"!==");
		return arr[0] != arr[1];
	}else if(value.indexOf("==") >= 0){
		//==
		arr=ScriptIF.getCheckInt(value,"==");
		return arr[0] == arr[1];
	}else if(value.indexOf("!=") >= 0){
		//!=
		arr=ScriptIF.getCheckInt(value,"!=");
		return arr[0] != arr[1];
	}else if(value.indexOf(">=") >= 0){
		//>=
		arr=ScriptIF.getCheckInt(value,">=");
		return arr[0] >= arr[1];
	}else if(value.indexOf("<=") >= 0){
		//<=
		arr=ScriptIF.getCheckInt(value,"<=");
		return arr[0] <= arr[1];
	}else if(value.indexOf(">") >= 0){
		//>
		arr=ScriptIF.getCheckInt(value,">");
		return arr[0] > arr[1];
	}else if(value.indexOf("<") >= 0){
		//<
		arr=ScriptIF.getCheckInt(value,"<");
		return arr[0] < arr[1];
	}
	return false;
};
ScriptIF.getCheckInt = function (value,s){
	var arr;
	arr = value.split(s);
	arr[0] = parseInt(arr[0]);
	arr[1] = parseInt(arr[1]);
	
	return arr;
};
ScriptIF.getCheckStr = function (value,s){
	var arr;
	arr = value.split(s);
	arr[0] = LMath.trim(arr[0].replace('"',''));
	arr[1] = LMath.trim(arr[1].replace('"',''));
	
	
	return arr;
};

最后修改解析函数的switch部分,如下

switch(sarr[0]){
	case "Load":
		ScriptLoad.analysis(lineValue);
		break;
	case "Text":
		ScriptText.analysis(lineValue);
		break;
	case "Var":
		ScriptVarlable.analysis(lineValue);
		break;
	case "Call":
		ScriptFunction.analysis(lineValue);
		break;
	default:
		if(lineValue.indexOf("if") >= 0){
			ScriptIF.getIF(lineValue);
		}else if(lineValue.indexOf("function") >= 0){
			ScriptFunction.setFunction(lineValue);
		}else{
			self.analysis();
		}
}

最后,来测试一下,修改Main.ls脚本文件如下

Var.set(num,1);
if(@num==0);
	Var.set(name,lufy);
else;
	Var.set(name,legend);
endif;
运行程序得到效果

图3

再来个稍微复杂点的例子,继续修改Main.ls脚本文件如下

Var.set(num,5);
if(@num>10);
	Var.set(name,lufy);
elseif(@num>4);
	if(@num==5);
		Var.set(name,你好);
	else;
		Var.set(name,hellow);
	endif;
else;
	Var.set(name,legend);
endif;
运行程序得到效果

图4

基本语法讲完了,本来想实现下循环的,不过暂时用不到,等以后用到的时候再详细说说吧,

目前为止讲解的脚本只有文字,大家是不是觉得很无聊?下一章开始进入图形和图片等脚本的解析,要开始有图像了。


测试连接如下

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

本章为止的lufylegend.lsharp.js源码如下

http://lufylegend.com/demo/test/lsharp/03/lufylegend.lsharp.js

《游戏脚本的设计与开发》系列文章目录

http://blog.csdn.net/lufy_legend/article/details/8888787



本章就讲到这里,欢迎继续关注我的博客

转载请注明:转自lufy_legend的博客http://blog.csdn.net/lufy_legend

没有更多推荐了,返回首页