1. 程式人生 > >《遊戲指令碼的設計與開發》-(RPG部分)3.3 加入多個人物以及對話實現

《遊戲指令碼的設計與開發》-(RPG部分)3.3 加入多個人物以及對話實現

上一節中 給地圖加入了遮擋功能,嘗試著加入了一個可以控制的測試人物,並且實現了人物行走時的各個動作變換的控制。本節中接下來要做的事情就是把之前的工作全部指令碼化,並且使用遊戲指令碼加入多個人物角色,最後通過點選地圖上的人物,來實現遊戲中的對話功能。文章中貼出的只是部分主要的程式碼,你在看的時候,有些程式碼可能會不理解,這個不要緊,最後我會放出完整原始碼的下載。

首先,使用之前已經開發好的指令碼來給遊戲新增一個logo頁面。

Main.ls

//新增顯示層back
Layer.add(-,back,0,0);
Layer.drawRectLine(back,0,0,800,480,#000000);
//顯示文字,讀取中
Text.label(-,loading,圖片讀取中...,300,200,15,#000000);
//讀取圖片
Load.img(logobtnover,./images/logo/logobtnover.png);
Load.img(logobtnup,./images/logo/logobtnup.png);
Load.img(logo,./images/logo/logo.jpg);
//刪除文字,讀取中
Text.remove(loading);
Img.add(back,logo01,logo,0,0,800,480,1);
//顯示遊戲名稱
Text.label(back,logo,RPG指令碼測試,470,50,40,#000000);
//在back層上新增一個按鈕,作為選項
Button.add(back,btn01,遊戲開始,500,150,logobtnup,logobtnover,logobtnover,#ffffff);
function btn01click();
	Button.remove(btn01);
	Text.label(-,loading,指令碼讀取中...,300,200,20,#ffffff);
	Load.script(script/R01.ls);
endfunction;
//為按鈕新增點選事件
Button.mousedown(btn01,btn01click);
使用指令碼新增圖片,按鈕,函式等功能,在裡都已經詳細介紹過了,不瞭解的朋友可以看一下第一章的內容,上面的指令碼效果如下圖。

點選[遊戲開始]按鈕後,進入指令碼R01.ls,先來預設一下本地要實現的所有功能所需要的指令碼內容,一會兒我來一點點兒的實現它。

R01.ls

//清除畫面
Layer.clear(-);
//RPG地圖設定開始
RPGMap.start();
//RPG地圖初始化開始
initialization.start;
//地圖資料設定,包括地形和圖片等
addMap(r01.rmap);
//加入人物角色,引數以此為 人物index,動作action,方向direction,座標x,座標y,是否可控ishero
RPGCharacter.add(1,stand,right,20,9,true);
RPGCharacter.add(2,stand,down,22,13,false);
RPGCharacter.add(3,stand,down,55,15,false);
RPGCharacter.add(4,stand,up,35,40,false);
RPGCharacter.add(5,stand,left,35,7,false);
//RPG地圖初始化結束
initialization.end;
//RPG地圖中各函式初始化開始
function.start;
//函式名稱為“characterclick”+人物序號的函式,功能是當點選到某人物的時候,會呼叫相應的函式
function characterclick2();
	RPGTalk.set(2,0,你知道lufy嗎,聽說那傢伙除了做遊戲,啥都不會。);
	RPGTalk.set(1,0,怪不得啊,哈哈哈。);
endfunction;
function characterclick3();
	RPGTalk.set(3,0,。。。。。。);
	RPGTalk.set(1,0,少年,你能幫我撿肥皂嗎?);
endfunction;
function characterclick4();
	RPGTalk.set(4,0,請不要跟我說話,我只是過來打醬油的。);
	RPGTalk.set(1,0,不跟我說話是因為看不起我嗎?);
endfunction;
function characterclick5();
	RPGTalk.set(5,0,你現在進入的是由lufylegend.js製作的一個虛幻的遊戲指令碼世界。目前正在執行是對話指令碼。);
endfunction;
//RPG地圖中各函式初始化結束
function.end;
//RPG地圖設定結束
RPGMap.end();

以上的指令碼,已經足夠滿足接下來要完成的工作了,實現的效果為


下面一個一個的來看,這些指令碼如何來實現。

1,進入地圖指令碼

//RPG地圖設定開始
RPGMap.start();
//RPG地圖設定結束
RPGMap.end();
包括第二章的戰旗部分,前面已經介紹了多種指令碼,RPG腳本當然要與其他指令碼區分開,所以RPG部分的指令碼我會全部以RPG開頭,上面這兩個指令碼,分別會進入和退出RPG部分的地圖指令碼。

RPG指令碼部分有區別於其他部分指令碼,當然有自己的一個指令碼解析函式,如下

/*
* LScriptRPG.js
**/
var LScriptRPG = function (){};
LScriptRPG.analysis = function (childType, lineValue){
	var start,end,params;
	start = lineValue.indexOf("(");
	end = lineValue.indexOf(")");
	switch(childType){
		case "RPGMap":
			LGlobal.script.scriptLayer.controller.mapLoad();
			break;
		case "RPGTalk":
			LRPGTalkScript.analysis(lineValue);
			break;
		default:
			LGlobal.script.analysis();
	}
};

修改LScript.analysis指令碼解析函式中的switch部分,可以很容易的進入到上面RPG指令碼解析函式裡,稍後大家可以看一下原始碼,上面的RPG指令碼解析函式裡,遇到RPGMap指令碼,會呼叫LGlobal.script.scriptLayer.controller.mapLoad(),我已經提前在IndexController中把LGlobal.script.scriptLayer設定成了程式中的IndexView,所以這裡其實是呼叫了IndexController的mapLoad函式,

IndexController.prototype.mapLoad=function(){
	var self = this;
	self.loadMvc("Map",self.mapComplete);
};

當Map的相關的檔案讀取完成之後,會自動呼叫LRPGMapScript.analysis();進行下一個指令碼的解析。

2,地圖的初始化

//RPG地圖初始化開始
initialization.start;
//RPG地圖初始化結束
initialization.end;
地圖的初始化,包括地圖圖片和地形的設定,人物角色的新增等等,看一下RPG地圖指令碼的解析函式
/*
* LRPGMapScript.js
**/
LRPGMapScript = function(){};
LRPGMapScript.analysis=function(){
	var script = LGlobal.script;
	if(script.lineList.length == 0)return;
	var lineValue = LMath.trim(script.lineList.shift());
	if(lineValue.length == 0){
		LRPGMapScript.analysis();
		return;
	}
	trace("LRPGMapScript analysis lineValue = " + lineValue);
	switch(lineValue){
		case "RPGMap.end()":
			setTimeout(function(){
				LRPGObject.RPGMap.initOver=true;
				LGlobal.script.analysis();
			},100);
			return;
		case "initialization.start":
			LRPGMapScript.initialization();
			break;
		case "function.start":
			LRPGMapScript.addFunction();
			break;
		default:
			LRPGMapScript.analysis();
	}
};
當遇到指令碼initialization.start的時候,會呼叫LRPGMapScript.initialization();函式來進行地圖初始化。
LRPGMapScript.initialization=function(){
	var script = LGlobal.script;
	var lineValue = LMath.trim(script.lineList.shift());
	trace("LRPGMapScript initialization lineList = " + lineValue);
	if(lineValue.length == 0){
		LRPGMapScript.initialization();
		return;
	}
	if(lineValue == "initialization.end"){
		LRPGMapScript.analysis();
		return;
	}
	var params,i;
	var start = lineValue.indexOf("(");
	var end = lineValue.indexOf(")");
	switch(lineValue.substr(0,start)){
		case "addMap":
			params = lineValue.substring(start+1,end).split(",");
			LRPGObject.RPGMap.addMap.apply(LRPGObject.RPGMap,params);
			break;
		case "RPGCharacter.add":
			params = lineValue.substring(start+1,end).split(",");
			LRPGObject.RPGMap.addCharacter.apply(LRPGObject.RPGMap,params);
			break;
		default:
			LRPGMapScript.initialization();
	}
};
可以看到,LRPGMapScript.initialization();函式進行地圖的初始化工作,目前裡面包括addMap地圖資料新增,RPGCharacter.add人物角色新增等功能。
3,地圖資料新增
//地圖資料設定,包括地形和圖片等
addMap(r01.rmap);
r01.rmap檔案中的內容就是前面所介紹過的地圖資料,如下
{
"width":1280
,"height":960
,"data":[
			[1,1,1,......]
			,[0,0,0,......]
			......
		]
		,"pieceWidth":1280
		,"pieceHeight":960
		,"imgs":[
			[{"img":"map-1","rect":[0,0],"path":"map-1.png"}]
		]
		,"builds":[
			[{"img":"build-1","rect":[0,0],"path":"build-1.png"}]
		]
	}
解析函式遇到addMap指令碼之後,讀取相應的地圖資料檔案,然後進行地圖的相關的設定。
4,人物角色的新增
//加入人物角色,引數以此為 人物index,動作action,方向direction,座標x,座標y,是否可控ishero
RPGCharacter.add(1,stand,right,20,9,true);
RPGCharacter.add(2,stand,down,22,13,false);

上一節中我為了測試,臨時加入了一個可以控制的人物,上面指令碼RPGCharacter.add用來新增一個人物角色,我只需要解析這個指令碼,然後把相應的引數分離出來,和上一節用同樣的方法,就可以加入一個人物了,當然地圖上可以控制行走的人物通常只有一個(或者說是一組,因為有些遊戲中是控制一個隊伍的行走,如fc版《吞食天地》),所以在加入人物角色的時候,要設定這些人物哪些是可以控制的,哪些是不可以控制的。

5,函式的新增

//RPG地圖中各函式初始化開始
function.start;
//函式名稱為“characterclick”+人物序號的函式,功能是當點選到某人物的時候,會呼叫相應的函式
function characterclick2();
	RPGTalk.set(2,0,你知道lufy嗎,聽說那傢伙除了做遊戲,啥都不會。);
	RPGTalk.set(1,0,怪不得啊,哈哈哈。);
endfunction;
//RPG地圖中各函式初始化結束
function.end;
之前也已經介紹了,如何用指令碼來實現一個函式的新增和呼叫,但是函式的解析需要在LScript.analysis指令碼解析函式中進行,而現在指令碼解析已經進入到了RPG部分的解析,所以這裡我加入了function.start;和function.end;來做一下特殊的處理,呼叫原來的ScriptFunction指令碼,來解析裡面的函式指令碼。
6,對話指令碼
RPGTalk.set(1,0,少年,你能幫我撿肥皂嗎?);
對話的顯示,要分兩部分,一個是人物頭像的顯示,一個是對話內容的顯示。

頭像的顯示,我用下面的類來實現。

function Face(index,subindex){
	var self = this;
	base(self,LSprite,[]);
	loader = new LLoader();
	loader.parent = self;
	loader.addEventListener(LEvent.COMPLETE,self.loadOver);
	loader.load(LMvc.IMG_PATH+"face/"+index+"-"+subindex+".png","bitmapData");
}
Face.prototype.loadOver = function(event){
	var self = event.target.parent;
	var bitmapData = new LBitmapData(event.currentTarget);
	var bitmap = new LBitmap(bitmapData);
	self.addChild(bitmap);
};
index是人物的頭像序號,subindex是人物的表情,因為在遊戲裡一個人物,通常有多種表情,所以,我們要為每個人物準備多個頭像,並且能分別呼叫它們。
接著,先準備一張背景圖,


然後,將人物的名字和對話的名稱顯示到相應的位置上就可以了,下面是Talk的所有程式碼。

function Talk(){
	if(arguments.length == 6){
		TalkRun.apply(this,arguments);
	}else if(arguments.length == 4){
		TalkRun.call(this,LMvc.layer,150,arguments[0],arguments[1],arguments[2],arguments[3]);
	}else{
		TalkRun.call(this,arguments[0],150,arguments[1],arguments[2],arguments[3],arguments[4]);
	}
}
function TalkRun(layer,y,index,faceindex,msg,callback){
	if(LGlobal.talkLayer && LGlobal.talkLayer.parent){
		LGlobal.talkLayer.parent.removeChild(LGlobal.talkLayer);
	}
	var talkLayer = new LSprite();
	talkLayer.y = y;
	talkLayer.x = 50;
	var charaLayer = new Face(LMvc.datalist["chara"]["peo"+index]["Face"],faceindex);
	charaLayer.x = 200;
	talkLayer.addChild(charaLayer);
	
	var back = new LBitmap(new LBitmapData(LMvc.datalist["talkbox"]));
	back.y = 130;
	back.alpha = 0.7;
	talkLayer.addChild(back);
	var nameText = new LTextField();
	nameText.size = 18;
	nameText.color = "#FFFFFF";
	nameText.text = LMvc.datalist["chara"]["peo"+index]["Name"];
	nameText.x = 30+(90 - nameText.getWidth())*0.5;
	nameText.y = back.y + 22;
	talkLayer.addChild(nameText);
	
	var msgText = new LTextField();
	msgText.x = 25;
	msgText.y = 225;
	msgText.text = msg;
	msgText.size = 12;
	msgText.color = "#FFFFFF";
	msgText.width = 430;
	msgText.setWordWrap(true,23);
	msgText.wind(callback);
	talkLayer.addChild(msgText);
	layer.addChild(talkLayer);
	LRPGObject.talkOver = false;
	LRPGObject.talkLayer = talkLayer;
}
function TalkRemove(){
	LRPGObject.talkLayer.remove();
	LRPGObject.talkOver = false;
	LRPGObject.talkLayer = null;
	LGlobal.script.analysis();
}

OK,是不是已經迫不及待的想要看到效果了?測試連線如下:

預覽效果:

最後,給出本次的程式碼下載:

※原始碼執行說明:

1,需要伺服器支援,詳細請看本系列文章《序》和《第一章》

2,原始碼中不含lufylegend.js引擎程式碼,請自己到官方下載引擎

預告:

現在地圖上的NPC人物都是靜止不動的,在實際的遊戲中有些NPC會隨即的走動,使得遊戲效果更為生動,

另外大家都知道,RPG遊戲中有一個重要的功能,就是任務系統,玩家完成某一項人物會得到什麼獎勵,然後接著出現一個新的任務等等,

後面我接著來給大家介紹地圖的切換效果,NPC人物的隨即走動,以及地圖指令碼中的任務系統,請期待下次更新。

《遊戲指令碼的設計與開發》系列文章目錄