小遊戲小技巧教學8-4(大富翁結合RPG-角色移動)

大富翁結合RPG是從什麼時候開始我沒研究過,但是小時候就用紙當卡片畫遊戲卡,並且在紙上畫不同的地圖,並用骰子當移動方式,開始一場怪物道館挑戰與蒐集(對戰是用剪刀石頭布來判斷攻擊成功與否)!是當時我跟我朋友們的回憶~而那時的我還沒接觸電腦遊戲呢。
當我看到我以前自製的桌遊搬到了手機或電腦線上遊戲中,感覺很微妙呢~(雖然這靈感並不是那麼稀奇)


用大富翁結合RPG的遊戲已經不新鮮了,但是移動的方式、地圖每格的突發狀況卻可以一而再再而三的引起玩家每一格都有不同的樂趣。
現在,就用一個傳統的大富翁結合RPG遊戲來學習並延伸設計吧!

一樣上網址:http://shuinvy.pancakeapps.com/MonoRPG/

這次是本遊戲製作最難,也是最重要的設計。

如果你的遊戲沒有跟我的設定一樣(得到錢、增加好感度等等),

那你至少一定要讓你的角色隨著骰子動起來!

因此繼續研究Map_Render的角色移動階段:

function Map_Render(map,place,character,char_height,area_size,types,order){
	var place_render="";
	place_render+="<table id=tbMap cellpaing=0 cellspacing=0>";
	for(var p in place){
		place_render+="<tr>";
		for(var l in place[p]){
			if(place[p][l]==null) place_render+="<td></td>";
			else  place_render+="<td class='area"+place[p][l]+"' style='background: url(\"image/map/area"+place[p][l]+".png\");'></td>";
		}
		place_render+="</tr>";
	}
	place_render+='<tr><td align="right" colspan="'+place[p].length+'"><span id="lbDice" class="noselect">1</span><input type="button" value="擲骰子" id="btnDice"></td></tr></table>';
	$("#"+map).html(place_render);
	//按下擲骰子按鈕後的動作
	$("#btnDice").click(function(){
		clearInterval(timerDice);				//關閉擲骰子效果
		$("#btnDice").prop("disabled",true);	//讓使用者等待角色移動
		//這邊有兩種做法,一種是讓使用者憑感覺點到想要的數字,另一種則是用隨機(正常應該這樣做)
		if(!types) dice=parseInt(Math.random()*6) + 1;				//讓結果是1~6的結果(隨機)
		$("#lbDice").html(dice);
		//思考:每個位置都可以上下左右走,若有多重路線,則以右下左上為順序。預設是順時針走。
		//現在位置在now=0,依照place陣列走對應的格數
		Anime(character,dice,area_size,place,order);
	});
	//讓角色在地圖初始的位置上(左上角)
	var mheight=($("#"+map).height()+"").substring(0,($("#"+map).height()+"").length-2);
	var mtop=$("#"+map).css("top").substring(0,$("#"+map).css("top").length-2);
	var mwidth=($("#"+map).width()+"").substring(0,($("#"+map).width()+"").length-2);
	var mleft=$("#"+map).css("left").substring(0,$("#"+map).css("left").length-2);
	$("#"+character).css("top",mtop-char_height+area_size).css("left",mleft);
}

跟上一篇不同的地方,

在於追加了使用者按下擲骰子按鈕後的動作。

也就是$("#btnDice").click部分。

第一個動作,讓使用者不能再點擊擲骰子按鈕,並且停止擲骰子動畫,

因此用clearInterval(timerDice);以及$("#btnDice").prop("disabled",true);

接著就決定擲骰子的結果方法,因此用參數types控制。

這邊有兩種,一種是讓使用者自行憑感覺按到想要的數字,另一種則是隨機產生數字,也就是說跑動畫是給你看好玩的,即使你看著6按下去,結果也是隨機出個3之類。

在使用者呼叫Map_Render時,用true或false來控制你要的是哪種,

若想要隨機的,就這樣呼叫:

Map_Render("map",place,"char1",70,50,false,orders);

若想要使用者發揮他的好眼力,就這樣呼叫:

Map_Render("map",place,"char1",70,50,true,orders);

不過我會建議將參數設成false,畢竟擲骰子就是要隨機才有樂趣不是嗎~

然後也是讓你學到另一種方式,有看過能量條嗎?就是會有紅到綠漸層的能量條,使用者要按下按鈕使能量條在最大或有效範圍內,這時就要用true的情況了!

否則,我看到能量條在綠色處,跑出來卻不是(因為結果是隨機的),不是很奇怪嗎~

接著就聚焦到最大的重點─Anime上面了:

//角色走路的效果
function Anime(character,dice,area_size,place,order){
	charleft=$("#"+character).css("left").substring(0,$("#"+character).css("left").length-2);
	chartop=$("#"+character).css("top").substring(0,$("#"+character).css("top").length-2);
	var p = parseInt(now / place[0].length), l = parseInt(now % place[0].length);
	//判斷每一步是往哪個方向走
	//透過order陣列的提示,0代表不動;1=向右;2=向下;3=向左;4=向上
	if(order[p][l]==1){
		//向右走
		//讓角色面向右邊
		$("#"+character).css("background",'url("image/char/char1.png")');
		$("#"+character).css("left",parseInt(charleft)+parseInt(area_size/2));
		$("#"+character).css("top",parseInt(chartop)-parseInt(area_size/2));
		
		timerMove=setTimeout(function(){
			charleft=$("#"+character).css("left").substring(0,$("#"+character).css("left").length-2);
			chartop=$("#"+character).css("top").substring(0,$("#"+character).css("top").length-2);
			$("#"+character).css("left",parseInt(charleft)+parseInt(area_size/2));
			$("#"+character).css("top",parseInt(chartop)+parseInt(area_size/2));
			//走完一格後,將步數減一,並判斷是否還要再走下去
			now++;					//向右的話,索引值是加1(將地圖由0排到n-1,此處n=40)
			isMove(character,dice,area_size,order,place,p,l);
		}, speed);
	}else if(order[p][l]==2){
		//向下走
		$("#"+character).css("top",parseInt(chartop)+parseInt(area_size/2));
		timerMove=setTimeout(function(){
			chartop=$("#"+character).css("top").substring(0,$("#"+character).css("top").length-2);
			$("#"+character).css("top",parseInt(chartop)+parseInt(area_size/2));
			//走完一格後,將步數減一,並判斷是否還要再走下去
			now+=8;					//向下的話,索引值是加8(將地圖由0排到n-1,此處n=40)
			isMove(character,dice,area_size,order,place,p,l);
		}, speed);
	}else if(order[p][l]==3){
		//向左走
		//讓角色面向左邊
		$("#"+character).css("background",'url("image/char/char2.png")');
		$("#"+character).css("left",parseInt(charleft)-parseInt(area_size/2));
		$("#"+character).css("top",parseInt(chartop)-parseInt(area_size/2));
		
		timerMove=setTimeout(function(){
			charleft=$("#"+character).css("left").substring(0,$("#"+character).css("left").length-2);
			chartop=$("#"+character).css("top").substring(0,$("#"+character).css("top").length-2);
			$("#"+character).css("left",parseInt(charleft)-parseInt(area_size/2));
			$("#"+character).css("top",parseInt(chartop)+parseInt(area_size/2));
			//走完一格後,將步數減一,並判斷是否還要再走下去
			now--;					//向左的話,索引值是減1(將地圖由0排到n-1,此處n=40)
			isMove(character,dice,area_size,order,place,p,l);
		}, speed);
	}else if(order[p][l]==4){
		//向上走
		$("#"+character).css("top",parseInt(chartop)-parseInt(area_size/2));
		timerMove=setTimeout(function(){
			chartop=$("#"+character).css("top").substring(0,$("#"+character).css("top").length-2);
			$("#"+character).css("top",parseInt(chartop)-parseInt(area_size/2));
			//走完一格後,將步數減一,並判斷是否還要再走下去
			now-=8;					//向下的話,索引值是減8(將地圖由0排到n-1,此處n=40)
			isMove(character,dice,area_size,order,place,p,l);
		}, speed);
	}
}

看這一坨可能會嚇到,因此先以往右走解說:

charleft=$("#"+character).css("left").substring(0,$("#"+character).css("left").length-2);
chartop=$("#"+character).css("top").substring(0,$("#"+character).css("top").length-2);
	var p = parseInt(now / place[0].length), l = parseInt(now % place[0].length);
	//判斷每一步是往哪個方向走
	//透過order陣列的提示,0代表不動;1=向右;2=向下;3=向左;4=向上
	if(order[p][l]==1){
		//向右走
		//讓角色面向右邊
		$("#"+character).css("background",'url("image/char/char1.png")');
		$("#"+character).css("left",parseInt(charleft)+parseInt(area_size/2));
		$("#"+character).css("top",parseInt(chartop)-parseInt(area_size/2));
		
		timerMove=setTimeout(function(){
			charleft=$("#"+character).css("left").substring(0,$("#"+character).css("left").length-2);
			chartop=$("#"+character).css("top").substring(0,$("#"+character).css("top").length-2);
			$("#"+character).css("left",parseInt(charleft)+parseInt(area_size/2));
			$("#"+character).css("top",parseInt(chartop)+parseInt(area_size/2));
			//走完一格後,將步數減一,並判斷是否還要再走下去
			now++;					//向右的話,索引值是加1(將地圖由0排到n-1,此處n=40)
			isMove(character,dice,area_size,order,place,p,l);
		}, speed);
	}

charleft=$("#"+character).css("left").substring(0,$("#"+character).css("left").length-2);
chartop=$("#"+character).css("top").substring(0,$("#"+character).css("top").length-2);

其實是先取得我們的角色目前的座標,等移動時要用來做參考。

var p = parseInt(now / place[0].length), l = parseInt(now % place[0].length);

是用我們目前的位置(也就是變數now)來判斷是地圖陣列中的哪個位置,包括要用來判斷要往哪個方向走,以及是哪種地圖效果。

範例的地圖是0~39,

