文档章节

h5小游戏——HitRocket

lonelydawn
 lonelydawn
发布于 2016/05/31 19:53
字数 2016
阅读 964
收藏 24

一.游戏介绍

游戏介绍:
不断有携带字母炸弹的火箭撞向地面,请根据火箭身上的字母敲击键盘,每一次对应的敲击会击落携带该字母的火箭并使得分加一,每一架火箭撞到地面会使生命值减一,每击落十架火箭,火箭数会加一,并奖励一点额外生命值,生命值上限为八。

二.HTML内容

index.html里包含canvas画布和一个 介绍游戏规则的div,

当鼠标点击页面任何一个地方的时候,进入游戏界面:


index

浏览器不支持canvas

1.Click space to pause or begin game.
2.Enter the letter on the rockets to hit them.
3.Number of the hearts will add one together with the rockets after ten rockets hit.
4.Failed when the number of hearts equals zero.
5.Click anywhere to start game!

 

二.JS内容

1.动画实现

common.js中并未使用setTimeOut()或者setInterval()作为动画定时器,而是使用window.requestAnimationFrame,一种更为优化的方式实现动画效果。

requestAnimFrame的用法,先设置浏览器的兼容,并设置动画的帧率,这里设置为每1000/60ms执行一次回调函数

window.requestAnimFrame = (function() {
        return  window.requestAnimationFrame        ||
        window.webkitRequestAnimationFrame          ||
        window.mozRequestAnimationFrame             ||
        window.oRequestAnimationFrame               ||
        window.msRequestAnimationFrame              ||
        function(callback) {
            window.setTimeout(callback, 1000 / 60);
        };
    })();

 requestAnimFrame中的两个参数,callback为下次重画执行的函数,element为要重画的节点,即

requestAnimFrame(callback, element);

 

2.代码块及对象分析

(1) 在Game代码块控制游戏的开始、结束和重新开始以及动画的运行和鼠标、键盘点击事件的判定

Game.begin(),游戏开始界面,点击任意位置,调用Game.run()函数进入游戏运行界面

Game.run(),游戏运行界面,这里使用自定义兼容函数requestAnimFrame不断回调Game.transform()函数对画布重绘,并调用键盘点击监听函数Game.hit(event),判断按键是否击中火箭

Game.over(),游戏结束界面,显示游戏得分、游戏中奖励的生命值和最终火箭数,点击任意位置会调用Game.restart()函数初始化游戏参数并调用Game.begin()进入游戏开始界面

 

(2) Background代码块用于绘制背景,默认样式为垂直方向灰度加深的渐变色

 

(3) Hearts代码块,根据全局变量heartsNum设置生命值的数量,初始为5,通过迭代和设置水平偏移量offSetX创建hearts数组中对象的位置属性,而Heart对象则要保存位置和半径属性。另外,Heart对象中设置draw()函数进行自绘,而Hearts代码块中设置draw()函数控制hearts数组中所有对象的自绘 ,以此实现动画运行时生命值的状态管理 。

 

(4) Rockets代码块负责绘制小火箭们,火箭数量由全局变量rocketsNum控制,初始为5,火箭的具体绘制由Rocket对象完成,在这里Rocket对象的状态是随机产生的,Rockets代码块中的rockets数组负责保存所有Rocket对象的状态。Rockets代码块控制所有Rocket对象的自绘,并在键盘敲击时,循环遍历数组中的对象以判断火箭是否被击中。

 

(5) TextNodes代码块负责绘制文本,TextNodes.setup()函数负责设置文本的内容、位置、字体、样式和字符的偏移量,当然也可以不进行设置,这时会采用默认值。TextNode对象保存单个字符的内容和位置信息并负责文本的自绘,TextNodes代码块会负责所有nodes数组中的对象的自绘。

 

(6) start函数负责从介绍游戏规则的界面转到游戏开始界面。

 

下面就不多说了,贴代码common.js:

