前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >h5小游戏——HitRocket

h5小游戏——HitRocket

作者头像
lonelydawn
发布2018-02-09 11:07:02
1.1K0
发布2018-02-09 11:07:02
举报

一.游戏介绍

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

二.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执行一次回调函数

代码语言:javascript
复制
window.requestAnimFrame = (function() {
        return  window.requestAnimationFrame        ||
        window.webkitRequestAnimationFrame          ||
        window.mozRequestAnimationFrame             ||
        window.oRequestAnimationFrame               ||
        window.msRequestAnimationFrame              ||
        function(callback) {
            window.setTimeout(callback, 1000 / 60);
        };
    })();

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

代码语言:javascript
复制
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:

代码语言:javascript
复制
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);
};

在线演示

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.游戏介绍
  • 二.HTML内容
  • 二.JS内容
    • 1.动画实现
      • 2.代码块及对象分析
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档