也就是0 1 2 3....7

還有是8 9 10....15

一直到39(因為陣列從0開始,所以40格,最後索引值=40-1)

因此now=9時,代表在地圖的從上往下數第二行的第二個位置(也就是p=1,l=1;為null)。

既然有了p和l,就利用人眼辨識好的order陣列來判斷是往哪個方向走。

這邊其實用switch...case...會比較好,

不過因為這邊夠麻煩了,用if包住看起來會比較容易。

那麼往右走其實就是這部分:

//向右走
//讓角色面向右邊
		$("#"+character).css("background",'url("image/char/char1.png")');
		$("#"+character).css("left",parseInt(charleft)+parseInt(area_size/2));
		$("#"+character).css("top",parseInt(chartop)-parseInt(area_size/2));
		
		timerMove=setTimeout(function(){
			charleft=$("#"+character).css("left").substring(0,$("#"+character).css("left").length-2);
			chartop=$("#"+character).css("top").substring(0,$("#"+character).css("top").length-2);
			$("#"+character).css("left",parseInt(charleft)+parseInt(area_size/2));
			$("#"+character).css("top",parseInt(chartop)+parseInt(area_size/2));
			//走完一格後,將步數減一,並判斷是否還要再走下去
			now++;					//向右的話,索引值是加1(將地圖由0排到n-1,此處n=40)
			isMove(character,dice,area_size,order,place,p,l);
		}, speed);

動畫設計上,我只讓角色變換左右方向,若你想讓角色有正面與背面圖,請自己加上程式碼和圖片囉~

改變角色面對方向的部分就是$("#"+character).css("background",'url("image/char/char1.png")');

接著要透過你很好的頭腦思考移動的動畫怎麼做。

我的方法很簡單:先往右上走,等0.2秒再往右下走,所以有角色跳著移動的錯覺。

0.2秒的部分則是用變數speed操控,一樣是200/1000,因此將200改成其他數字可以修改角色移動速度。

如何往右上走呢?

程式碼的座標軸跟數學上的不太一樣,越往右就是x越大,越往上y就是越小。

因此當角色往右移動時,他的x座標是變大的,往上移動時,他的y座標是變小的。

變多大呢?我是用地圖格子當參考點。

也就是往右邊走一半的地圖格子,並且往上走一半的地圖格子大小。

而在javascript裡面,改變元素的css的top等於改變y軸座標,改變css的left等於改變x軸座標,

才會有這兩行程式碼:

$("#"+character).css("left",parseInt(charleft)+parseInt(area_size/2));
$("#"+character).css("top",parseInt(chartop)-parseInt(area_size/2));

後面部分就是移動地圖格子大小的一半,left部分中間的加號則是往右移動的意思,top部分中間的減號代表往上移動的意思。

接著,要等0.2秒後才能移動下一步,

否則中間連在一起,人眼就會看到角色橫移的現象,就不是跳動著了!

所以單純走一格的話,

接下來的程式碼是長這樣:

timerMove=setTimeout(function(){
	charleft=$("#"+character).css("left").substring(0,$("#"+character).css("left").length-2);
	chartop=$("#"+character).css("top").substring(0,$("#"+character).css("top").length-2);
	$("#"+character).css("left",parseInt(charleft)+parseInt(area_size/2));
	$("#"+character).css("top",parseInt(chartop)+parseInt(area_size/2));
}, speed);

一樣類似的程式碼,只是top部分中間變成加號了(往下走)。而上面一樣是更新角色的目前位置座標。

接著是走完一步後,要更新現在所在的位置,因此now++;

然後用isMove(character,dice,area_size,order,place,p,l);做判斷是否還要繼續走下去(是否走完骰子投出的步數)?

於是來到isMove部分:

function isMove(character,dice,area_size,order,place,p,l){
	dice--;
	clearTimeout(timerMove);
	if(dice>0) timerAnime=setTimeout(function(){ clearTimeout(timerAnime); Anime(character,dice,area_size,place,order); }, speed);
	else {
		p = parseInt(now / place[0].length), l = parseInt(now % place[0].length);
		//當角色停止,查看遇到的狀況
		Options(place,p,l);
	}
}

用dice--表示走完一步了,因為四種走法只差走的方法不同,走過的步數是一樣每次一格,因此用isMove做後續的動作。

clearTimeout(timerMove);

則是將移動的動畫停止,因為要判斷是否還有其他步數要走。

if(dice>0) timerAnime=setTimeout(function(){ clearTimeout(timerAnime); Anime(character,dice,area_size,place,order); }, speed);

這邊表示如果dice>0(使用者可能投出1~6結果),那麼就將剛剛的動畫重新跑一遍並預防性關閉角色動畫,這裡用到了程式碼中的遞迴。

直到dice=0時,

p = parseInt(now / place[0].length), l = parseInt(now % place[0].length);
//當角色停止,查看遇到的狀況
Options(place,p,l);

將參數丟給Options來判斷這格的效果,以及rollDice讓使用者重新擲骰子。

那麼下一篇文章就來介紹如何處理角色在不同格子的情況囉。