• 如何用H5实现好玩的2048小游戏


    Hello,我是岚尹~一个热爱技术的项目经理。不定期更新项目管理、前端以及运维相关方面的经验分享~ 如果你对我的文章感兴趣,就请动动你的小手帮赞一下哦。欢迎关注长期交流~

    一、游戏介绍

    相信大多数读者都玩过一款小游戏 2048,这款数字小游戏在几年前很是流行。2048是2014年由Gabriele Cirulli利用周末的时间写出的,果然大佬就是大佬,一个周末就写出了这么经典的游戏。游戏的玩法非常简单,利用上、下、左、右移动的方式拼出2048即算赢得游戏,因为游戏上手简单同时赢得游戏又存在一定难度,所以游戏的可玩性很高,本人当时也是十分喜爱这款游戏,下面讲解下如何用H5去实现这款游戏。

    二、游戏界面

    游戏界面十分简单,我截图了进行中的几个场景,基本由一块矩形游戏区,一个按钮及一个分数展示区域组成。

    三、代码设计

    H5代码:

    H5代码十分简单,主要用16个div代表16个数字方块,再加上另外restart按钮区,还有一个分值展示区域。

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="utf-8">
    5. <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    6. <title>2048</title>
    7. <link type="text/css" rel="stylesheet" href="css/style.css">
    8. <script type="text/javascript" language="javascript" src="js/jquery-3.6.0.min.js">
    9. document.addEventListener('plusready', function(){
    10. //console.log("所有plus api都应该在此事件发生后调用,否则会出现plus is undefined。"
    11. });
    12. </script>
    13. <script type="text/javascript" language="javascript" src="js/gamerun.js"></script>
    14. </head>
    15. <body onload="init()">
    16. <div class="authorized" >
    17. <p>2048</p></div>
    18. <div class="restart" ontouchstart="restart()">Restart </div>
    19. <div class="score">
    20. <p class="maxnum" id="num_p">最高值</p>
    21. <p class="maxnum" id="num_v">0</p>
    22. </div>
    23. <article class="game">
    24. <div class="square" id="f1"> </div>
    25. <div class="square" id="f2"> </div>
    26. <div class="square" id="f3"> </div>
    27. <div class="square" id="f4"> </div>
    28. <div class="square" id="f5"> </div>
    29. <div class="square" id="f6"> </div>
    30. <div class="square" id="f7"> </div>
    31. <div class="square" id="f8"> </div>
    32. <div class="square" id="f9"> </div>
    33. <div class="square" id="f10"></div>
    34. <div class="square" id="f11"> </div>
    35. <div class="square" id="f12"></div>
    36. <div class="square" id="f13"> </div>
    37. <div class="square" id="f14"></div>
    38. <div class="square" id="f15"> </div>
    39. <div class="square" id="f16"> </div>
    40. </article>
    41. <audio id="yin" src="media/anniu.mp3"autoplay="autoplay"> </audio>
    42. <footer></footer>
    43. </body>
    44. </html>

    js代码:

    js代码相对复杂一些,游戏处理逻辑主要可以分成以下两块:

    (1)移动处理逻辑游戏玩法是通过上下左右移动进行数字的相加变换。那么首先就会有上滑、下滑、左滑、右滑四个处理函数。上下滑动的时候,将相邻两行相同的数值进行相加;左右滑动的时候,将相邻两列相同的数值进行相加。

    (2)移动判断逻辑

            首先第一类判断逻辑是判断是否可以移动。整个游戏区是 4X4的格局分布,那么在上下左右移动的时候我们会通过相邻的两行或者两列是否存在相同的数值去判断,如果有则可以继续移动,如果没有,则不可以继续移动。

           其次,我们需要判断游戏是否可以继续,如果整个游戏区的数值方块已经被占满,上下左右都没有移动空间了,那么游戏就是结束了,game over。

    (3)其余逻辑

    其余的还剩一些比较简单的逻辑,颜色分配函数、重新开始函数、最大分值记录函数等。

    1. // JavaScript Document
    2. var flag=[false,false,false,false,false,false,false,false,
    3. false,false,false,false,false,false,false,false];
    4. var ds=document.getElementsByClassName("square");
    5. var color=["#FF9966","#FFCCCC","#CC9966","#99CCCC","#99CC99","#99CCFF","#CCCCFF","#FFCC00","#333399","#CC9999","#009999","#996666","#CCCCCC","#CCCCFF","#99CC99"];
    6. var max_num=parseInt("2");
    7. var lose_view=false;
    8. function init() //初始化界面,生成2个数字方块
    9. {
    10. var i=Math.round(Math.random()*16);
    11. var j=Math.round(Math.random()*16);
    12. while(i==j)
    13. { j=Math.round(Math.random()*16);
    14. }
    15. ds[i].style.backgroundColor="#FF7578";
    16. ds[j].style.backgroundColor="#FF7578";
    17. ds[i].innerHTML="2";
    18. ds[j].innerHTML="2";
    19. flag[i]=true;
    20. flag[j]=true;
    21. document.body.addEventListener('load',load(event),false);
    22. //var msg=parseInt(document.getElementsByClassName("authorized")[0].innerText);
    23. //alert(msg);
    24. }
    25. function max_score() //记录取得的最大分数
    26. {
    27. for (var i=0;i<ds.length;i++){
    28. var c_num = parseInt(ds[i].innerText);
    29. if( c_num > max_num )
    30. {
    31. max_num=c_num;
    32. }
    33. }
    34. document.getElementById("num_v").innerHTML= max_num;
    35. }
    36. function restart() //重新开始游戏,重新回到初始化页面
    37. {
    38. if(lose_view==true)
    39. {
    40. var $h2=document.getElementById("reset1");
    41. // var $bu2=document.getElementById("reset2");
    42. var $par2=$("body");
    43. $par2[0].removeChild($h2);
    44. // $par2[0].removeChild($bu2);
    45. lose_view=false;
    46. }
    47. for (var i=0;i<ds.length;i++)
    48. {
    49. ds[i].innerHTML="";
    50. flag[i]=false;
    51. ds[i].removeAttribute("style");
    52. }
    53. init();
    54. }
    55. function voice() //设置移动时产生的音效
    56. {
    57. var vs=document.getElementById("yin");
    58. vs.play();
    59. }
    60. function load(event) // 捕捉屏幕滑动事件
    61. {
    62. $("body").on("touchstart", function(e) {
    63. e.preventDefault();
    64. startX = e.originalEvent.changedTouches[0].pageX,
    65. startY = e.originalEvent.changedTouches[0].pageY;
    66. });
    67. $("body").on("touchend", function(e)
    68. {
    69. e.preventDefault();
    70. moveEndX = e.originalEvent.changedTouches[0].pageX,
    71. moveEndY = e.originalEvent.changedTouches[0].pageY,
    72. X = Math.floor(moveEndX - startX),
    73. Y = Math.floor(moveEndY - startY);
    74. if ( Math.abs(X)> Math.abs(Y))
    75. {
    76. if (Math.abs(X)>20)
    77. {
    78. if( X > 0)
    79. {
    80. rightgo();
    81. voice();
    82. }
    83. else
    84. {
    85. leftgo();
    86. voice();
    87. }
    88. }
    89. }
    90. else if(Math.abs(X)< Math.abs(Y))
    91. {
    92. if (Math.abs(Y)>20)
    93. {
    94. if(Y > 0)
    95. {
    96. bottomgo();
    97. voice();
    98. }
    99. else
    100. {
    101. topgo();
    102. voice();
    103. }
    104. }
    105. }
    106. }
    107. );
    108. }
    109. function addcolor() //设置方块颜色
    110. { for(var i=0;i<ds.length;i++)
    111. {
    112. var ss=parseInt(ds[i].innerHTML);
    113. switch(ss)
    114. {
    115. case 2:
    116. ds[i].style.backgroundColor=color[0];
    117. break;
    118. case 4:
    119. ds[i].style.backgroundColor=color[1];
    120. break;
    121. case 8:
    122. ds[i].style.backgroundColor=color[2];
    123. break;
    124. case 16:
    125. ds[i].style.backgroundColor=color[3];
    126. break;
    127. case 32:
    128. ds[i].style.backgroundColor=color[4];
    129. break;
    130. case 64:
    131. ds[i].style.backgroundColor=color[5];
    132. break;
    133. case 128:
    134. ds[i].style.backgroundColor=color[6];
    135. break;
    136. case 256:
    137. ds[i].style.backgroundColor=color[7];
    138. break;
    139. case 512:
    140. ds[i].style.backgroundColor=color[8];
    141. break;
    142. case 1024:
    143. ds[i].style.backgroundColor=color[9];
    144. break;
    145. case 2048:
    146. ds[i].style.backgroundColor=color[10];
    147. break;
    148. case 4096:
    149. ds[i].style.backgroundColor=color[11];
    150. break;
    151. case 8192:
    152. ds[i].style.backgroundColor=color[12];
    153. break;
    154. case 16384:
    155. ds[i].style.backgroundColor=color[13];
    156. break;
    157. default:
    158. break;
    159. }
    160. }
    161. }
    162. function istop()
    163. { //判断是否可以继续上移,true则表示可以
    164. var istop=false;
    165. for(var i1=4;i1<16;i1++)
    166. {
    167. if(flag[i1]==true)
    168. {
    169. if(flag[i1-4]==true)
    170. {
    171. if( ds[i1].innerHTML==ds[i1-4].innerHTML )
    172. {
    173. istop=true;
    174. }
    175. }
    176. else
    177. {
    178. istop=true;
    179. }
    180. }
    181. }
    182. return istop;
    183. }
    184. function isbottom() //判断是否可以继续下移,true则表示可以
    185. {
    186. var isbottom=false;
    187. for(var i2=11;i2>=0;i2--)
    188. {
    189. if(flag[i2]==true)
    190. {
    191. if(flag[i2+4]==true)
    192. {
    193. if( ds[i2].innerHTML==ds[i2+4].innerHTML )
    194. {
    195. isbottom=true;
    196. }
    197. }
    198. else
    199. {
    200. isbottom=true;
    201. }
    202. }
    203. }
    204. return isbottom;
    205. }
    206. function isleft(){ //判断是否可以继续左移,true则表示可以
    207. var isleft=false;
    208. for(var i3=0;i3<4;i3++)
    209. {
    210. for(var j3=0;j3<3;j3++)
    211. {
    212. if(flag[j3+4*i3+1]==true)
    213. {
    214. if(flag[j3+4*i3]==true)
    215. {
    216. if( ds[j3+4*i3].innerHTML==ds[j3+4*i3+1].innerHTML )
    217. {
    218. isleft=true;
    219. }
    220. }
    221. else
    222. {
    223. isleft=true;
    224. }
    225. }
    226. }
    227. }
    228. return isleft;
    229. }
    230. function isright(){ //判断是否可以继续右移,true则表示可以
    231. var isright=false;
    232. for(var i4=3;i4>=0;i4--)
    233. {
    234. for(var j4=2;j4>=0;j4--)
    235. {
    236. if(flag[j4+4*i4]==true)
    237. {
    238. if(flag[j4+4*i4+1]==true)
    239. {
    240. if( ds[j4+4*i4].innerHTML==ds[j4+4*i4+1].innerHTML )
    241. {
    242. isright=true;
    243. }
    244. }
    245. else
    246. {
    247. isright=true;
    248. }
    249. }
    250. }
    251. }
    252. return isright;
    253. }
    254. function islose()
    255. { //判断是否输掉游戏
    256. if (lose_view==false) {
    257. var los=0;
    258. for(var i5=0;i5<16;i5++)
    259. { if(flag[i5]==true)
    260. {
    261. los++;
    262. }
    263. }
    264. if(los==16)
    265. {
    266. var left=isleft();
    267. var right=isright();
    268. var top=istop();
    269. var bottom=isbottom();
    270. if((left||right||top||bottom)==false)
    271. {
    272. max_score();
    273. var $h1=$('<div>');
    274. //var $bu1=$('<button>');
    275. var $par=$("body");
    276. $h1.attr("id","reset1");
    277. $h1.text('Game Over !');
    278. //$bu1.text('重新开始');
    279. //$bu1.attr("id","reset2");
    280. $par.append($h1);
    281. //$par.append($bu1);
    282. var sleep = function(time) {
    283. var startTime = new Date().getTime() + parseInt(time, 10);
    284. while(new Date().getTime() < startTime) {}
    285. };
    286. lose_view=true;
    287. //跳出失败提醒界面
    288. //$bu1.click(restart());
    289. //$("body").on("touchend",restart());
    290. //$("body").on("touchstart",function (){
    291. // $par[0].removeChild($h1[0]);
    292. // $par[0].removeChild($bu1[0]);
    293. // restart();
    294. // });
    295. }
    296. }
    297. }
    298. }
    299. function topgo(){
    300. //上移
    301. var top1=istop();
    302. for(var ss6=0;ss6<3;ss6++)
    303. {
    304. for(var i6=4;i6<16;i6++)
    305. {
    306. if(flag[i6]==true)
    307. {
    308. if(flag[i6-4]==true)
    309. {
    310. if( ds[i6].innerHTML==ds[i6-4].innerHTML )
    311. { var sum=parseInt(ds[i6].innerHTML)*2;
    312. ds[i6-4].innerHTML=sum;
    313. flag[i6]=false;
    314. ds[i6].removeAttribute("style");
    315. ds[i6].innerHTML="";
    316. }
    317. }
    318. else
    319. {
    320. ds[i6-4].innerHTML=ds[i6].innerHTML;
    321. ds[i6-4].style.backgroundColor="#FF9E3E";
    322. flag[i6-4]=true;
    323. ds[i6].innerHTML="";
    324. ds[i6].removeAttribute("style");
    325. flag[i6]=false;
    326. }
    327. }
    328. }
    329. }
    330. if(top1==true)
    331. {
    332. var kk1=Math.round(Math.random()*15);
    333. while(flag[kk1]==true)
    334. {
    335. var kk1=Math.round(Math.random()*15);
    336. }
    337. ds[kk1].innerHTML="2";
    338. flag[kk1]=true;
    339. addcolor();
    340. }
    341. else
    342. {
    343. islose();
    344. }
    345. }
    346. function leftgo(){ //左移
    347. var left1=isleft();
    348. for(var ss7=0;ss7<3;ss7++)
    349. {
    350. for(var i7=0;i7<4;i7++)
    351. {
    352. for(var j7=0;j7<3;j7++)
    353. {
    354. if(flag[j7+4*i7+1]==true)
    355. {
    356. if(flag[j7+4*i7]==true)
    357. {
    358. if( ds[j7+4*i7].innerHTML==ds[j7+4*i7+1].innerHTML )
    359. { var sum=parseInt(ds[j7+4*i7+1].innerHTML)*2;
    360. ds[j7+4*i7].innerHTML=sum;
    361. flag[j7+4*i7+1]=false;
    362. ds[j7+4*i7+1].removeAttribute("style");
    363. ds[j7+4*i7+1].innerHTML="";
    364. }
    365. }
    366. else
    367. {
    368. ds[j7+4*i7].innerHTML=ds[j7+4*i7+1].innerHTML;
    369. ds[j7+4*i7].style.backgroundColor="#FF9E3E";
    370. flag[j7+4*i7]=true;
    371. ds[j7+4*i7+1].innerHTML="";
    372. ds[j7+4*i7+1].removeAttribute("style");
    373. flag[j7+4*i7+1]=false;
    374. }
    375. }
    376. }
    377. }
    378. }
    379. if(left1==true){
    380. var kk2=Math.round(Math.random()*15);
    381. while(flag[kk2]==true)
    382. {
    383. var kk2=Math.round(Math.random()*15);
    384. }
    385. ds[kk2].style.backgroundColor="#FF9E3E";
    386. ds[kk2].innerHTML="2";
    387. flag[kk2]=true;
    388. addcolor();
    389. }
    390. else
    391. {
    392. islose();
    393. }
    394. }
    395. function rightgo(){ //右移
    396. var right1=isright();
    397. for(var ss8=0;ss8<3;ss8++)
    398. {
    399. for(var i8=3;i8>=0;i8--)
    400. {
    401. for(var j8=2;j8>=0;j8--)
    402. {
    403. if(flag[j8+4*i8]==true)
    404. {
    405. if(flag[j8+4*i8+1]==true)
    406. {
    407. if( ds[j8+4*i8].innerHTML==ds[j8+4*i8+1].innerHTML )
    408. { var sum=parseInt(ds[j8+4*i8+1].innerHTML)*2;
    409. ds[j8+4*i8+1].innerHTML=sum;
    410. flag[j8+4*i8]=false;
    411. ds[j8+4*i8].removeAttribute("style");
    412. ds[j8+4*i8].innerHTML="";
    413. }
    414. }
    415. else
    416. {
    417. ds[j8+4*i8+1].innerHTML=ds[j8+4*i8].innerHTML;
    418. ds[j8+4*i8+1].style.backgroundColor="#FF9E3E";
    419. flag[j8+4*i8+1]=true;
    420. ds[j8+4*i8].innerHTML="";
    421. ds[j8+4*i8].removeAttribute("style");
    422. flag[j8+4*i8]=false;
    423. }
    424. }
    425. }
    426. }}
    427. if(right1==true){
    428. var kk3=Math.round(Math.random()*15);
    429. while(flag[kk3]==true)
    430. {
    431. var kk3=Math.round(Math.random()*15);
    432. }
    433. ds[kk3].style.backgroundColor="#FF9E3E";
    434. ds[kk3].innerHTML="2";
    435. flag[kk3]=true;
    436. addcolor();
    437. }
    438. else
    439. {
    440. islose();
    441. }
    442. // var ss=max_score();
    443. //alert(ss);
    444. }
    445. function bottomgo(){ //下移
    446. var bottom1=isbottom();
    447. for(var ss9=0;ss9<3;ss9++)
    448. {
    449. for(var i9=11;i9>=0;i9--)
    450. {
    451. if(flag[i9]==true)
    452. {
    453. if(flag[i9+4]==true)
    454. {
    455. if( ds[i9].innerHTML==ds[i9+4].innerHTML )
    456. { var sum=parseInt(ds[i9].innerHTML)*2;
    457. ds[i9+4].innerHTML=sum;
    458. flag[i9]=false;
    459. ds[i9].removeAttribute("style");
    460. ds[i9].innerHTML="";
    461. }
    462. }
    463. else
    464. {
    465. ds[i9+4].innerHTML=ds[i9].innerHTML;
    466. ds[i9+4].style.backgroundColor="#FF9E3E";
    467. flag[i9+4]=true;
    468. ds[i9].innerHTML="";
    469. ds[i9].removeAttribute("style");
    470. flag[i9]=false;
    471. }
    472. }
    473. }
    474. }
    475. if(bottom1==true){
    476. var kk4=Math.round(Math.random()*15);
    477. while(flag[kk4]==true)
    478. {
    479. var kk4=Math.round(Math.random()*15);
    480. }
    481. ds[kk4].style.backgroundColor="#FF9E3E";
    482. ds[kk4].innerHTML="2";
    483. flag[kk4]=true;
    484. addcolor()
    485. }
    486. else
    487. {
    488. islose();
    489. }
    490. }

    css代码:

    1. @charset "utf-8";
    2. /* CSS Document */
    3. body,html{
    4. height:100vw;
    5. width: 100vw;
    6. padding: 0;
    7. margin: 0;
    8. background:fixed;
    9. background-color: papayawhip;
    10. }
    11. .authorized {
    12. color: grey;
    13. margin-left: 2%;
    14. text-align:center;
    15. font-style: normal;
    16. font-size:210%;
    17. margin-top:10%;
    18. width: 30%;
    19. }
    20. .restart {
    21. position: absolute;
    22. margin-left: 5%;
    23. width: 25vw;
    24. height: 10vw;
    25. padding-top:5%;
    26. text-align:center;
    27. font-size: 22px;
    28. background-color:#99CCCC;
    29. color: white;
    30. border-radius: 5% 5% 5% 5%;
    31. }
    32. .score {
    33. position: absolute;
    34. margin-left: 50%;
    35. padding-top: 2%;
    36. width:45vw ;
    37. background-color:#99CCCC;
    38. color: white;
    39. border-radius:5% 5% 5% 5%;
    40. }
    41. .maxnum {
    42. position: relative;
    43. float:left;
    44. margin-left: 3%;
    45. font-size: 120%;
    46. text-align: center;
    47. color: white;
    48. width: 40%;
    49. height: 4vw;
    50. }
    51. #num_p {
    52. margin-top: 5%;
    53. margin-left: 5%;
    54. }
    55. #num_v {
    56. margin-top: 5%;
    57. color:#ffd700;
    58. font-size: 25px;}
    59. .game{
    60. position:absolute;
    61. top:30% ;
    62. left:2.5%;
    63. width:95vw;
    64. height:95vw;
    65. background-color:#AAAA;
    66. border-radius:5% 5% 5% 5%;
    67. }
    68. #reset1{
    69. position:absolute;
    70. top:30% ;
    71. left:2.5%;
    72. width:95vw;
    73. height:60vw;
    74. font-size: 40px;
    75. background: rgba(250,250,250,0.5);
    76. text-align: center;
    77. padding-top: 35%;
    78. color: grey;
    79. border-radius:5% 5% 5% 5%;}
    80. #reset2 {
    81. position:absolute;
    82. width: 30vw;
    83. height: 13vw;
    84. margin:70% 20% 30% 35%;
    85. font-size: 20px;
    86. padding-left: 0.125rem;
    87. background-color: lightsalmon;
    88. font-weight: bold;
    89. color: white;
    90. border-radius:5% 5% 5% 5%;
    91. }
    92. .square{
    93. position:relative;
    94. width:23%;
    95. height:23%;
    96. line-height: 180%; /*行高,指的是两行文字基线之间的距离,又可以称为这行文字所占的高度 */
    97. float:left;
    98. background-color:#DDDDDD;
    99. border-radius:15% 15% 15% 15%;
    100. margin:1% 1% 1% 1%;
    101. color:white;
    102. text-align:center;
    103. font-size:12vw;
    104. }

    四、源码及APK

    可以利用Hbuilder将H5代码打包成移动端应用,这样我们就可以在手机端安装APP愉快的玩耍了。下面是我打包的安卓包,点击自取:

    链接: https://pan.baidu.com/s/1ooirqPYvZpDx4CfVq2jJ5Q?pwd=c3wy 提取码: c3wy 复制这段内容后打开百度网盘手机App,操作更方便哦

  • 相关阅读:
    给echart.js折线图设置滚动条
    多御安全浏览器更新隐私锁,个人隐私有救了
    Golang中的GC原理(介于三个不同版本)
    ROS -话题通信示例
    ES6语法新特性(上)
    六、OpenAI之嵌入式(Embedding)
    服务发现原理分析与源码解读
    C/C++ 基础知识总结
    c++多线程(一)线程管理
    素数算法(Prime Num Algorithm)
  • 原文地址:https://blog.csdn.net/Nicolege678/article/details/125501448