h5小遊戲——HitRocket

一.遊戲介紹

遊戲介紹:
不斷有攜帶字母炸彈的火箭撞向地面,請根據火箭身上的字母敲擊鍵盤,每一次對應的敲擊會擊落攜帶該字母的火箭並使得分加一,每一架火箭撞到地面會使生命值減一,每擊落十架火箭,火箭數會加一,並獎勵一點額外生命值,生命值上限爲八。javascript

二.HTML內容

index.html裏包含canvas畫布和一個 介紹遊戲規則的div,html

當鼠標點擊頁面任何一個地方的時候,進入遊戲界面:java

 

indexnode

瀏覽器不支持canvas web

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!
canvas

 

二.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爲要重畫的節點,即dom

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);
};

在線演示

相關文章
相關標籤/搜索