JS游戲開發(fā)(四)用地圖塊拼成大地圖
小時候我們玩過拼圖游戲,是用自己的手去拼的。今天我們來研究研究用javascript來拼圖。同樣是拼圖,但用js拼圖要比用手拼圖麻煩多了,因此以后我要把它優(yōu)化成引擎。
一、前言
以上是一段導(dǎo)語,話不扯遠,對《三國志曹操傳》熟悉的玩家知道,《三國志曹操傳》的地圖是由小地圖塊拼成的,那要實現(xiàn)它就和導(dǎo)語說得一樣:很麻煩。不過即使麻煩也是一門技術(shù),因此在此分享給大家,希望大家喜歡。
二、代碼講解
今天我要換換講解方式,先不給代碼,我們先來想想原理。現(xiàn)在,假如你有一幅圖片,把它裁開成若干份,并打亂?,F(xiàn)在如果讓你用js把他們組織起來,如何做呢?先不說圖的順序,首先來看把它們弄在一起就很難了。這時我減少難度,給你幾個選擇:
A.用margin慢慢調(diào) B.用數(shù)組把它們排列好 C.放棄
在這道題中,選A是很不明智的,選C就代表你也拿不定主意??磥磉xB是***的。既然都告訴大家用數(shù)組,那就先上代碼吧。免得消磨大家興致。
js代碼:
- /*
- *Prompt:
- *If you want to add hurdle, find string: "{{Add hurdle above." and "{{After add hurdle, add the hurdle to the vector above." please.
- *If you want to add or change type of grid, find string: "{{Add new grid above.".
- *If you want to change position of map, please find string: "{{Change map margin above.".
- *If the icon of crid is changed, you have to change the size of icon. Find "{{Change icon size above." to change size.
- */
- //Map of hurdle or military or resource.
- var vView = [];
- /*Remarks:
- *L: land *S: sea *R: river *W: swamp *A: lawn *B: bridge *H: house *h: hospital *w: warehouse *b: bourse *M: military academy *m: military factories
- *r: research Center *P: port *D: dock *s: Shipyard
- */
- var mScene = {
- 'L': ['./land.png', '陸地']
- , 'S': ['./sea.png', '河流']
- , 'T': ['./tree.png', '樹木']
- , 'B': ['./bridge.png', '橋']
- , 'C': ['./beach.png', '沙灘']
- };
- //{{Add new grid above.
- var mCurrent = {
- Margin: {
- left: -1
- , top: -1
- , right: -1
- , bottom: -1
- }
- , Position: {
- X: -1
- , Y: -1
- }
- , Type: 'NONE'
- };
- var mTitle = {};
- var sHurdleONE =
- 'S,S,S,S,S,S,S,S,S,S,S'
- + ';T,L,T,T,T,T,S,S,S,S,T'
- + ';T,L,L,T,S,S,S,S,S,L,T'
- + ';T,L,L,L,C,C,C,S,S,T,S'
- + ';T,L,L,L,C,C,C,B,B,L,T'
- + ';T,L,L,C,C,C,C,S,S,L,T'
- + ';T,L,L,C,C,T,S,S,L,L,T'
- ;
- //{{Add hurdle above.
- var vHurdles = [sHurdleONE];
- //{{After add hurdle, add the hurdle to the vector above.
- function _createGrid(nWidthBasic, nHeightBasic, nPicWidth, nPicHeight, cType, mMargin)
- {
- var mCoordMember = {
- left: nWidthBasic
- , top: nHeightBasic
- , right: nWidthBasic + nPicWidth
- , bottom: nHeightBasic + nPicHeight
- };
- var mPositionMember = {
- X: (mCoordMember.left - mMargin.x) / nPicWidth
- , Y: (mCoordMember.top - mMargin.y) / nPicHeight
- };
- var mItem = {
- Coord: mCoordMember
- , Position: mPositionMember
- , Type: cType
- };
- return mItem;
- }
- function _loadHurdle(sHurdle)
- {
- var nBasic = 0;
- var nWidthBasic = nBasic; //margin-left.
- var nHeightBasic = 0; //margin-top.
- //{{Change map margin above.
- var nPicWidth = 45; //Picture width is nBasic.
- var nPicHeight = 45; //Picturn height is nHeightBasic.
- //{{Change icon size above.
- var nSub;
- var nRow;
- var nCol;
- var v = sHurdle.split(';');
- var vRec = [];
- for(nSub = 0; nSub < v.length; nSub++){
- var vCrid = v[nSub].split(',');
- vRec[vRec.length] = vCrid;
- }
- for(nRow = 0; nRow < vRec.length; nRow++){
- var vCol = vRec[nRow];
- for(nCol = 0; nCol < vCol.length; nCol++){
- var cType = vCol[nCol];
- var mMargin = {x: nBasic, y: nBasic};
- vView[vView.length] = _createGrid(nWidthBasic, nHeightBasic, nPicWidth, nPicHeight, cType, mMargin);
- nWidthBasic += nPicWidth;
- }
- nHeightBasic += nPicHeight;
- nWidthBasic = nBasic;
- }
- }
- //Show map with vector 'vView'.
- function _showMap(sID)
- {
- var xDiv=document.getElementById(sID);
- var xGrid;
- var xImg;
- var nTop = 0;
- var nSub;
- var sIdPrefix = 'ID_IMG_NUM_';
- var sIdGrid = 'ID_A_NUM_';
- for(nSub = 0; nSub < vView.length; nSub++){
- var mGrid = vView[nSub];
- if(mGrid){
- var xMargin = mGrid.Coord;
- var cType = mGrid.Type;
- var xProper = mScene[cType];
- if(xProper){
- xGrid = document.createElement('a');
- xImg = document.createElement('img');
- xImg.style.position = 'absolute';
- xImg.style.marginLeft = xMargin.left;
- xImg.style.marginTop = xMargin.top;
- xImg.src = xProper[0];
- xImg.style.border = '0px solid #000000';
- xImg.id = sIdPrefix + nSub;
- xImg.style.width = 45;
- xImg.style.height = 45;
- xImg.style.display = 'block';
- xGrid.onclick = function(e){
- var xCurrentGrid = e.target;
- var sId = xCurrentGrid.id;
- var nIdAsSub = parseInt(sId.substring(sIdPrefix.length, sId.length));
- mCurrent = vView[nIdAsSub];
- if(!mCurrent){
- alert("Error 0004.");
- }
- };
- xGrid.title = xProper[1] + '(' + parseInt(mGrid.Position.X) + ', ' + parseInt(mGrid.Position.Y+2) + ')';
- xGrid.id = sIdGrid + nSub;
- xGrid.appendChild(xImg);
- xDiv.appendChild(xGrid);
- }else{
- alert("Error: 0003.");
- }
- }else{
- alert("Error: 0002.");
- }
- }
- }
- //Show map of hurdle.
- function _showHurdle(nHurdle)
- {
- if(vHurdles[nHurdle - 1]){
- _loadHurdle(vHurdles[nHurdle - 1]);
- _showMap('ID_DIV_BATTLEFIELD');
- }else{
- alert("Error: 0001.");
- }
- }
看看,這點程序就用了195行,而且這還是一張地圖,看來還很有點麻煩哦。沒關(guān)系,慢慢解釋。
首先還是把素材放在這里.
素材不是來自《三國志曹操傳》,因為沒整理好《三國志曹操傳》的地圖素材,所以就隨便找了些。不過也照樣可以用。希望大家不要介意。
麻煩的代碼最容易弄得亂七八糟,因此在此時要良好的區(qū)分開樣式設(shè)置和拼圖核心。
拼圖核心在哪里呢???在這里:
- var mScene = {
- 'L': ['./land.png', '陸地']
- , 'S': ['./sea.png', '河流']
- , 'T': ['./tree.png', '樹木']
- , 'B': ['./bridge.png', '橋']
- , 'C': ['./beach.png', '沙灘']
- };
- //{{Add new grid above.
- var mCurrent = {
- Margin: {
- left: -1
- , top: -1
- , right: -1
- , bottom: -1
- }
- , Position: {
- X: -1
- , Y: -1
- }
- , Type: 'NONE'
- };
- var mTitle = {};
- var sHurdleONE =
- 'S,S,S,S,S,S,S,S,S,S,S'
- + ';T,L,T,T,T,T,S,S,S,S,T'
- + ';T,L,L,T,S,S,S,S,S,L,T'
- + ';T,L,L,L,C,C,C,S,S,T,S'
- + ';T,L,L,L,C,C,C,B,B,L,T'
- + ';T,L,L,C,C,C,C,S,S,L,T'
- + ';T,L,L,C,C,T,S,S,L,L,T'
- ;
- //{{Add hurdle above.
- var vHurdles = [sHurdleONE];
- //{{After add hurdle, add the hurdle to the vector above.
首先我把S,T,B,C,L定義好,使S代表河流,T代表樹木,B代表橋,C代表沙灘,L代表陸地。var mCurrent后面有用,暫不解釋。然后是var mTitle,這個專門是用來顯示title的,所以也不解釋了。關(guān)鍵是在下:
- var sHurdleONE =
- 'S,S,S,S,S,S,S,S,S,S,S'
- + ';T,L,T,T,T,T,S,S,S,S,T'
- + ';T,L,L,T,S,S,S,S,S,L,T'
- + ';T,L,L,L,C,C,C,S,S,T,S'
- + ';T,L,L,L,C,C,C,B,B,L,T'
- + ';T,L,L,C,C,C,C,S,S,L,T'
- + ';T,L,L,C,C,T,S,S,L,L,T'
- ;
這段代碼就是把定義好的S,T,B,C,L連在一起的核心。后面只用定義S,T,B,C,L的寬度高度定義就能把它們連成一塊。并且只要把它們在數(shù)組里的位置調(diào)一調(diào)就能改變樣式。
接下來為了能切換地圖,我們把***張地圖放進了數(shù)組:
- var vHurdles = [sHurdleONE];
- //{{After add hurdle, add the hurdle to the vector above.
如果以后加了地圖,只用把地圖所屬的數(shù)組名加到vHurdles數(shù)組就可以了,調(diào)用是就可以直接寫對應(yīng)下標(biāo)。
樣式設(shè)置在下:
- function _createGrid(nWidthBasic, nHeightBasic, nPicWidth, nPicHeight, cType, mMargin)
- {
- var mCoordMember = {
- left: nWidthBasic
- , top: nHeightBasic
- , right: nWidthBasic + nPicWidth
- , bottom: nHeightBasic + nPicHeight
- };
- var mPositionMember = {
- X: (mCoordMember.left - mMargin.x) / nPicWidth
- , Y: (mCoordMember.top - mMargin.y) / nPicHeight
- };
- var mItem = {
- Coord: mCoordMember
- , Position: mPositionMember
- , Type: cType
- };
- return mItem;
- }
- function _loadHurdle(sHurdle)
- {
- var nBasic = 0;
- var nWidthBasic = nBasic; //margin-left.
- var nHeightBasic = 0; //margin-top.
- //{{Change map margin above.
- var nPicWidth = 45; //Picture width is nBasic.
- var nPicHeight = 45; //Picturn height is nHeightBasic.
- //{{Change icon size above.
- var nSub;
- var nRow;
- var nCol;
- var v = sHurdle.split(';');
- var vRec = [];
- for(nSub = 0; nSub < v.length; nSub++){
- var vCrid = v[nSub].split(',');
- vRec[vRec.length] = vCrid;
- }
- for(nRow = 0; nRow < vRec.length; nRow++){
- var vCol = vRec[nRow];
- for(nCol = 0; nCol < vCol.length; nCol++){
- var cType = vCol[nCol];
- var mMargin = {x: nBasic, y: nBasic};
- vView[vView.length] = _createGrid(nWidthBasic, nHeightBasic, nPicWidth, nPicHeight, cType, mMargin);
- nWidthBasic += nPicWidth;
- }
- nHeightBasic += nPicHeight;
- nWidthBasic = nBasic;
- }
- }
- //Show map with vector 'vView'.
- function _showMap(sID)
- {
- var xDiv=document.getElementById(sID);
- var xGrid;
- var xImg;
- var nTop = 0;
- var nSub;
- var sIdPrefix = 'ID_IMG_NUM_';
- var sIdGrid = 'ID_A_NUM_';
- for(nSub = 0; nSub < vView.length; nSub++){
- var mGrid = vView[nSub];
- if(mGrid){
- var xMargin = mGrid.Coord;
- var cType = mGrid.Type;
- var xProper = mScene[cType];
- if(xProper){
- xGrid = document.createElement('a');
- xImg = document.createElement('img');
- xImg.style.position = 'absolute';
- xImg.style.marginLeft = xMargin.left;
- xImg.style.marginTop = xMargin.top;
- xImg.src = xProper[0];
- xImg.style.border = '0px solid #000000';
- xImg.id = sIdPrefix + nSub;
- xImg.style.width = 45;
- xImg.style.height = 45;
- xImg.style.display = 'block';
- xGrid.onclick = function(e){
- var xCurrentGrid = e.target;
- var sId = xCurrentGrid.id;
- var nIdAsSub = parseInt(sId.substring(sIdPrefix.length, sId.length));
- mCurrent = vView[nIdAsSub];
- if(!mCurrent){
- alert("Error 0004.");
- }
- };
- xGrid.title = xProper[1] + '(' + parseInt(mGrid.Position.X) + ', ' + parseInt(mGrid.Position.Y+2) + ')';
- xGrid.id = sIdGrid + nSub;
- xGrid.appendChild(xImg);
- xDiv.appendChild(xGrid);
- }else{
- alert("Error: 0003.");
- }
- }else{
- alert("Error: 0002.");
- }
- }
- }
以上的代碼很簡單,自己可以看看,提示一下:當(dāng)你在自己開發(fā)的過程中如果彈出一個Error: 0002, Error: 0003, Error: 0001什么之類的,就代表出了錯,需要馬上去檢查。這是為了在麻煩的程序開發(fā)中有一點提醒而設(shè)計的。值得注意的是:這里的圖片全是createElement弄出來的,所以請不要猜疑html代碼里有什么蹊蹺。
接著看:
- function _showHurdle(nHurdle)
- {
- if(vHurdles[nHurdle - 1]){
- _loadHurdle(vHurdles[nHurdle - 1]);
- _showMap('ID_DIV_BATTLEFIELD');
- }else{
- alert("Error: 0001.");
- }
- }
這是在你要弄出地圖的調(diào)用函數(shù),當(dāng)你在html代碼里寫上:<body onload="_showHurdle(nHurdle)">幾可以把拼的圖一下子畫出來。nHurdle就是地圖在數(shù)組vHurdles里的對應(yīng)下標(biāo),***是1,而不是0,也就是說要用***張地圖,那nHurdle就該賦值為1,調(diào)用是寫為:<body onload="_showHurdle(1)">。
源代碼下載:http://files.cnblogs.com/ducle/map.rar
三、演示效果
演示圖在下:
由于是靜態(tài)的,所以就不給demo了。這種方法雖然很麻煩,而且地圖塊多了就很慢,但是畢竟是種技術(shù),如果大家有什么好的方法也可以來告訴我。
原文鏈接:http://www.cnblogs.com/ducle/archive/2012/09/23/2699066.html