怎么用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
