本章来解析一些无论在任何语言中都必不可少的脚本语法,分别是注释,变量,函数,条件语句,脚本格式如下。
/*
游戏脚本的设计与开发 第三章
*/
//设定变量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);
测试一下,得到效果如下
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;
运行程序得到效果
再来个稍微复杂点的例子,继续修改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