<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
很早就知道canvas,當時一直沒應用到,最近閒來無事就順便寫寫看。吃豆遊戲可以說是我們80,90後共同的回憶錄,小時候常常在學習機上玩,所以也就有了強烈的慾望去寫。為了寫這個遊戲,看了很多吃豆人遊戲的相關資訊,包括遊戲規則,歷史,其它相關遊戲設計。又去了各個小遊戲平臺玩了下,找些靈感!!!就連最簡單的配色,也是反覆斟酌幾個小時確定的方案……哈哈,不容易啊程式碼分兩部分,遊戲引擎和遊戲主程式,引擎用來類的定義,管理動畫場景,事件定義等等……遊戲主程式主要組織業務邏輯。算物件導向程式設計吧,純手工打造是男人就吃光所有豆子!!!
專案結構
因需要四個檔案即可實現,index.html 遊戲主頁,index.js 主頁控制,game.js 遊戲控制,favicon.png 圖示。程式碼簡單,易懂。後續小夥伴們還可以自己加入音樂!可以部署到伺服器上,也可用瀏覽器直接開啟index.html玩耍!!!
<html> <head> <meta charset="utf8"> <title>Pac-Man . 吃豆遊戲</title> <link rel="shortcut icon" href="favicon.png" rel="external nofollow" > <style> body{background-color: #292929} *{padding:0;margin:0;} .wrapper{ width: 960px; margin:0 auto; line-height:36px; text-align:center; color:#999; } canvas{display:block;background: #000;} .mod-botton{ height: 32px; padding: 15px 0; text-align: center; } </style> </head> <body> <div class="wrapper"> <canvas id="canvas" width="960" height="640">不支援畫布</canvas> <p>按[空格]暫停或繼續</p> </div> <script src="game.js"></script> <script src="index.js"></script> </body> </html>
//主程式,業務邏輯 (function(){ var _DATA = [ //地圖資料 [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1], [1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1], [1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1], [1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1], [1,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1], [1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1], [1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1], [1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1], [1,1,1,1,1,1,0,1,1,0,1,1,1,2,2,1,1,1,0,1,1,0,1,1,1,1,1,1], [1,1,1,1,1,1,0,1,1,0,1,2,2,2,2,2,2,1,0,1,1,0,1,1,1,1,1,1], [0,0,0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0], [1,1,1,1,1,1,0,1,1,0,1,2,2,2,2,2,2,1,0,1,1,0,1,1,1,1,1,1], [1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1], [1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1], [1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1], [1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1], [1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1], [1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1], [1,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,1], [1,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,1], [1,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1], [1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1], [1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] ], _GOODS = { //能量豆 '1,3':1, '26,3':1, '1,23':1, '26,23':1 }, _COS = [1,0,-1,0], _SIN = [0,1,0,-1], _COLOR = ['#F00','#F93','#0CF','#F9C'],//紅,橙, _LIFE = 3, _SCORE = 0; //得分 var game = new Game('canvas'); //啟動頁 (function(){ var stage = game.createStage(); //logo stage.createItem({ x:game.width/2, y:game.height*.45, width:100, height:100, frames:3, draw:function(context){ var t = Math.abs(5-this.times%10); context.fillStyle = '#FFE600'; context.beginPath(); context.arc(this.x,this.y,this.width/2,t*.04*Math.PI,(2-t*.04)*Math.PI,false); context.lineTo(this.x,this.y); context.closePath(); context.fill(); context.fillStyle = '#000'; context.beginPath(); context.arc(this.x+5,this.y-27,7,0,2*Math.PI,false); context.closePath(); context.fill(); } }); //遊戲名 stage.createItem({ x:game.width/2, y:game.height*.6, draw:function(context){ context.font = 'bold 42px Helvetica'; context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillStyle = '#FFF'; context.fillText('Pac-Man',this.x,this.y); } }); //版權資訊 stage.createItem({ x:game.width-12, y:game.height-5, draw:function(context){ context.font = '14px Helvetica'; context.textAlign = 'right'; context.textBaseline = 'bottom'; context.fillStyle = '#AAA'; context.fillText('© passer-by.com',this.x,this.y); } }); //事件繫結 stage.bind('keydown',function(e){ switch(e.keyCode){ case 13: case 32: game.nextStage(); break; } }); })(); //遊戲主程式 (function(){ var stage,map,beans,player,times; stage = game.createStage({ update:function(){ var stage = this; if(stage.status==1){ //場景正常執行 items.forEach(function(item){ if(map&&!map.get(item.coord.x,item.coord.y)&&!map.get(player.coord.x,player.coord.y)){ var dx = item.x-player.x; var dy = item.y-player.y; if(dx*dx+dy*dy<750&&item.status!=4){ //物體檢測 if(item.status==3){ item.status = 4; _SCORE += 10; }else{ stage.status = 3; stage.timeout = 30; } } } }); if(JSON.stringify(beans.data).indexOf(0)<0){ //當沒有物品的時候,進入結束畫面 game.nextStage(); } }else if(stage.status==3){ //場景臨時狀態 if(!stage.timeout){ _LIFE--; if(_LIFE){ stage.resetItems(); }else{ game.nextStage(); return false; } } } } }); //繪製地圖 map = stage.createMap({ x:60, y:10, data:_DATA, cache:true, draw:function(context){ context.lineWidth = 2; for(var j=0; j<this.y_length; j++){ for(var i=0; i<this.x_length; i++){ var value = this.get(i,j); if(value){ var code = [0,0,0,0]; if(this.get(i+1,j)&&!(this.get(i+1,j-1)&&this.get(i+1,j+1)&&this.get(i,j-1)&&this.get(i,j+1))){ code[0]=1; } if(this.get(i,j+1)&&!(this.get(i-1,j+1)&&this.get(i+1,j+1)&&this.get(i-1,j)&&this.get(i+1,j))){ code[1]=1; } if(this.get(i-1,j)&&!(this.get(i-1,j-1)&&this.get(i-1,j+1)&&this.get(i,j-1)&&this.get(i,j+1))){ code[2]=1; } if(this.get(i,j-1)&&!(this.get(i-1,j-1)&&this.get(i+1,j-1)&&this.get(i-1,j)&&this.get(i+1,j))){ code[3]=1; } if(code.indexOf(1)>-1){ context.strokeStyle=value==2?"#FFF":"#09C"; var pos = this.coord2position(i,j); switch(code.join('')){ case '1100': context.beginPath(); context.arc(pos.x+this.size/2,pos.y+this.size/2,this.size/2,Math.PI,1.5*Math.PI,false); context.stroke(); context.closePath(); break; case '0110': context.beginPath(); context.arc(pos.x-this.size/2,pos.y+this.size/2,this.size/2,1.5*Math.PI,2*Math.PI,false); context.stroke(); context.closePath(); break; case '0011': context.beginPath(); context.arc(pos.x-this.size/2,pos.y-this.size/2,this.size/2,0,.5*Math.PI,false); context.stroke(); context.closePath(); break; case '1001': context.beginPath(); context.arc(pos.x+this.size/2,pos.y-this.size/2,this.size/2,.5*Math.PI,1*Math.PI,false); context.stroke(); context.closePath(); break; default: var dist = this.size/2; code.forEach(function(v,index){ if(v){ context.beginPath(); context.moveTo(pos.x,pos.y); context.lineTo(pos.x-_COS[index]*dist,pos.y-_SIN[index]*dist); context.stroke(); context.closePath(); } }); } } } } } } }); //物品地圖 beans = stage.createMap({ x:60, y:10, data:_DATA, frames:8, draw:function(context){ for(var j=0; j<this.y_length; j++){ for(var i=0; i<this.x_length; i++){ if(!this.get(i,j)){ var pos = this.coord2position(i,j); context.fillStyle = "#F5F5DC"; if(_GOODS[i+','+j]){ context.beginPath(); context.arc(pos.x,pos.y,3+this.times%2,0,2*Math.PI,true); context.fill(); context.closePath(); }else{ context.fillRect(pos.x-2,pos.y-2,4,4); } } } } } }); //得分 stage.createItem({ x:690, y:100, draw:function(context){ context.font = 'bold 28px Helvetica'; context.textAlign = 'left'; context.textBaseline = 'bottom'; context.fillStyle = '#C33'; context.fillText('SCORE',this.x,this.y); context.font = '28px Helvetica'; context.textAlign = 'left'; context.textBaseline = 'top'; context.fillStyle = '#FFF'; context.fillText(_SCORE,this.x+12,this.y); } }); //狀態文字 stage.createItem({ x:690, y:320, frames:25, draw:function(context){ if(stage.status==2&&this.times%2){ context.font = '24px Helvetica'; context.textAlign = 'left'; context.textBaseline = 'center'; context.fillStyle = '#09F'; context.fillText('PAUSE',this.x,this.y); } } }); //生命值 stage.createItem({ x:705, y:540, width:30, height:30, draw:function(context){ for(var i=0;i<_LIFE-1;i++){ var x=this.x+40*i,y=this.y; context.fillStyle = '#FFE600'; context.beginPath(); context.arc(x,y,this.width/2,.15*Math.PI,-.15*Math.PI,false); context.lineTo(x,y); context.closePath(); context.fill(); } } }); //NPC for(var i=0;i<4;i++){ stage.createItem({ width:30, height:30, orientation:3, color:_COLOR[i], location:map, coord:{x:12+i,y:14}, vector:{x:12+i,y:14}, type:2, frames:10, speed:1, timeout:Math.floor(Math.random()*120), update:function(){ var new_map; if(this.status==3&&!this.timeout){ this.status = 1; } if(!this.coord.offset){ //到達座標中心時計算 if(this.status==1){ if(!this.timeout){ //定時器 new_map = JSON.parse(JSON.stringify(map.data).replace(/2/g,0)); var id = this._id; items.forEach(function(item){ if(item._id!=id&&item.status==1){ //NPC將其它所有還處於正常狀態的NPC當成一堵牆 new_map[item.coord.y][item.coord.x]=1; } }); this.path = map.finder({ map:new_map, start:this.coord, end:player.coord }); if(this.path.length){ this.vector = this.path[0]; } } }else if(this.status==3){ new_map = JSON.parse(JSON.stringify(map.data).replace(/2/g,0)); var id = this._id; items.forEach(function(item){ if(item._id!=id){ new_map[item.coord.y][item.coord.x]=1; } }); this.path = map.finder({ map:new_map, start:player.coord, end:this.coord, type:'next' }); if(this.path.length){ this.vector = this.path[Math.floor(Math.random()*this.path.length)]; } }else if(this.status==4){ new_map = JSON.parse(JSON.stringify(map.data).replace(/2/g,0)); this.path = map.finder({ map:new_map, start:this.coord, end:this._params.coord }); if(this.path.length){ this.vector = this.path[0]; }else{ this.status = 1; } } //是否轉變方向 if(this.vector.change){ this.coord.x = this.vector.x; this.coord.y = this.vector.y; var pos = map.coord2position(this.coord.x,this.coord.y); this.x = pos.x; this.y = pos.y; } //方向判定 if(this.vector.x>this.coord.x){ this.orientation = 0; }else if(this.vector.x<this.coord.x){ this.orientation = 2; }else if(this.vector.y>this.coord.y){ this.orientation = 1; }else if(this.vector.y<this.coord.y){ this.orientation = 3; } } this.x += this.speed*_COS[this.orientation]; this.y += this.speed*_SIN[this.orientation]; }, draw:function(context){ var isSick = false; if(this.status==3){ isSick = this.timeout>80||this.times%2?true:false; } if(this.status!=4){ context.fillStyle = isSick?'#BABABA':this.color; context.beginPath(); context.arc(this.x,this.y,this.width*.5,0,Math.PI,true); switch(this.times%2){ case 0: context.lineTo(this.x-this.width*.5,this.y+this.height*.4); context.quadraticCurveTo(this.x-this.width*.4,this.y+this.height*.5,this.x-this.width*.2,this.y+this.height*.3); context.quadraticCurveTo(this.x,this.y+this.height*.5,this.x+this.width*.2,this.y+this.height*.3); context.quadraticCurveTo(this.x+this.width*.4,this.y+this.height*.5,this.x+this.width*.5,this.y+this.height*.4); break; case 1: context.lineTo(this.x-this.width*.5,this.y+this.height*.3); context.quadraticCurveTo(this.x-this.width*.25,this.y+this.height*.5,this.x,this.y+this.height*.3); context.quadraticCurveTo(this.x+this.width*.25,this.y+this.height*.5,this.x+this.width*.5,this.y+this.height*.3); break; } context.fill(); context.closePath(); } context.fillStyle = '#FFF'; if(isSick){ context.beginPath(); context.arc(this.x-this.width*.15,this.y-this.height*.21,this.width*.08,0,2*Math.PI,false); context.arc(this.x+this.width*.15,this.y-this.height*.21,this.width*.08,0,2*Math.PI,false); context.fill(); context.closePath(); }else{ context.beginPath(); context.arc(this.x-this.width*.15,this.y-this.height*.21,this.width*.12,0,2*Math.PI,false); context.arc(this.x+this.width*.15,this.y-this.height*.21,this.width*.12,0,2*Math.PI,false); context.fill(); context.closePath(); context.fillStyle = '#000'; context.beginPath(); context.arc(this.x-this.width*(.15-.04*_COS[this.orientation]),this.y-this.height*(.21-.04*_SIN[this.orientation]),this.width*.07,0,2*Math.PI,false); context.arc(this.x+this.width*(.15+.04*_COS[this.orientation]),this.y-this.height*(.21-.04*_SIN[this.orientation]),this.width*.07,0,2*Math.PI,false); context.fill(); context.closePath(); } } }); } items = stage.getItemsByType(2); //主角 player = stage.createItem({ width:30, height:30, type:1, location:map, coord:{x:13.5,y:23}, orientation:2, speed:2, frames:10, update:function(){ var coord = this.coord; if(!coord.offset){ if(this.control.orientation!='undefined'){ if(!map.get(coord.x+_COS[this.control.orientation],coord.y+_SIN[this.control.orientation])){ this.orientation = this.control.orientation; } } this.control = {}; var value = map.get(coord.x+_COS[this.orientation],coord.y+_SIN[this.orientation]); if(value==0){ this.x += this.speed*_COS[this.orientation]; this.y += this.speed*_SIN[this.orientation]; }else if(value<0){ this.x -= map.size*(map.x_length-1)*_COS[this.orientation]; this.y -= map.size*(map.y_length-1)*_SIN[this.orientation]; } }else{ if(!beans.get(this.coord.x,this.coord.y)){ //吃豆 _SCORE++; beans.set(this.coord.x,this.coord.y,1); if(_GOODS[this.coord.x+','+this.coord.y]){ //吃到能量豆 items.forEach(function(item){ if(item.status==1||item.status==3){ //如果NPC為正常狀態,則置為臨時狀態 item.timeout = 450; item.status = 3; } }); } } this.x += this.speed*_COS[this.orientation]; this.y += this.speed*_SIN[this.orientation]; } }, draw:function(context){ context.fillStyle = '#FFE600'; context.beginPath(); if(stage.status!=3){ //玩家正常狀態 if(this.times%2){ context.arc(this.x,this.y,this.width/2,(.5*this.orientation+.20)*Math.PI,(.5*this.orientation-.20)*Math.PI,false); }else{ context.arc(this.x,this.y,this.width/2,(.5*this.orientation+.01)*Math.PI,(.5*this.orientation-.01)*Math.PI,false); } }else{ //玩家被吃 if(stage.timeout) { context.arc(this.x,this.y,this.width/2,(.5*this.orientation+1-.02*stage.timeout)*Math.PI,(.5*this.orientation-1+.02*stage.timeout)*Math.PI,false); } } context.lineTo(this.x,this.y); context.closePath(); context.fill(); } }); //事件繫結 stage.bind('keydown',function(e){ switch(e.keyCode){ case 13: //回車 case 32: //空格 this.status = this.status==2?1:2; break; case 39: //右 player.control = {orientation:0}; break; case 40: //下 player.control = {orientation:1}; break; case 37: //左 player.control = {orientation:2}; break; case 38: //上 player.control = {orientation:3}; break; } }); })(); //結束畫面 (function(){ var stage = game.createStage(); //遊戲結束 stage.createItem({ x:game.width/2, y:game.height*.35, draw:function(context){ context.fillStyle = '#FFF'; context.font = 'bold 48px Helvetica'; context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillText('GAME OVER',this.x,this.y); } }); //記分 stage.createItem({ x:game.width/2, y:game.height*.5, draw:function(context){ context.fillStyle = '#FFF'; context.font = '20px Helvetica'; context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillText('FINAL SCORE: '+(_SCORE+50*Math.max(_LIFE-1,0)),this.x,this.y); } }); //事件繫結 stage.bind('keydown',function(e){ switch(e.keyCode){ case 13: //回車 case 32: //空格 _SCORE = 0; _LIFE = 3; var st = game.setStage(1); st.reset(); break; } }); })(); game.init(); })();
操作說明 ,按鍵盤上下左右鍵移動 ,按鍵盤空格鍵暫停!
到此這篇關於JS+HTML實現經典遊戲吃豆人的文章就介紹到這了,更多相關JS吃豆人遊戲內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45