window.onload=function(){
	var canvas=document.getElementById("canvas");
	var ctx=canvas.getContext("2d");
	canvas_width=canvas.width;
	canvas_height=canvas.height;

	var heartsNum=5;//生命值数
	var extraHearts=0;//额外增加的生命值数
	var rocketsNum=5;//火箭数
	var score=0;//得分数

	window.requestAnimFrame = (function() {
        return  window.requestAnimationFrame        ||
        window.webkitRequestAnimationFrame          ||
        window.mozRequestAnimationFrame             ||
        window.oRequestAnimationFrame               ||
        window.msRequestAnimationFrame              ||
        function(callback) {
            window.setTimeout(callback, 1000 / 60);
        };
	})();

	var Game={
		animationID:0,//animationID,flag控制暂停和开始
		flag:true,

		begin:function(){
			ctx.clearRect(0,0,canvas_width,canvas_height);

			Background.draw("yellow");

			TextNodes.setup("HitRocket",100,240,"96px 楷体,arial","#000");
			TextNodes.draw();
			TextNodes.setup("Click anywhere to start",150,360,"42px 楷体,arial","#000");
			TextNodes.draw();

			Rockets.init();
			Hearts.init();

			window.addEventListener("click",Game.run);
		},

		//缓冲,移除监听器
		run:function(){
			window.removeEventListener("click",Game.run);
			Game.transform();

			window.addEventListener("keyup",Game.hit);
		},

		transform:function(){
			if(heartsNum>0){
				Game.animationID=requestAnimationFrame(Game.transform);

				//清空画布
				ctx.clearRect(0,0,canvas_width,canvas_height);

				//背景
				Background.draw();

				//计分
				TextNodes.setup("Score:"+score,640,50,"42px 楷体,arial","#f00",20);
				TextNodes.draw();

				//火箭
				Rockets.transform();
				Rockets.modify();
				Rockets.draw();

				//生命值
				Hearts.modify();
				Hearts.draw();

			}else Game.over();
		},

		hit:function(event){
			event = (event)?event:window.event;

			if(event.keyCode==32)
				//暂停开始
				if(Game.flag){
					Game.flag=false;
					window.cancelAnimationFrame(Game.animationID);
				}else{
					Game.flag=true;
					requestAnimationFrame(Game.transform);
				}
			else if(event.keyCode>=65&&event.keyCode<=90)
				//打击火箭
				Rockets.loopHit(String.fromCharCode(event.keyCode));
		},

		over:function(){
			window.removeEventListener("keyup",Game.hit);

			Background.draw("#000");

			TextNodes.setup("Game Over!",100,120,"96px 楷体,arial","#f00");
			TextNodes.draw();
			TextNodes.setup("Click anywhere to retry!",120,200,"48px 楷体,arial","#f00");
			TextNodes.draw();
			TextNodes.setup("Score:"+score,240,320,"90px 楷体,arial","#f00");
			TextNodes.draw();
			TextNodes.setup("Extra hearts:"+extraHearts,120,420,"42px 楷体,arial","#f00",25);
			TextNodes.draw();
			TextNodes.setup("Total rockets:"+rocketsNum,120,500,"42px 楷体,arial","#f00",25);
			TextNodes.draw();

			window.addEventListener("click",Game.restart);
		},

		restart:function(){
			window.removeEventListener("click",Game.restart);

			//init
			heartsNum=5;
			extraHearts=0;
			rocketsNum=5;
			score=0;

			Game.begin();
		}
	}

	//绘制背景
	//有传入绘制样式,则使用该样式,否则使用默认样式,垂直加深灰色渐变
	var Background={
		draw:function(fillStyle){
			if(fillStyle)
				ctx.fillStyle=fillStyle;
			else{
				var grd=ctx.createLinearGradient(0,0,0,canvas_height);
				grd.addColorStop(0,"#ccc");
				grd.addColorStop(1,"#777");
				ctx.fillStyle=grd;
			}
			ctx.fillRect(0,0,canvas_width,canvas_height);
		}
	}

	//绘制生命值
	var Heart=function(x,y){
		//不能初始化
		var x,y,radius;

		this.x=x;
		this.y=y;
		this.radius=20;

		this.draw=function(){
			ctx.beginPath();
			ctx.arc(this.x,this.y,this.radius,0,2*Math.PI);
			ctx.closePath();

			var grd=ctx.createRadialGradient(this.x,this.y,0,this.x,this.y,this.radius);
			grd.addColorStop(1,"#f00");
			grd.addColorStop(0,"#fff");
			ctx.fillStyle=grd;

			ctx.fill();
		}
	}

	//管理生命值的初始化和数量变化
	var Hearts={
		hearts:null,
		x:50,
		y:40,
		offSetX:50,
		init:function(){
			this.hearts=new Array();
			for(var i=0;i<heartsNum;i++)//按水平偏移排列生命值
				this.hearts[i]=new Heart(this.x+i*this.offSetX,this.y);
		},
		modify:function(){
			//修改生命值数量
			if(heartsNum<this.hearts.length)
				for(var i=heartsNum;i<this.hearts.length;i++)
					this.hearts[i]=null;
			else 
				for(var i=this.hearts.length;i<heartsNum;i++)
					this.hearts[i]=new Heart(this.x+i*this.offSetX,this.y);
		},
		draw:function(){
			for(var i=0;i<this.hearts.length;i++)
				this.hearts[i].draw();
		}
	}

	var Rocket=function(scale){
		var str="ABCDEFGHIJKLMNOPQRSTUVWSYZ";
		var x,y,scale,letter,speed,fontColor;

		this.init=function(scale){
			this.x=Math.random()*(canvas_width-20)+10;
			this.y=Math.random()*canvas_height/5*2;
			this.scale=scale;
			//随机生成火箭体上的字母
			this.letter=str[Math.floor(Math.random()*26)];
			this.speed=Math.random()*2+1;
			this.fontColor="#000";
		}

		this.init(scale);

		this.draw=function(){

			ctx.save();
			ctx.translate(this.x,this.y);
			ctx.scale(this.scale,this.scale);

			//火箭火焰
			ctx.beginPath();
			ctx.moveTo(0,-80);
			ctx.lineTo(-25,0);
			ctx.lineTo(-10,30);
			ctx.lineTo(10,30);
			ctx.lineTo(25,0);
			ctx.closePath();
			var fire=ctx.createLinearGradient(-25,0,25,0);
			fire.addColorStop(0.2,"#f00");
			fire.addColorStop(0.5,"#f80");
			fire.addColorStop(0.8,"#f00");
			ctx.fillStyle=fire;
			ctx.fill();

			//火箭身体
			ctx.beginPath();
			ctx.moveTo(-10,30);
			ctx.lineTo(-30,160);
			ctx.lineTo(0,200);
			ctx.lineTo(30,160);
			ctx.lineTo(10,30);
			ctx.closePath();
			var body=ctx.createLinearGradient(-30,0,30,0);
			body.addColorStop(0.01,"#fff");
			body.addColorStop(0.5,"#00f");
			body.addColorStop(0.99,"#fff");
			ctx.fillStyle=body;
			ctx.fill();

			//火箭窗户
			ctx.fillStyle="#ade";
			ctx.fillRect(-10,150,12,20);

			//火箭文字
			ctx.fillStyle=this.fontColor;
			ctx.font="42px bold 楷体,arial";

			ctx.fillText(this.letter,-12,120);

			ctx.restore();

		}

		this.hit=function(key){
			//判断是否击中火箭
			//若击中,分数+1,重置火箭
			if(this.letter==key){
				score++;
				
				//每击中10个火箭,生命值+1,上限为8,火箭数+1,无上限
				if(score%10==0&&heartsNum<8){
					heartsNum++;
					extraHearts++;
					rocketsNum++;
				}

				//击中时重置火箭属性
				this.init(this.scale);
				return true;
			}
			return false;
		}

		this.move=function(){
			//火箭没有被击落,重置火箭属性,生命值-1
			if(this.y>=canvas_height){
				this.init(this.scale);
				heartsNum--;
			}//即将超出屏幕范围的时候,进行预警,火箭体上的字母变为红色
			else if(this.y>canvas_height/7*5)
				this.fontColor="#f00";
			this.y+=this.speed;
		}
	}

	//管理火箭的初始化,数量变化,运动
	var Rockets={
		rockets:null,
		scale:0.4,//控制火箭的大小
		init:function(){
			this.rockets=new Array();
			for(var i=0;i<rocketsNum;i++)
				this.rockets[i]=new Rocket(this.scale);
		},
		draw:function(){
			for(var i=0;i<this.rockets.length;i++)
				this.rockets[i].draw();
		},
		transform:function(){
			for(var i=0;i<this.rockets.length;i++)
				this.rockets[i].move();
		},
		modify:function(){
			//修改火箭数量
			for(var i=this.rockets.length;i<rocketsNum;i++)
				this.rockets[i]=new Rocket(this.scale);
		},
		loopHit:function(key){
			for(var i=0;i<this.rockets.length;i++)
				if(this.rockets[i].hit(key))
					//如果击中火箭,退出循环
					break;
		}
	}

	//绘制文本的单个字符
	var TextNode=function(txt,x,y){
		var txt,x,y;

		this.txt=txt;
		this.x=x;
		this.y=y;

		this.setup=function(txt,x,y){
			this.txt=txt;
			this.x=x;
			this.y=y;
		}

		this.draw=function(){
			ctx.fillText(this.txt,this.x,this.y);
		}
	}

	//管理字符串属性的变化和绘制
	var TextNodes={
		nodes:null,
		x:100,
		y:200,
		offSetX:60,
		font:"96px 楷体,arial",
		fillStyle:"#000",
		setup:function(txt,x,y,font,fillStyle,offSetX){
			this.x=x;
			this.y=y;
			this.font=font;
			this.fillStyle=fillStyle;
			//可选参数,字符的偏移
			this.offSetX=offSetX?offSetX:(canvas_width-2*x)/txt.length;

			this.nodes=new Array();
			for(var i=0;i<txt.length;i++)
				this.nodes[i]=new TextNode(txt[i],this.x+i*this.offSetX,this.y);
		},
		draw:function(){
			ctx.font=this.font;
			ctx.fillStyle=this.fillStyle;

			for(var i=0;i<this.nodes.length;i++)
				this.nodes[i].draw();
		}
	}

	var start=function(){
		document.getElementById("ins").style.display="none";
		Game.begin();
		window.removeEventListener("click",start);
	}

	//点击介绍,开始游戏
	window.addEventListener("click",start);
};

