版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第JS+HTML实现经典游戏吃豆人目录前言HTML代码JS代码游戏截图
前言
很早就知道canvas,当时一直没应用到,最近闲来无事就顺便写写看。吃豆游戏可以说是我们80,90后共同的回忆录,小时候常常在学习机上玩,所以也就有了强烈的欲望去写。为了写这个游戏,看了很多吃豆人游戏的相关信息,包括游戏规则,历史,其它相关游戏设计。又去了各个小游戏平台玩了下,找些灵感!!!就连最简单的配色,也是反复斟酌几个小时确定的方案哈哈,不容易啊代码分两部分,游戏引擎和游戏主程序,引擎用来类的定义,管理动画场景,事件定义等等游戏主程序主要组织业务逻辑。算面向对象编程吧,纯手工打造是男人就吃光所有豆子!!!
项目结构
因需要四个文件即可实现,index.html游戏主页,index.js主页控制,game.js游戏控制,favicon.png图标。代码简单,易懂。后续小伙伴们还可以自己加入音乐!可以部署到服务器上,也可用浏览器直接打开index.html玩耍!!!
HTML代码
html
head
metacharset="utf8"
titlePac-Man.吃豆游戏/title
linkrel="shortcuticon"href="favicon.png"rel="externalnofollow"
style
body{background-color:#292929}
*{padding:0;margin:0;}
.wrapper{
width:960px;
margin:0auto;
line-height:36px;
text-align:center;
color:#999;
canvas{display:block;background:#000;}
.mod-botton{
height:32px;
padding:15px0;
text-align:center;
/style
/head
body
div
canvasid="canvas"width="960"height="640"不支持画布/canvas
p按[空格]暂停或继续/p
/div
scriptsrc="game.js"/script
scriptsrc="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;//得分
vargame=newGame('canvas');
//启动页
(function(){
varstage=game.createStage();
//logo
stage.createItem({
x:game.width/2,
y:game.height*.45,
width:100,
height:100,
frames:3,
draw:function(context){
vart=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='bold42pxHelvetica';
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='14pxHelvetica';
context.textAlign='right';
context.textBaseline='bottom';
context.fillStyle='#AAA';
context.fillText('©',this.x,this.y);
//事件绑定
stage.bind('keydown',function(e){
switch(e.keyCode){
case13:
case32:
game.nextStage();
break;
})();
//游戏主程序
(function(){
varstage,map,beans,player,times;
stage=game.createStage({
update:function(){
varstage=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)){
vardx=item.x-player.x;
vardy=item.y-player.y;
if(dx*dx+dy*dy750item.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();
}elseif(stage.status==3){//场景临时状态
if(!stage.timeout){
_LIFE--;
if(_LIFE){
stage.resetItems();
}else{
game.nextStage();
returnfalse;
//绘制地图
map=stage.createMap({
x:60,
y:10,
data:_DATA,
cache:true,
draw:function(context){
context.lineWidth=2;
for(varj=0;jthis.y_length;j++){
for(vari=0;ithis.x_length;i++){
varvalue=this.get(i,j);
if(value){
varcode=[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.stroke"#FFF":"#09C";
varpos=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:
vardist=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(varj=0;jthis.y_length;j++){
for(vari=0;ithis.x_length;i++){
if(!this.get(i,j)){
varpos=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='bold28pxHelvetica';
context.textAlign='left';
context.textBaseline='bottom';
context.fillStyle='#C33';
context.fillText('SCORE',this.x,this.y);
context.font='28pxHelvetica';
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==2this.times%2){
context.font='24pxHelvetica';
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(vari=0;i_LIFE-1;i++){
varx=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(vari=0;ii++){
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(){
varnew_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));
varid=this._id;
items.forEach(function(item){
if(item._id!=iditem.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];
}elseif(this.status==3){
new_map=JSON.parse(JSON.stringify(map.data).replace(/2/g,0));
varid=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)];
}elseif(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;
varpos=map.coord2position(this.coord.x,this.coord.y);
this.x=pos.x;
this.y=pos.y;
//方向判定
if(this.vector.xthis.coord.x){
this.orientation=0;
}elseif(this.vector.xthis.coord.x){
this.orientation=2;
}elseif(this.vector.ythis.coord.y){
this.orientation=1;
}elseif(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){
varisSick=false;
if(this.status==3){
isSick=this.timeout80||this.times%2true: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){
case0:
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;
case1:
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(){
varcoord=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={};
varvalue=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];
}elseif(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,fals
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024-2025学年全国统考教师资格考试《教育教学知识与能力(小学)》练习题审定版附答案详解
- 2024-2025学年度宝鸡职业技术学院单招《语文》综合提升测试卷附参考答案详解【综合题】
- 2024-2025学年农村信用社招聘考试题库试题(精练)附答案详解
- 2024-2025学年度执业兽医测试卷及参考答案详解(综合卷)
- 2024-2025学年度施工员模拟题库含答案详解AB卷
- 2024-2025学年度监理工程师全真模拟模拟题及完整答案详解【名校卷】
- 2024-2025学年度天津城市建设管理职业技术学院单招数学练习题及参考答案详解【研优卷】
- 2024-2025学年度计算机四级考前冲刺练习题及参考答案详解(研优卷)
- 2024-2025学年度广东环境保护工程职业学院妇产护理期末模拟试题带答案详解(突破训练)
- 企业资产完备无损承诺书(6篇)
- 汽轮机组试车方案
- 漆安慎力学第二版课后习题解答及漆安慎-力学答案
- PCI围术期强化他汀治疗的获益和机制课件
- 沥青搅拌站安全生产风险分级管控体系方案资料(2022-2023版)
- WTO海关估价协议中文版
- 【广东省】工作证明模板(仅供参考)
- YS/T 613-2006碳膜电位器用电阻浆料
- GB/T 33365-2016钢筋混凝土用钢筋焊接网试验方法
- GB/T 17626.10-2017电磁兼容试验和测量技术阻尼振荡磁场抗扰度试验
- GB/T 14536.6-2008家用和类似用途电自动控制器燃烧器电自动控制系统的特殊要求
- 《乡风文明建设》(王博文)
评论
0/150
提交评论