怎么用JS和HTML来还原童年经典游戏吃豆人
导读:在这篇文章中,我们将学习“怎么用JS和HTML来还原童年经典游戏吃豆人”的相关知识,下文有详细的介绍及示例,小编觉得挺不错的,有需要的朋友可以借鉴参考,希望对大家阅读完这篇能有所获。 前言 很早就知道canvas,...
在这篇文章中,我们将学习“怎么用JS和HTML来还原童年经典游戏吃豆人”的相关知识,下文有详细的介绍及示例,小编觉得挺不错的,有需要的朋友可以借鉴参考,希望对大家阅读完这篇能有所获。
前言
很早就知道canvas,当时一直没应用到,最近闲来无事就顺便写写看。吃豆游戏可以说是我们80,90后共同的回忆录,小时候常常在学习机上玩,所以也就有了强烈的欲望去写。为了写这个游戏,看了很多吃豆人游戏的相关信息,包括游戏规则,历史,其它相关游戏设计。又去了各个小游戏平台玩了下,找些灵感!!!就连最简单的配色,也是反复斟酌几个小时确定的方案……哈哈,不容易啊代码分两部分,游戏引擎和游戏主程序,引擎用来类的定义,管理动画场景,事件定义等等……游戏主程序主要组织业务逻辑。算面向对象编程吧,纯手工打造是男人就吃光所有豆子!!!
项目结构
因需要四个文件即可实现,index.html 游戏主页,index.js 主页控制,game.js 游戏控制,favicon.png 图标。代码简单,易懂。后续小伙伴们还可以自己加入音乐!可以部署到服务器上,也可用浏览器直接打开index.html玩耍!!!
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>
js代码
//主程序,业务逻辑 (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*dy750& & 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; jthis.y_length; j++){ for(var i=0; ithis.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; jthis.y_length; j++){ for(var i=0; ithis.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; i4; 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.xthis.coord.x){ this.orientation = 2; } else if(this.vector.y> this.coord.y){ this.orientation = 1; } else if(this.vector.ythis.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(value0){ 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和HTML来还原童年经典游戏吃豆人已经有了进一步的了解。大家如果还想学习更多知识,欢迎关注网络,小编将为大家输出更多高质量的实用文章!
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: 怎么用JS和HTML来还原童年经典游戏吃豆人
本文地址: https://pptw.com/jishu/654310.html