在线演示

© 著作权归作者所有

lonelydawn
粉丝 44
博文 50
码字总数 52905
作品 0
闵行
前端工程师
私信 提问
加载中

评论(3)

lonelydawn
lonelydawn 博主

引用来自“Mr_K”的评论

效果呢?
没做兼容,用polyfill做兼容失败了,不知道为什么,就没再尝试
lonelydawn
lonelydawn 博主

引用来自“Mr_K”的评论

效果呢?
FF、chrome或者任意支持canvas的浏览器可见:在线演示
Mr_K
Mr_K
效果呢?
青龙大厅大厅房卡低至0.2,哪里有?

  青龙大厅大厅房卡低至0.2,青龙大厅大厅房卡0元做代理,专属总代微信gghuhhjs, 玄武大厅房卡+歪信gghuhhjs   >>>>玩法介绍   一、   1、打开游戏选择你要玩的游戏点击“创建房间”...

H5棋牌游戏开发
2018/01/02
0
0
大番薯棋牌房低至0.2出卡,找谁可以拿到?

  大番薯棋牌房低至0.2,大番薯房卡零门槛代理,嘉微:gghuhhjs   专属总代微信:gghuhhjs,为你服务,棋牌是地方特色游戏,多种玩法可供选择。微信一键登录,无须下载无须安装,邀约好友...

