Html5小游戏之【是男人就忍30秒】
好久没写过小游戏了,最近抽时间写了一个,看了一些高人写的游戏,无限感慨呀。。。也从中学到了不少东西。
我不会写什么大的游戏,因为需要很多时间与精力,而且自认水平有限,个人喜欢写些小游戏。
这次的游戏是【是男人就忍30秒】,游戏玩法很简单,就是用键盘控制飞机,飞来飞去,躲开子弹,看你能撑多久。
本来是打算写一个教程来一步步说明的,但后来想了想,还是算了,免得误人子弟,在源码上加些注释就可以。
没有测试过IE9,因为没有IE9。。。鄙人还是用XP的。。。所以还是请用谷歌或者FF或者Safari吧。
刚刚修复了在FF下不能使用的问题,因为没给canvas设置tabindex属性,它就不能触发keydown事件,以及设置canvas为focus,现在可以了。。。
游戏预览如下:
此游戏用了四个js文件。
1。canvas.js,简单了封装了一些需要用到的画图方法,相信一看就懂了。。。
1 //画布类
2 var Canvas = {
3 //画布的2d对象
4 cxt : null,
5 //初始化画布
6 init : function(id){
7 this.cxt = (typeof id=='string'?document.getElementById(id):id).getContext('2d');
8 },
9 //清除画布
10 clear : function(x,y){
11 this.cxt.clearRect(0,0,x,y);
12 },
13 //画图
14 drawImg : function(obj){
15 this.cxt.drawImage(obj.img,obj.x,obj.y);
16 },
17 //画文字
18 drawText : function(string,x,y,color){
19
20 this.cxt.fillStyle = color;
21 this.cxt.font = 'bold 14px sans-serif';
22 this.cxt.fillText(string,x,y);
23 },
24 //加载图片
25 //imgs:图片对象的数组
26 //callback:回调函数
27 //context:上下文对象
28 loadImgs : function(imgs,callback,context){
29
30 var success_count = 0;
31
32 var addCount = function(){
33 success_count += 1;
34
35 if(success_count == l)callback && callback.call(context);
36 }
37
38 for(var i=0,l=imgs.length;i<l;i++){
39 imgs[i].onload = addCount;
40 }
41
42 }
43 }
2 var Canvas = {
3 //画布的2d对象
4 cxt : null,
5 //初始化画布
6 init : function(id){
7 this.cxt = (typeof id=='string'?document.getElementById(id):id).getContext('2d');
8 },
9 //清除画布
10 clear : function(x,y){
11 this.cxt.clearRect(0,0,x,y);
12 },
13 //画图
14 drawImg : function(obj){
15 this.cxt.drawImage(obj.img,obj.x,obj.y);
16 },
17 //画文字
18 drawText : function(string,x,y,color){
19
20 this.cxt.fillStyle = color;
21 this.cxt.font = 'bold 14px sans-serif';
22 this.cxt.fillText(string,x,y);
23 },
24 //加载图片
25 //imgs:图片对象的数组
26 //callback:回调函数
27 //context:上下文对象
28 loadImgs : function(imgs,callback,context){
29
30 var success_count = 0;
31
32 var addCount = function(){
33 success_count += 1;
34
35 if(success_count == l)callback && callback.call(context);
36 }
37
38 for(var i=0,l=imgs.length;i<l;i++){
39 imgs[i].onload = addCount;
40 }
41
42 }
43 }
2。fly.js,飞机类,采用在计时器中移动,而非一般的键盘按一下就移动一格,这样做,控制得更流畅,而且,可以支持打斜飞。
1 var Fly = function(img,x,y){
2 //飞机的图片
3 this.img = img;
4 //飞机的坐标
5 this.x = x;
6 this.y = y;
7 //飞机的移动方向值
8 this.moveDir = {
9 left:false,
10 right:false,
11 up:false,
12 down:false
13 };
14 //移动的速度
15 this.movesp = 2;
16 }
17 Fly.prototype = {
18 //键盘码对应移动方向
19 keyCode2Dir : {
20 37:'left',
21 38:'up',
22 39:'right',
23 40:'down'
24 },
25 //键盘按下事件
26 //keyCode:键盘码
27 keyDown : function(keyCode){
28 //修改对应的移动值
29 this.moveDir[this.keyCode2Dir[keyCode]]=true;
30 },
31 //键盘释放事件
32 //keyCode:键盘码
33 keyUp : function(keyCode){
34 //修改对应的移动值
35 this.moveDir[this.keyCode2Dir[keyCode]]=false;
36 },
37 //判断是否在移动中
38 checkMove : function(){
39 if(this.moveDir.left || this.moveDir.right || this.moveDir.up || this.moveDir.down)return true;
40 return false;
41 },
42 //移动
43 //gameInfo:游戏背景信息
44 move : function(gameInfo){
45
46 var This = this;
47 //根据方向来移动
48 if(This.moveDir.left)This.x -= This.movesp;
49 if(This.moveDir.right)This.x += This.movesp;
50 if(This.moveDir.up)This.y -= This.movesp;
51 if(This.moveDir.down)This.y += This.movesp;
52 //边界值检测
53 if(This.x <0)This.x=0;
54 else if(This.x >gameInfo.width-This.img.width)This.x = gameInfo.width-This.img.width;
55 if(This.y <0)This.y=0;
56 else if(This.y >gameInfo.height-This.img.height)This.y = gameInfo.height-This.img.height;
57 }
58
59 }
2 //飞机的图片
3 this.img = img;
4 //飞机的坐标
5 this.x = x;
6 this.y = y;
7 //飞机的移动方向值
8 this.moveDir = {
9 left:false,
10 right:false,
11 up:false,
12 down:false
13 };
14 //移动的速度
15 this.movesp = 2;
16 }
17 Fly.prototype = {
18 //键盘码对应移动方向
19 keyCode2Dir : {
20 37:'left',
21 38:'up',
22 39:'right',
23 40:'down'
24 },
25 //键盘按下事件
26 //keyCode:键盘码
27 keyDown : function(keyCode){
28 //修改对应的移动值
29 this.moveDir[this.keyCode2Dir[keyCode]]=true;
30 },
31 //键盘释放事件
32 //keyCode:键盘码
33 keyUp : function(keyCode){
34 //修改对应的移动值
35 this.moveDir[this.keyCode2Dir[keyCode]]=false;
36 },
37 //判断是否在移动中
38 checkMove : function(){
39 if(this.moveDir.left || this.moveDir.right || this.moveDir.up || this.moveDir.down)return true;
40 return false;
41 },
42 //移动
43 //gameInfo:游戏背景信息
44 move : function(gameInfo){
45
46 var This = this;
47 //根据方向来移动
48 if(This.moveDir.left)This.x -= This.movesp;
49 if(This.moveDir.right)This.x += This.movesp;
50 if(This.moveDir.up)This.y -= This.movesp;
51 if(This.moveDir.down)This.y += This.movesp;
52 //边界值检测
53 if(This.x <0)This.x=0;
54 else if(This.x >gameInfo.width-This.img.width)This.x = gameInfo.width-This.img.width;
55 if(This.y <0)This.y=0;
56 else if(This.y >gameInfo.height-This.img.height)This.y = gameInfo.height-This.img.height;
57 }
58
59 }
3。bullet.js,子弹类,子弹的飞,我承认,确实做得很简单,希望大家自己改进这个算法。。。
1 var Bullet = function(img){
2 //子弹的图片
3 this.img = img;
4 //X坐标
5 this.x = 0;
6 //Y坐标
7 this.y = 0;
8 //子弹要飞的x与y的速度
9 this.arc = {};
10 //移动的帧数
11 this.moveFps = 20;
12 //帧数延迟
13 this.moveFpsLazy = 0;
14 //初始化
15 this.init();
16 }
17 Bullet.prototype = {
18 //方向数组
19 arrDir : ['left','right','up','down'],
20 //初始化
21 init : function(){
22 //最小位置
23 var min = 5,
24 //最大位置
25 max = 395,
26 //随机位置
27 rnd = Math.floor(Math.random()*370+10),
28 //移动方向
29 dir = this.arrDir[Math.floor(Math.random()*4)];
30
31 //设置子弹的初始位置与将要飞的方向与速度
32 switch(dir){
33 case 'left':{
34 this.x = max;
35 this.y = rnd;
36 if(this.y>=max/2)this.arc = {x:-5,y:-2};
37 else this.arc={x:-5,y:2};
38 break;
39 }
40 case 'right':{
41 this.x = min;
42 this.y = rnd;
43 if(this.y>=max/2)this.arc = {x:5,y:-2};
44 else this.arc={x:5,y:2};
45 break;
46 }
47 case 'up':{
48 this.y = max;
49 this.x = rnd;
50 if(this.x>=max/2)this.arc = {x:-3,y:-5};
51 else this.arc={x:3,y:-5};
52 break;
53 }
54 case 'down':{
55 this.y = min;
56 this.x = rnd;
57 if(this.x>=max/2)this.arc = {x:-3,y:5};
58 else this.arc={x:3,y:5};
59 break;
60 }
61 }
62 },
63 //更新子弹数据
64 //gameInfo:游戏背景信息
65 updata : function(gameInfo){
66 //延迟+10
67 this.moveFpsLazy += 10;
68 //判断延迟是否等于移动帧数
69 if(this.moveFpsLazy == this.moveFps){
70 //移动
71 this.x += this.arc.x;
72 this.y += this.arc.y;
73 //边界值检测
74 if(this.x <0 || this.x > gameInfo.width || this.y <0 || this.y > gameInfo.height){
75 this.callback();
76 return false;
77 }
78 //清0
79 this.moveFpsLazy = 0;
80 }
81 },
82 //检测是否撞到飞机
83 //fly:飞机对象
84 checkCrashFly : function(fly){
85 //获取子弹与飞机的圆心坐标
86 var bx = this.x+this.img.width/2,by = this.y+this.img.height/2,
87 fx = fly.x+fly.img.width/2,fy = fly.y+fly.img.height/2;
88 //判断圆心距
89 if(Math.sqrt(Math.pow(bx-fx,2)+Math.pow(by-fy,2)) < (fly.img.width/2+this.img.width/2)){
90 return true;
91 }
92 return false;
93 },
94 //回调函数
95 callback : function(){}
96
97 }
2 //子弹的图片
3 this.img = img;
4 //X坐标
5 this.x = 0;
6 //Y坐标
7 this.y = 0;
8 //子弹要飞的x与y的速度
9 this.arc = {};
10 //移动的帧数
11 this.moveFps = 20;
12 //帧数延迟
13 this.moveFpsLazy = 0;
14 //初始化
15 this.init();
16 }
17 Bullet.prototype = {
18 //方向数组
19 arrDir : ['left','right','up','down'],
20 //初始化
21 init : function(){
22 //最小位置
23 var min = 5,
24 //最大位置
25 max = 395,
26 //随机位置
27 rnd = Math.floor(Math.random()*370+10),
28 //移动方向
29 dir = this.arrDir[Math.floor(Math.random()*4)];
30
31 //设置子弹的初始位置与将要飞的方向与速度
32 switch(dir){
33 case 'left':{
34 this.x = max;
35 this.y = rnd;
36 if(this.y>=max/2)this.arc = {x:-5,y:-2};
37 else this.arc={x:-5,y:2};
38 break;
39 }
40 case 'right':{
41 this.x = min;
42 this.y = rnd;
43 if(this.y>=max/2)this.arc = {x:5,y:-2};
44 else this.arc={x:5,y:2};
45 break;
46 }
47 case 'up':{
48 this.y = max;
49 this.x = rnd;
50 if(this.x>=max/2)this.arc = {x:-3,y:-5};
51 else this.arc={x:3,y:-5};
52 break;
53 }
54 case 'down':{
55 this.y = min;
56 this.x = rnd;
57 if(this.x>=max/2)this.arc = {x:-3,y:5};
58 else this.arc={x:3,y:5};
59 break;
60 }
61 }
62 },
63 //更新子弹数据
64 //gameInfo:游戏背景信息
65 updata : function(gameInfo){
66 //延迟+10
67 this.moveFpsLazy += 10;
68 //判断延迟是否等于移动帧数
69 if(this.moveFpsLazy == this.moveFps){
70 //移动
71 this.x += this.arc.x;
72 this.y += this.arc.y;
73 //边界值检测
74 if(this.x <0 || this.x > gameInfo.width || this.y <0 || this.y > gameInfo.height){
75 this.callback();
76 return false;
77 }
78 //清0
79 this.moveFpsLazy = 0;
80 }
81 },
82 //检测是否撞到飞机
83 //fly:飞机对象
84 checkCrashFly : function(fly){
85 //获取子弹与飞机的圆心坐标
86 var bx = this.x+this.img.width/2,by = this.y+this.img.height/2,
87 fx = fly.x+fly.img.width/2,fy = fly.y+fly.img.height/2;
88 //判断圆心距
89 if(Math.sqrt(Math.pow(bx-fx,2)+Math.pow(by-fy,2)) < (fly.img.width/2+this.img.width/2)){
90 return true;
91 }
92 return false;
93 },
94 //回调函数
95 callback : function(){}
96
97 }
4。Game.js,游戏控制类,主要控制游戏的逻辑
1 var Game = {
2 //游戏背景数据,主要是宽与高
3 gameInfo :{width:0,height:0},
4 //飞机对象
5 fly : null,
6 //子弹对象数组
7 bullets : [],
8 //子弹图片
9 bulletImg : null,
10 //子弹产生的延迟
11 bulletLazyFps : 0,
12 //绘画处理计时器ID
13 processId : 0,
14 //分数
15 score : 0,
16 //是否开始
17 isStart : false,
18 //显示开始信息
19 showStart : function(){
20
21 var This = this;
22 //画开始字
23 Canvas.drawText("Press Enter to Start!",120,200,'white');
24 //绑定事件
25 document.body.onkeydown = function(e){This.keyDown(e);};
26 document.body.onkeyup = function(e){This.keyUp(e);};
27 },
28 //游戏初始化
29 init : function(){
30 //设置游戏背景信息
31 var gameBg = document.getElementById('js_canvas');
32 this.gameInfo.width = gameBg.offsetWidth;
33 this.gameInfo.height = gameBg.offsetHeight;
34 //初始化画布
35 gameBg.focus();Canvas.init(gameBg);
36 //设置飞机图片与子弹图片
37 var flyImg = new Image();
38 var bulletImg = new Image();
39 flyImg.src = "img/fly.gif";
40 bulletImg.src = "img/bullet.gif";
41 //加载图片,成功后,回调显示开始信息
42 Canvas.loadImgs([flyImg,bulletImg],this.showStart,this);
43 //设置飞机对象
44 this.fly = new Fly(flyImg,200,200);
45 //设置Game子弹图片
46 this.bulletImg = bulletImg;
47 },
48 //生成子弹
49 createBullets : function(){
50
51 var This = this;
52 //判断延时是否200
53 if(this.bulletLazyFps == 200){
54 //创建子弹,添加到数组中
55 var bullet = new Bullet(this.bulletImg);
56 bullet.callback = function(){
57 This.removeBullet(this);
58 }
59
60 this.bullets.push(bullet);
61 this.bulletLazyFps = 0;
62 }
63 else{
64 this.bulletLazyFps += 10;
65 }
66 },
67 //开始
68 start : function(){
69
70 var This = this;
71 //重置数据
72 this.reset();
73 //设置开始
74 this.isStart = true;
75 //开始绘画
76 this.process();
77
78 },
79 //重置数据
80 reset : function(){
81
82 this.score = 0;
83 this.bullets = [];
84 },
85 //结束
86 end : function(){
87
88 this.isStart = false;
89
90 clearInterval(this.processId);
91
92 this.showStart();
93 },
94 //绘画函数
95 process : function(){
96
97 var This = this;
98 //绘画计时器
99 this.processId = setInterval(function(){
100
101 if(!This.isStart)return;
102 //清除画布
103 Canvas.clear(This.gameInfo.width,This.gameInfo.height);
104 //判断飞机是否移动
105 if(This.fly.checkMove())This.fly.move(This.gameInfo);
106 //画飞机
107 Canvas.drawImg(This.fly);
108 //生产子弹
109 This.createBullets();
110 //画出所有子弹
111 for(var i=0,l=This.bullets.length;i<l;i++){
112
113 var bullet = This.bullets[i];
114
115 if(!bullet)continue;
116 //更新子弹信息
117 bullet.updata(This.gameInfo);
118 //画子弹
119 Canvas.drawImg(bullet);
120 //检测是否撞到飞机
121 if(bullet.checkCrashFly(This.fly)){
122 This.end();
123 }
124 }
125 //画分数
126 Canvas.drawText(Math.floor(This.score/1000)+"秒",20,20,'white');
127 This.score += 10;
128 },10);
129 },
130 //键盘按下事件
131 keyDown : function(e){
132
133 var This =this;
134 //游戏还没开始,而且按了回车
135 if(e.keyCode == 13 && !this.isStart){
136 e.preventDefault();
137 //开始
138 this.start();
139 }
140 //游戏开始而且按了方向键
141 if(e.keyCode >= 37 && e.keyCode <= 40 && this.isStart){
142 e.preventDefault();
143
144 This.fly.keyDown(e.keyCode);
145 }
146 },
147 //键盘释放事件
148 keyUp : function(e){
149
150 var This = this;
151 //释放了方向键
152 if(e.keyCode >= 37 && e.keyCode <= 40){
153 e.preventDefault();
154
155 This.fly.keyUp(e.keyCode);
156 }
157 },
158 //移除子弹
159 removeBullet : function(item){
160
161 for(var i=0,l=this.bullets.length;i<l;i++){
162
163 if(this.bullets[i] == item){
164 this.bullets.splice(i,1);
165 return true;
166 }
167 }
168 }
169
170 }
2 //游戏背景数据,主要是宽与高
3 gameInfo :{width:0,height:0},
4 //飞机对象
5 fly : null,
6 //子弹对象数组
7 bullets : [],
8 //子弹图片
9 bulletImg : null,
10 //子弹产生的延迟
11 bulletLazyFps : 0,
12 //绘画处理计时器ID
13 processId : 0,
14 //分数
15 score : 0,
16 //是否开始
17 isStart : false,
18 //显示开始信息
19 showStart : function(){
20
21 var This = this;
22 //画开始字
23 Canvas.drawText("Press Enter to Start!",120,200,'white');
24 //绑定事件
25 document.body.onkeydown = function(e){This.keyDown(e);};
26 document.body.onkeyup = function(e){This.keyUp(e);};
27 },
28 //游戏初始化
29 init : function(){
30 //设置游戏背景信息
31 var gameBg = document.getElementById('js_canvas');
32 this.gameInfo.width = gameBg.offsetWidth;
33 this.gameInfo.height = gameBg.offsetHeight;
34 //初始化画布
35 gameBg.focus();Canvas.init(gameBg);
36 //设置飞机图片与子弹图片
37 var flyImg = new Image();
38 var bulletImg = new Image();
39 flyImg.src = "img/fly.gif";
40 bulletImg.src = "img/bullet.gif";
41 //加载图片,成功后,回调显示开始信息
42 Canvas.loadImgs([flyImg,bulletImg],this.showStart,this);
43 //设置飞机对象
44 this.fly = new Fly(flyImg,200,200);
45 //设置Game子弹图片
46 this.bulletImg = bulletImg;
47 },
48 //生成子弹
49 createBullets : function(){
50
51 var This = this;
52 //判断延时是否200
53 if(this.bulletLazyFps == 200){
54 //创建子弹,添加到数组中
55 var bullet = new Bullet(this.bulletImg);
56 bullet.callback = function(){
57 This.removeBullet(this);
58 }
59
60 this.bullets.push(bullet);
61 this.bulletLazyFps = 0;
62 }
63 else{
64 this.bulletLazyFps += 10;
65 }
66 },
67 //开始
68 start : function(){
69
70 var This = this;
71 //重置数据
72 this.reset();
73 //设置开始
74 this.isStart = true;
75 //开始绘画
76 this.process();
77
78 },
79 //重置数据
80 reset : function(){
81
82 this.score = 0;
83 this.bullets = [];
84 },
85 //结束
86 end : function(){
87
88 this.isStart = false;
89
90 clearInterval(this.processId);
91
92 this.showStart();
93 },
94 //绘画函数
95 process : function(){
96
97 var This = this;
98 //绘画计时器
99 this.processId = setInterval(function(){
100
101 if(!This.isStart)return;
102 //清除画布
103 Canvas.clear(This.gameInfo.width,This.gameInfo.height);
104 //判断飞机是否移动
105 if(This.fly.checkMove())This.fly.move(This.gameInfo);
106 //画飞机
107 Canvas.drawImg(This.fly);
108 //生产子弹
109 This.createBullets();
110 //画出所有子弹
111 for(var i=0,l=This.bullets.length;i<l;i++){
112
113 var bullet = This.bullets[i];
114
115 if(!bullet)continue;
116 //更新子弹信息
117 bullet.updata(This.gameInfo);
118 //画子弹
119 Canvas.drawImg(bullet);
120 //检测是否撞到飞机
121 if(bullet.checkCrashFly(This.fly)){
122 This.end();
123 }
124 }
125 //画分数
126 Canvas.drawText(Math.floor(This.score/1000)+"秒",20,20,'white');
127 This.score += 10;
128 },10);
129 },
130 //键盘按下事件
131 keyDown : function(e){
132
133 var This =this;
134 //游戏还没开始,而且按了回车
135 if(e.keyCode == 13 && !this.isStart){
136 e.preventDefault();
137 //开始
138 this.start();
139 }
140 //游戏开始而且按了方向键
141 if(e.keyCode >= 37 && e.keyCode <= 40 && this.isStart){
142 e.preventDefault();
143
144 This.fly.keyDown(e.keyCode);
145 }
146 },
147 //键盘释放事件
148 keyUp : function(e){
149
150 var This = this;
151 //释放了方向键
152 if(e.keyCode >= 37 && e.keyCode <= 40){
153 e.preventDefault();
154
155 This.fly.keyUp(e.keyCode);
156 }
157 },
158 //移除子弹
159 removeBullet : function(item){
160
161 for(var i=0,l=this.bullets.length;i<l;i++){
162
163 if(this.bullets[i] == item){
164 this.bullets.splice(i,1);
165 return true;
166 }
167 }
168 }
169
170 }
突然发觉自己写的那些,为啥会那么卡了。。。因为,我以前都是计时器分开来写,后来才发现,要放在同一个计时器去做动画。。。
希望大家喜欢,有啥不好的,欢迎指出。。。