小時候我們玩過拼圖游戲,是用自己的手去拼的。今天我們來研究研究用javascript來拼圖。同樣是拼圖,但用js拼圖要比用手拼圖麻煩多了,因此以后我要把它優化成引擎。
一、前言
以上是一段導語,話不扯遠,對《三國志曹操傳》熟悉的玩家知道,《三國志曹操傳》的地圖是由小地圖塊拼成的,那要實現它就和導語說得一樣:很麻煩。不過即使麻煩也是一門技術,因此在此分享給大家,希望大家喜歡。
前幾章的位置:
用Javascript開發《三國志曹操傳》-零部件開發(三)-人物對話中,仿打字機輸出文字
http://www.cnblogs.com/ducle/archive/2012/09/15/2686532.html
用Javascript開發《三國志曹操傳》-零部件開發(二)-讓目標人物移動
http://www.cnblogs.com/ducle/archive/2012/09/08/2677127.html
用Javascript開發《三國志曹操傳》-零部件開發(一)-讓靜態人物動起來
http://www.cnblogs.com/ducle/archive/2012/09/02/2667481.html
二、代碼講解
今天我要換換講解方式,先不給代碼,我們先來想想原理。現在,假如你有一幅圖片,把它裁開成若干份,並打亂。現在如果讓你用js把他們組織起來,如何做呢?先不說圖的順序,首先來看把它們弄在一起就很難了。這時我減少難度,給你幾個選擇:
A.用margin慢慢調 B.用數組把它們排列好 C.放棄
在這道題中,選A是很不明智的,選C就代表你也拿不定主意。看來選B是最好的。既然都告訴大家用數組,那就先上代碼吧。免得消磨大家興致。
js代碼:
1 /* 2 *Prompt: 3 *If you want to add hurdle, find string: "{{Add hurdle above." and "{{After add hurdle, add the hurdle to the vector above." please. 4 *If you want to add or change type of grid, find string: "{{Add new grid above.". 5 *If you want to change position of map, please find string: "{{Change map margin above.". 6 *If the icon of crid is changed, you have to change the size of icon. Find "{{Change icon size above." to change size. 7 */ 8 9 //Map of hurdle or military or resource. 10 var vView = []; 11 12 /*Remarks: 13 *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 14 *r: research Center *P: port *D: dock *s: Shipyard 15 */ 16 var mScene = { 17 'L': ['./land.png', '陸地'] 18 , 'S': ['./sea.png', '河流'] 19 , 'T': ['./tree.png', '樹木'] 20 , 'B': ['./bridge.png', '橋'] 21 , 'C': ['./beach.png', '沙灘'] 22 }; 23 //{{Add new grid above. 24 25 var mCurrent = { 26 Margin: { 27 left: -1 28 , top: -1 29 , right: -1 30 , bottom: -1 31 } 32 , Position: { 33 X: -1 34 , Y: -1 35 } 36 , Type: 'NONE' 37 38 }; 39 var mTitle = {}; 40 41 var sHurdleONE = 42 'S,S,S,S,S,S,S,S,S,S,S' 43 + ';T,L,T,T,T,T,S,S,S,S,T' 44 + ';T,L,L,T,S,S,S,S,S,L,T' 45 + ';T,L,L,L,C,C,C,S,S,T,S' 46 + ';T,L,L,L,C,C,C,B,B,L,T' 47 + ';T,L,L,C,C,C,C,S,S,L,T' 48 + ';T,L,L,C,C,T,S,S,L,L,T' 49 ; 50 //{{Add hurdle above. 51 52 var vHurdles = [sHurdleONE]; 53 //{{After add hurdle, add the hurdle to the vector above. 54 55 function _createGrid(nWidthBasic, nHeightBasic, nPicWidth, nPicHeight, cType, mMargin) 56 { 57 var mCoordMember = { 58 left: nWidthBasic 59 , top: nHeightBasic 60 , right: nWidthBasic + nPicWidth 61 , bottom: nHeightBasic + nPicHeight 62 }; 63 var mPositionMember = { 64 X: (mCoordMember.left - mMargin.x) / nPicWidth 65 , Y: (mCoordMember.top - mMargin.y) / nPicHeight 66 }; 67 var mItem = { 68 Coord: mCoordMember 69 , Position: mPositionMember 70 , Type: cType 71 }; 72 73 return mItem; 74 } 75 76 function _loadHurdle(sHurdle) 77 { 78 var nBasic = 0; 79 var nWidthBasic = nBasic; //margin-left. 80 var nHeightBasic = 0; //margin-top. 81 82 //{{Change map margin above. 83 84 var nPicWidth = 45; //Picture width is nBasic. 85 var nPicHeight = 45; //Picturn height is nHeightBasic. 86 //{{Change icon size above. 87 88 var nSub; 89 var nRow; 90 var nCol; 91 92 var v = sHurdle.split(';'); 93 var vRec = []; 94 95 for(nSub = 0; nSub < v.length; nSub++){ 96 var vCrid = v[nSub].split(','); 97 vRec[vRec.length] = vCrid; 98 } 99 100 for(nRow = 0; nRow < vRec.length; nRow++){ 101 var vCol = vRec[nRow]; 102 103 for(nCol = 0; nCol < vCol.length; nCol++){ 104 var cType = vCol[nCol]; 105 var mMargin = {x: nBasic, y: nBasic}; 106 107 vView[vView.length] = _createGrid(nWidthBasic, nHeightBasic, nPicWidth, nPicHeight, cType, mMargin); 108 109 nWidthBasic += nPicWidth; 110 } 111 112 nHeightBasic += nPicHeight; 113 nWidthBasic = nBasic; 114 } 115 } 116 117 118 119 //Show map with vector 'vView'. 120 function _showMap(sID) 121 { 122 var xDiv=document.getElementById(sID); 123 124 var xGrid; 125 var xImg; 126 127 128 var nTop = 0; 129 130 var nSub; 131 var sIdPrefix = 'ID_IMG_NUM_'; 132 var sIdGrid = 'ID_A_NUM_'; 133 for(nSub = 0; nSub < vView.length; nSub++){ 134 var mGrid = vView[nSub]; 135 136 if(mGrid){ 137 var xMargin = mGrid.Coord; 138 var cType = mGrid.Type; 139 var xProper = mScene[cType]; 140 141 if(xProper){ 142 xGrid = document.createElement('a'); 143 xImg = document.createElement('img'); 144 145 xImg.style.position = 'absolute'; 146 xImg.style.marginLeft = xMargin.left; 147 xImg.style.marginTop = xMargin.top; 148 149 xImg.src = xProper[0]; 150 151 xImg.style.border = '0px solid #000000'; 152 xImg.id = sIdPrefix + nSub; 153 154 xImg.style.width = 45; 155 xImg.style.height = 45; 156 157 xImg.style.display = 'block'; 158 159 xGrid.onclick = function(e){ 160 var xCurrentGrid = e.target; 161 var sId = xCurrentGrid.id; 162 var nIdAsSub = parseInt(sId.substring(sIdPrefix.length, sId.length)); 163 164 mCurrent = vView[nIdAsSub]; 165 if(!mCurrent){ 166 alert("Error 0004."); 167 } 168 }; 169 xGrid.title = xProper[1] + '(' + parseInt(mGrid.Position.X) + ', ' + parseInt(mGrid.Position.Y+2) + ')'; 170 xGrid.id = sIdGrid + nSub; 171 172 xGrid.appendChild(xImg); 173 174 xDiv.appendChild(xGrid); 175 }else{ 176 alert("Error: 0003."); 177 } 178 }else{ 179 alert("Error: 0002."); 180 } 181 } 182 } 183 184 //Show map of hurdle. 185 function _showHurdle(nHurdle) 186 { 187 if(vHurdles[nHurdle - 1]){ 188 _loadHurdle(vHurdles[nHurdle - 1]); 189 _showMap('ID_DIV_BATTLEFIELD'); 190 }else{ 191 alert("Error: 0001."); 192 } 193 } 194 195
看看,這點程序就用了195行,而且這還是一張地圖,看來還很有點麻煩哦。沒關系,慢慢解釋。
首先還是把素材放在這里,當然也可以去我的相冊下載下來。
![]() |
![]() |
![]() |
![]() |
![]() |
tree.png | land.png | sea.png | bridge.png | beach.png |
素材不是來自《三國志曹操傳》,因為沒整理好《三國志曹操傳》的地圖素材,所以就隨便找了些。不過也照樣可以用。希望大家不要介意。
麻煩的代碼最容易弄得亂七八糟,因此在此時要良好的區分開樣式設置和拼圖核心。
拼圖核心在哪里呢???在這里:
1 var mScene = { 2 'L': ['./land.png', '陸地'] 3 , 'S': ['./sea.png', '河流'] 4 , 'T': ['./tree.png', '樹木'] 5 , 'B': ['./bridge.png', '橋'] 6 , 'C': ['./beach.png', '沙灘'] 7 }; 8 //{{Add new grid above. 9 10 var mCurrent = { 11 Margin: { 12 left: -1 13 , top: -1 14 , right: -1 15 , bottom: -1 16 } 17 , Position: { 18 X: -1 19 , Y: -1 20 } 21 , Type: 'NONE' 22 23 }; 24 var mTitle = {}; 25 26 var sHurdleONE = 27 'S,S,S,S,S,S,S,S,S,S,S' 28 + ';T,L,T,T,T,T,S,S,S,S,T' 29 + ';T,L,L,T,S,S,S,S,S,L,T' 30 + ';T,L,L,L,C,C,C,S,S,T,S' 31 + ';T,L,L,L,C,C,C,B,B,L,T' 32 + ';T,L,L,C,C,C,C,S,S,L,T' 33 + ';T,L,L,C,C,T,S,S,L,L,T' 34 ; 35 //{{Add hurdle above. 36 37 var vHurdles = [sHurdleONE]; 38 //{{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的,所以也不解釋了。關鍵是在下:
1 var sHurdleONE = 2 'S,S,S,S,S,S,S,S,S,S,S' 3 + ';T,L,T,T,T,T,S,S,S,S,T' 4 + ';T,L,L,T,S,S,S,S,S,L,T' 5 + ';T,L,L,L,C,C,C,S,S,T,S' 6 + ';T,L,L,L,C,C,C,B,B,L,T' 7 + ';T,L,L,C,C,C,C,S,S,L,T' 8 + ';T,L,L,C,C,T,S,S,L,L,T' 9 ;
這段代碼就是把定義好的S,T,B,C,L連在一起的核心。后面只用定義S,T,B,C,L的寬度高度定義就能把它們連成一塊。並且只要把它們在數組里的位置調一調就能改變樣式。
接下來為了能切換地圖,我們把第一張地圖放進了數組:
1 var vHurdles = [sHurdleONE]; 2 //{{After add hurdle, add the hurdle to the vector above.
如果以后加了地圖,只用把地圖所屬的數組名加到vHurdles數組就可以了,調用是就可以直接寫對應下標。
樣式設置在下:
1 function _createGrid(nWidthBasic, nHeightBasic, nPicWidth, nPicHeight, cType, mMargin) 2 { 3 var mCoordMember = { 4 left: nWidthBasic 5 , top: nHeightBasic 6 , right: nWidthBasic + nPicWidth 7 , bottom: nHeightBasic + nPicHeight 8 }; 9 var mPositionMember = { 10 X: (mCoordMember.left - mMargin.x) / nPicWidth 11 , Y: (mCoordMember.top - mMargin.y) / nPicHeight 12 }; 13 var mItem = { 14 Coord: mCoordMember 15 , Position: mPositionMember 16 , Type: cType 17 }; 18 19 return mItem; 20 } 21 22 function _loadHurdle(sHurdle) 23 { 24 var nBasic = 0; 25 var nWidthBasic = nBasic; //margin-left. 26 var nHeightBasic = 0; //margin-top. 27 28 //{{Change map margin above. 29 30 var nPicWidth = 45; //Picture width is nBasic. 31 var nPicHeight = 45; //Picturn height is nHeightBasic. 32 //{{Change icon size above. 33 34 var nSub; 35 var nRow; 36 var nCol; 37 38 var v = sHurdle.split(';'); 39 var vRec = []; 40 41 for(nSub = 0; nSub < v.length; nSub++){ 42 var vCrid = v[nSub].split(','); 43 vRec[vRec.length] = vCrid; 44 } 45 46 for(nRow = 0; nRow < vRec.length; nRow++){ 47 var vCol = vRec[nRow]; 48 49 for(nCol = 0; nCol < vCol.length; nCol++){ 50 var cType = vCol[nCol]; 51 var mMargin = {x: nBasic, y: nBasic}; 52 53 vView[vView.length] = _createGrid(nWidthBasic, nHeightBasic, nPicWidth, nPicHeight, cType, mMargin); 54 55 nWidthBasic += nPicWidth; 56 } 57 58 nHeightBasic += nPicHeight; 59 nWidthBasic = nBasic; 60 } 61 } 62 63 64 65 //Show map with vector 'vView'. 66 function _showMap(sID) 67 { 68 var xDiv=document.getElementById(sID); 69 70 var xGrid; 71 var xImg; 72 73 74 var nTop = 0; 75 76 var nSub; 77 var sIdPrefix = 'ID_IMG_NUM_'; 78 var sIdGrid = 'ID_A_NUM_'; 79 for(nSub = 0; nSub < vView.length; nSub++){ 80 var mGrid = vView[nSub]; 81 82 if(mGrid){ 83 var xMargin = mGrid.Coord; 84 var cType = mGrid.Type; 85 var xProper = mScene[cType]; 86 87 if(xProper){ 88 xGrid = document.createElement('a'); 89 xImg = document.createElement('img'); 90 91 xImg.style.position = 'absolute'; 92 xImg.style.marginLeft = xMargin.left; 93 xImg.style.marginTop = xMargin.top; 94 95 xImg.src = xProper[0]; 96 97 xImg.style.border = '0px solid #000000'; 98 xImg.id = sIdPrefix + nSub; 99 100 xImg.style.width = 45; 101 xImg.style.height = 45; 102 103 xImg.style.display = 'block'; 104 105 xGrid.onclick = function(e){ 106 var xCurrentGrid = e.target; 107 var sId = xCurrentGrid.id; 108 var nIdAsSub = parseInt(sId.substring(sIdPrefix.length, sId.length)); 109 110 mCurrent = vView[nIdAsSub]; 111 if(!mCurrent){ 112 alert("Error 0004."); 113 } 114 }; 115 xGrid.title = xProper[1] + '(' + parseInt(mGrid.Position.X) + ', ' + parseInt(mGrid.Position.Y+2) + ')'; 116 xGrid.id = sIdGrid + nSub; 117 118 xGrid.appendChild(xImg); 119 120 xDiv.appendChild(xGrid); 121 }else{ 122 alert("Error: 0003."); 123 } 124 }else{ 125 alert("Error: 0002."); 126 } 127 } 128 }
以上的代碼很簡單,自己可以看看,提示一下:當你在自己開發的過程中如果彈出一個Error: 0002, Error: 0003, Error: 0001什么之類的,就代表出了錯,需要馬上去檢查。這是為了在麻煩的程序開發中有一點提醒而設計的。值得注意的是:這里的圖片全是createElement弄出來的,所以請不要猜疑html代碼里有什么蹊蹺。
接着看:
1 function _showHurdle(nHurdle) 2 { 3 if(vHurdles[nHurdle - 1]){ 4 _loadHurdle(vHurdles[nHurdle - 1]); 5 _showMap('ID_DIV_BATTLEFIELD'); 6 }else{ 7 alert("Error: 0001."); 8 } 9 }
這是在你要弄出地圖的調用函數,當你在html代碼里寫上:<body onload="_showHurdle(nHurdle)">幾可以把拼的圖一下子畫出來。nHurdle就是地圖在數組vHurdles里的對應下標,最低是1,而不是0,也就是說要用第一張地圖,那nHurdle就該賦值為1,調用是寫為:<body onload="_showHurdle(1)">。
源代碼下載:http://files.cnblogs.com/ducle/map.rar
三、演示效果
演示圖在下:
由於是靜態的,所以就不給demo了。這種方法雖然很麻煩,而且地圖塊多了就很慢,但是畢竟是種技術,如果大家有什么好的方法也可以來告訴我。
希望大家多支持。謝謝。