H5棋牌游戏开发
2017/12/30
0
0
手游推广新玩法,为什么“吴亦凡H5”刷爆了朋友圈?

1.为什么吴亦凡H5火了 2.你应该从中学到什么 下面开始: 一、在哪火了? 在广告人的朋友圈火了呀!影响力已经达到行业级别。 相信大家都经历了1分钟内在N个微信群里看见这篇H5的事情… 但目标...

lanyu1
2015/09/08
125
0
重磅 | 微信「小游戏」来了 !首发深度解读小程序年度压轴大招

摘要 「小游戏」开放的是一种能力,更是微信的一种姿态。而对开发者来说,这是一份不折不扣的年终大礼。 在 2017 年还剩最后三天的时候,微信小程序上线了「小游戏」板块,千呼万唤后,微信小...

早优夫斯基
2017/12/28
0
0
想入H5游戏圈 你应该先了解这些基础知识

  不管承不承认,H5受到了关注。   它并不像一款月流水过亿的爆款手游,或者风靡一时的app让人印象深刻。但当你打开微信朋友圈,看好友刷各式各样的动态,从厂商的PR新闻,到创业鸡汤,再...

H5棋牌游戏开发
2017/12/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

java通过ServerSocket与Socket实现通信

首先说一下ServerSocket与Socket. 1.ServerSocket ServerSocket是用来监听客户端Socket连接的类,如果没有连接会一直处于等待状态. ServetSocket有三个构造方法: (1) ServerSocket(int port);...

Blueeeeeee
33分钟前
4
0
用 Sphinx 搭建博客时,如何自定义插件?

之前有不少同学看过我的个人博客(http://python-online.cn),也根据我写的教程完成了自己个人站点的搭建。 点此:使用 Python 30分钟 教你快速搭建一个博客 为防有的同学不清楚 Sphinx ,这...

王炳明
昨天
4
0
黑客之道-40本书籍助你快速入门黑客技术免费下载

场景 黑客是一个中文词语,皆源自英文hacker,随着灰鸽子的出现,灰鸽子成为了很多假借黑客名义控制他人电脑的黑客技术,于是出现了“骇客”与"黑客"分家。2012年电影频道节目中心出品的电影...

badaoliumang
昨天
13
0
很遗憾,没有一篇文章能讲清楚线程的生命周期!

(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本。 简介 大家都知道线程是有生命周期,但是彤哥可以认真负责地告诉你网上几乎没有一篇文章讲得是完全正确的。 ...

彤哥读源码
昨天
13
0
jquery--DOM操作基础

本文转载于:专业的前端网站➭jquery--DOM操作基础 元素的访问 元素属性操作 获取:attr(name);$("#my").attr("src"); 设置:attr(name,value);$("#myImg").attr("src","images/1.jpg"); ......

前端老手
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部