用js寫飛機大戰的小遊戲
飛機大戰思路整理
飛機大戰分為5個階段:
1.遊戲開始階段
1.0定義整個遊戲的全域性變數,並進行初始化
1.1.0建立遊戲背景圖片
1.1.1初始化背景圖片的資料
1.1.2建立遊戲開始階段的建構函式
*繪製方法
*運動方法
1.1.3建立遊戲開始的物件
1.1.4繪製遊戲名
2.遊戲載入階段
2.0繪製遊戲載入
2.1.0建立遊戲載入時的圖片,並放在一個數組中
2.1.1初始化遊戲載入時的資料
2.1.2建立遊戲載入時的建構函式
*繪製方法
*運動方法
2.1.3建立遊戲載入時的物件
2.1.4增加滑鼠點選事件(點選第一階段會跳轉到第二階段)
3.遊戲執行階段
3.1 繪製我方飛機
3.1.0建立我方飛機的圖片,並把它們放進陣列中;
3.1.1初始化我方飛機的資料;
3.1.2建立我方飛機的建構函式;
*繪製方法
*運動方法
*碰撞方法(在3.3.8中呼叫)
*射擊方法(我方飛機射擊時,要呼叫子彈產生的物件例項)
3.1.3建立我方飛機的物件
3.1.4增加滑鼠移動事件(當滑鼠移動的時候我方飛機要跟著移動)
3.2 繪製子彈
3.2.0建立子彈的圖片
3.2.1初始化子彈的資料
3.2.2建立子彈的建構函式
*繪製方法
*運動方法
*碰撞方法(在3.3.8中呼叫)
3.2.3定義一個子彈的陣列。將新的子彈的物件例項放進陣列中(方便後面遍歷、增加、刪除)
3.2.4建立繪製子彈的函式:遍歷陣列呼叫子彈物件的建構函式中的繪製子彈的方法
3.2.5建立子彈移動函式:遍歷陣列呼叫子彈物件的建構函式中子彈運動的方法
3.2.6建立刪除子彈的函式:
刪除子彈滿足的條件:1.碰撞的時候刪除子彈
2.子彈的縱座標小於負的子彈的高度的時候刪除子彈
3.3 繪製敵方飛機
3.3.0建立敵方飛機的圖片(分別用3個數組存放:區別小飛機、中飛機、大飛機)
3.3.1分別初始化三個型別飛機的資料
3.3.2建立敵方飛機的建構函式
*繪製方法
*運動方法
*敵方飛機碰撞的方法(在3.3.8中呼叫)
*檢測敵方飛機是否碰撞的方法(有可能是我方飛機碰撞、也有可能事子彈碰撞)
3.3.3定義一個存放敵方飛機的陣列(方便後面函式遍歷、增加子彈、刪除、改變)
3.3.4建立一個函式,往陣列中新增資料(小飛機、中飛機、大飛機)————較難
它們的產生是隨機的,將new出來的飛機物件例項新增進入陣列中。
3.3.5建立函式,遍歷畫出敵方飛機(呼叫陣列中物件的方法)
3.3.6建立函式,遍歷出敵方飛機的運動(呼叫陣列物件中的方法)
3.3.7建立函式,刪除敵方飛機
刪除敵方飛機滿足的條件:
1.敵方飛機的高度大於整個畫布的高度時
2.敵方飛機爆炸完成時
3.3.8建立函式,敵方飛機碰撞以後的函式(迴圈遍歷敵方飛機的陣列)
判斷:1.碰撞為我方飛機的時候
呼叫陣列元素中的檢測是否碰撞的方法(傳入的實參就是我方飛機)
分別呼叫敵方飛機和我方飛機的撞擊以後的方法
2.碰撞為子彈的時候
呼叫陣列元素中的檢測是否碰撞的方法(傳入的引數就是子彈陣列的元素)
分別呼叫敵方飛機和子彈陣列元素中的撞擊以後的方法
3.4繪製分數值和生命值
4.遊戲暫停階段
用畫布的滑鼠移出事件暫停遊戲狀態
用畫布的滑鼠移入事件開始遊戲狀態
5.遊戲結束階段
繪製遊戲結束
寫一個定時器:呼叫所有的物件方法以及函式呼叫
通過判斷遊戲狀態呼叫所對應的物件方法和函式
注意:在後面的暫停階段和遊戲結束階段要把前面第三階段的我方飛機、子彈、敵方飛機都顯示出來
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
#canvas{
display: block;
margin: 100px auto;
/*border: solid 1px #000;*/
}
</style >
</head>
<body>
<canvas id="canvas" width="480" height="650"></canvas>
<script>
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
// 0.遊戲初始化
// 0.1將遊戲分為幾個階段
var START=0 ;// 第一個階段 遊戲開始階段
var STARTING=1;//第二個階段 遊戲遊戲開始的載入階段
var RUNNING=2;// 第三個階段 遊戲執行階段
var PAUSE=3;// 第四個階段 遊戲暫停階段
var GAMEOVER=4;//第五個階段 遊戲結束階段
// 0.2定義一個自己的狀態,去和上面的狀態作比較
var state=START;
// 0.3定義畫布的寬和高
var WIDTH=canvas.width;
var HEIGHT=canvas.height;
// 0.4定義遊戲的得分
var score=0;
// 0.5定義我方飛機的生命值為3
var life=3;
//初始化階段完成
//1.遊戲剛開始的階段
//1.1載入背景圖片
//1.1.1建立載入背景圖片的物件
var bg=new Image();
bg.src="img/background.png";
//1.1.2初始化背景圖片的資料
var BG={
imgs:bg,
width:480,
height:852
}
//1.1.3建立背景圖片的建構函式(構造出一個物件)
//這裡使用了建立物件的第二種方法:先使用函式來定義物件,然後建立新的物件例項
//建構函式的特點:
// ①建構函式的首字母必須要大寫,用來區分與普通函式
// ②內部使用的this物件,來指向直接要生成的例項物件
// ③使用new來生成例項物件
function Bg(config){//這裡的config是個形參,而BG就是後面要傳進來的實參;
this.imgs=config.imgs;
this.width=config.width;
this.height=config.height;
//在這裡定義了兩張背景圖,是為了第一張背景圖移動完後第二張背景圖接著移動
this.x1=0;
this.y1=0;
this.x2=0;
//第二張背景圖的初始高度放在背景高度(固定)的上面
this.y2=-this.height;
//定義背景圖片繪製的方法
this.paint=function(){
//分別繪製了兩張背景圖
context.drawImage(this.imgs,this.x1,this.y1);
context.drawImage(this.imgs,this.x2,this.y2);
}
//定義背景圖片運動的方法
this.step=function(){
//背景圖片位置向下移動一個,然後利用後面的定時器讓背景圖動起來
this.y1++;
this.y2++;
//判斷圖片高度的臨界點,只要圖片一和圖片二的高度等於設定的背景高度時,就讓他們的縱向位置變成背景高度的上面
if(this.y1 == this.height){
this.y1=-this.height;
}
if(this.y2 == this.height){
this.y2=-this.height;
}
}
}
// 1.1.4建立背景圖片的物件
var sky=new Bg(BG);
// 1.2繪製遊戲名
var logo=new Image();
logo.src="img/start.png";
// 2第二階段遊戲載入的過程
// 2.1遊戲載入時的四個圖片
var loadings = [];
loadings[0] = new Image();
loadings[0].src = "img/game_loading1.png";
loadings[1] = new Image();
loadings[1].src = "img/game_loading2.png";
loadings[2] = new Image();
loadings[2].src = "img/game_loading3.png";
loadings[3] = new Image();
loadings[3].src = "img/game_loading4.png";
// 2.2初始化圖片資料
var LOADING={
imges:loadings,
length:loadings.length,
width:186,
height:38
}
// 2.3寫建構函式
function Loading(config){
this.imges=config.imges;
this.length=config.length;
this.width=config.width;
this.height=config.height;
// 定義索引,判斷要顯示的圖片是哪個
this.startIndex=0;
// 定義繪製方法
this.paint=function(){
context.drawImage(this.imges[this.startIndex],0,HEIGHT-this.height)
}
//定義遊戲載入時的圖片切換速度初始值
this.time=0;
//定義遊戲載入時圖片動態效果(其實是圖片切換)的方法
this.step=function(){
this.time++;
//設定4步切換一張圖
if(this.time % 4 == 0){
this.startIndex++;
}
if(this.startIndex == this.length){
state = RUNNING;
}
}
}
//建立遊戲載入(第二階段)的物件
var loading=new Loading(LOADING);
//獲取滑鼠點選事件
canvas.onclick=function(){
// 滑鼠點選第一階段(遊戲開始)時,要切換到第二狀態(遊戲載入)
if(state == START){
state = STARTING;
}
}
// 3.1.0寫我方飛機
// 3.1.1載入我方飛機的圖片
var heros=[];
heros[0]=new Image();
heros[0].src="img/hero1.png";
heros[1]=new Image();
heros[1].src="img/hero2.png";
heros[2]=new Image();
heros[2].src="img/hero_blowup_n1.png";
heros[3]=new Image();
heros[3].src="img/hero_blowup_n2.png";
heros[4]=new Image();
heros[4].src="img/hero_blowup_n3.png";
heros[5]=new Image();
heros[5].src="img/hero_blowup_n4.png";
// 3.2初始化圖片資料
var HEROS={
imgs:heros,
length:heros.length,
width:99,
height:124,
frame:2
}
// 3.1.3構造我方飛機的函式
function Hero(config){
this.imgs=config.imgs;
this.length=config.length;
this.width=config.width;
this.height=config.height;
this.frame=config.frame;
// 定義索引值
this.startIndex=0;
// 定義我方飛機的位置
this.x=WIDTH / 2 - this.width / 2;
this.y=HEIGHT - this.height;
// 定義飛機撞擊的標誌,表示飛機沒有被撞擊
this.down=false;
// 定義飛機是否爆破完成,表示飛機還沒有完全爆炸
this.candel=false;
// 定義繪製的方法
this.paint=function(){
context.drawImage(this.imgs[this.startIndex],this.x,this.y)
}
// 定義我方飛機運動的方法
this.step=function(){
// 我方飛機的運動狀態:(所以在此處要判斷狀態)
// 1.正常狀態
// 2.爆破狀態
if(!this.down){//表示飛機正常狀態,此時只有兩張圖進行切換
if(this.startIndex == 0){
this.startIndex=1;
}
else{
this.startIndex=0;
}
}
else{//爆炸狀態
this.startIndex++;//爆炸狀態的圖片持續增加
if(this.startIndex == this.length){//判斷圖片是最後一張的時候
life--;//我方飛機爆炸完後,生命值減一
//判斷生命值為0時,遊戲結束
if(life == 0){
state=GAMEOVER;
//雖然遊戲結束了但是還停留在最後一張圖的狀態
this.startIndex=this.length-1;
}
// 想要開始新的生命時,直接給出建立的新的物件
else{
hero=new Hero(HEROS);
}
}
}
}
// 定義我方飛機碰撞的方法
this.bang=function(){
this.down=true;
}
// 增加我方飛機射擊的方法
//定義射擊速度初始值為0
this.time=0;
//我方飛機射擊的時候,就會出現子彈,所以要呼叫子彈的陣列中產生的子彈物件
this.shoot=function(){
this.time++;
//每3步射擊一次
if(this.time % 3 == 0){
//這裡把子彈每次建立好的新的例項增加到陣列的末尾
bullets.push(new Bullet(BULLETS))
}
}
}
//3.1.4建立我方飛機的物件例項
var hero=new Hero(HEROS);
//3.1.5獲取滑鼠移動事件
canvas.onmousemove=function(event){
var event=event || window.event;
if(state == RUNNING){//判斷當前遊戲狀態
//把獲取到的頁面中的滑鼠橫座標的值賦給飛機的橫座標(位置)
hero.x = event.offsetX-hero.width/2;
//把獲取到的頁面中的滑鼠縱座標的值賦給飛機的縱座標(位置)
hero.y = event.offsetY-hero.height/2;
}
}
// 3.2繪製子彈
// 3.2.0建立子彈的圖片
var bullet=new Image();
bullet.src="img/bullet1.png";
// 3.2.1初始化資料
var BULLETS={
imgs:bullet,//注意這裡的“ :”
width:9,
height:21
}
// 3.2.2建立子彈的建構函式
function Bullet(config){//用形參config去接收
this.imgs=config.imgs;
this.width=config.width;
this.height=config.height;
// 定義子彈的座標
//子彈的橫座標
this.x=hero.x + hero.width / 2-this.width / 2 ;
//子彈縱座標
this.y=hero.y - this.height;
// 定義繪製方法
this.paint=function(){
context.drawImage(this.imgs,this.x,this.y)
};
// 定義運動方法
this.step=function (){
this.y-=10;
};
// 定義子彈碰撞屬性 ,false表示沒有碰撞
this.candel=false;
// 定義子彈碰撞方法,碰撞之後他的碰撞屬性為true
this.bang=function(){
this.candel=true;
}
}
// 3.2.3讓所有new的子彈物件放到一個數組中(方便後面遍歷,增加,刪除)
var bullets=[];
// 3.2.4通過遍歷繪製子彈;最後在定時器中呼叫該函式即可
function bulletdPaint(){
for(var i=0;i<bullets.length;i++){
//呼叫陣列物件中的方法paint()
bullets[i].paint();
}
}
// 3.2.5通過遍歷呼叫子彈的運動;
function bulletdStep(){
for(var i=0;i<bullets.length;i++){
bullets[i].step();
}
}
// 3.2.6增加子彈的刪除函式
function bulletDel(){
//刪除子彈所滿足的條件:
// 1.碰撞的時候刪除子彈
// 2.超出畫布的高度,其實就是負的子彈的高度
for(var i=0;i<bullets.length;i++){
if(bullets[i].candel || bullets[i].y < -bullets[i].height){
//呼叫陣列中的方法
bullets.splice(i,1)
}
}
}
// 3.3敵方飛機的繪製
// 3.3.0建立敵方飛機圖片
var enemy1=[];//小飛機
enemy1[0]=new Image();
enemy1[0].src="img/enemy1.png";
enemy1[1]=new Image();
enemy1[1].src='img/enemy1_down1.png';
enemy1[2]=new Image();
enemy1[2].src='img/enemy1_down2.png';
enemy1[3]=new Image();
enemy1[3].src='img/enemy1_down3.png';
enemy1[4]=new Image();
enemy1[4].src='img/enemy1_down4.png';
var enemy2=[];//中飛機
enemy2[0]=new Image();
enemy2[0].src="img/enemy2.png";
enemy2[1]=new Image();
enemy2[1].src="img/enemy2_down1.png";
enemy2[2]=new Image();
enemy2[2].src="img/enemy2_down2.png";
enemy2[3]=new Image();
enemy2[3].src="img/enemy2_down3.png";
enemy2[4]=new Image();
enemy2[4].src="img/enemy2_down4.png";
var enemy3 = []; //大飛機
enemy3[0] = new Image();
enemy3[0].src = "img/enemy3_n1.png"
enemy3[1] = new Image();
enemy3[1].src = "img/enemy3_n2.png"
enemy3[2] = new Image();
enemy3[2].src = "img/enemy3_down1.png"
enemy3[3] = new Image();
enemy3[3].src = "img/enemy3_down2.png"
enemy3[4] = new Image();
enemy3[4].src = "img/enemy3_down3.png"
enemy3[5] = new Image();
enemy3[5].src = "img/enemy3_down4.png"
enemy3[6] = new Image();
enemy3[6].src = "img/enemy3_down5.png"
enemy3[7] = new Image();
enemy3[7].src = "img/enemy3_down6.png"
// 3.3.2敵方飛機的初始化資料
var ENEMY1={
imgs:enemy1,
length:enemy1.length,
width:57,
height:51,
type:1,
frame:1,
life:1,
score:1
}
var ENEMY2={
imgs:enemy2,
length:enemy2.length,
width:69,
height:95,
type:2,
frame:1,
life:5,
score:5
}
var ENEMY3={
imgs:enemy3,
length:enemy3.length,
width:165,
height:261,
type:3,
frame:2,
life:15,
score:20
}
// 3.3.3敵方飛機的建構函式
function Enemy(config){
this.imgs=config.imgs;
this.length=config.length;
this.width=config.width;
this.height=config.height;
this.type=config.type;
this.frame=config.frame;
this.life=config.life;
this.score=config.score;
// 定義敵方飛機的座標
this.x=Math.random()*(WIDTH-this.width);
this.y=-this.height;
//定義下標
this.startIndex=0;
//定義碰撞屬性,沒有碰撞為false
this.down=false;
//定義是否爆炸完成的屬性
this.candel=false;
//定義繪製方法
this.paint=function(){
context.drawImage(this.imgs[this.startIndex],this.x,this.y);
};
//定義運動方法
this.step = function(){
if(!this.down){ //飛機處於正常狀態
// 小飛機,中飛機的下標始終都是0
// 大飛機的下標是在0和1之間進行切換
this.startIndex ++;
this.startIndex = this.startIndex % this.frame;
// 飛機向下的動畫
this.y += 2;
}
else{ //飛機發生碰撞以後
this.startIndex ++;
if(this.startIndex == this.length){
this.candel = true;
this.startIndex = this.length - 1;
}
}
}
//是否被碰撞的方法
this.checkHit=function(wo){//判斷四個邊
return wo.y + wo.height > this.y
&& wo.x + wo.width > this.x
&& wo.y < this.y + this.height
&& wo.x < this.x + this.width;
}
//敵方飛機碰撞以後的方法
this.bang=function(){
this.life--;
if(this.life == 0){
this.down=true;
score +=this.score;
}
}
}
// 3.3.4建立陣列存放敵方飛機
var enemise=[];
// 3.3.5建立函式,往陣列中新增資料
function enterEnemise(){
//定義一個變數存放隨機數
var rand=Math.floor(Math.random()*100)
if(rand < 10){
//新增小飛機
enemise.push(new Enemy(ENEMY1));
}else if(rand < 55 && rand > 50){
//新增中飛機
enemise.push(new Enemy(ENEMY2));
}else if(rand == 88){
//新增大飛機
//大飛機尤其只有一個
//並且把大飛機放在陣列的第一位
if(enemise[0].type != 3 && enemise.length > 0){
enemise.splice(0,0,new Enemy(ENEMY3));
}
}
}
// 3.3.6建立函式繪製敵方飛機(因為敵方飛機在陣列中,所以要迴圈遍歷畫出)
function enemyPaint(){
for(var i=0;i<enemise.length;i++){
enemise[i].paint();
}
}
// 3.3.7建立函式敵方飛機的運動
function enemyStep(){
for(var i=0;i<enemise.length;i++){
enemise[i].step();
}
}
// 3.3.8建立函式刪除敵方飛機
function delenemy(){
for(var i=0;i<enemise.length;i++){
// console.log(enemise[i].candel)
if(enemise[i].y > HEIGHT || enemise[i].candel){
enemise.splice(i,1)
// arrayObject.splice(index,howmany,item1,.....,itemX)引數 描述
// index 必需。整數,規定新增/刪除專案的位置,使用負數可從陣列結尾處規定位置。
// howmany 必需。要刪除的專案數量。如果設定為 0,則不會刪除專案。
// item1, ..., itemX 可選。向陣列新增的新專案。
}
}
}
// 3.3.9繪製碰撞以後的函式
// (在上面3.3.3敵方飛機的物件中定義了一個檢測{我方飛機wo}是否碰撞的方法)
// (那個wo作為形參)(那麼下面的hero作為我方飛機的實參傳進去)(子彈的陣列中的元素物件{bullets[j]}作為實參傳進去)
function hitEnemise(){
for(var i = 0;i< enemise.length;i++){
// 如果我方飛機撞到了敵方飛機以後
if(enemise[i].checkHit(hero)){
// 處理敵方飛機碰撞以後的邏輯
enemise[i].bang();
// 處理我方飛機碰撞以後的邏輯
hero.bang()
}
// 子彈如果碰到敵方飛機以後
for(var j = 0;j<bullets.length;j++){
if(enemise[i].checkHit(bullets[j])){
enemise[i].bang();
// 子彈的碰撞邏輯
bullets[j].bang();
}
}
}
}
// 3.4繪製分數和生命值
function scoreText(){
context.font="30px bold"
context.fillText("score:"+score,10,30)
context.fillText("life:"+life,300,30)
}
//第三階段的函式和物件方法寫完了
// 4.繪製遊戲暫停的階段
//呼叫畫布的滑鼠移出事件,改變狀態
canvas.onmouseout=function(){
if(state == RUNNING){
state = PAUSE;
}
}
//呼叫畫布的滑鼠移入事件
canvas.onmouseover=function(){
if(state == PAUSE){
state = RUNNING;
}
}
//繪製暫停圖片
var pause=new Image()
pause.src="img/game_pause_nor.png"
// 5.繪製遊戲結束
function gameover(){
context.font="50px bold"
context.fillText("GAME OVER !!!",80,300)
}
setInterval(function(){
//背景圖片無論在哪個狀態都有背景圖片以及它的動態效果
sky.paint();
sky.step();
if(state==START){//第一階段
context.drawImage(logo,35,0)
}else if(state == STARTING){//第二階段
loading.paint();
loading.step();
}else if(state == RUNNING){//第三狀態
//繪製我放飛機
hero.paint();
//我方飛機的運動
hero.step();
// 我方飛機的射擊方法
hero.shoot();
//子彈的繪製
bulletdPaint();
//子彈的運動
bulletdStep();
//子彈的刪除
bulletDel();
//建立敵方飛機
enterEnemise()
//繪製敵方飛機
enemyPaint();
//繪製敵方飛機的運動
enemyStep();
//刪除敵方飛機
delenemy();
//判斷是否撞擊
hitEnemise();
//繪製分數和生命值
scoreText()
}else if(state == PAUSE){
sky.paint();
sky.step();
hero.paint();
bulletdPaint();
enemyPaint();
scoreText()
context.drawImage(pause,220,300)
}else if(state == GAMEOVER){
sky.paint();
sky.step();
hero.paint();
bulletdPaint();
enemyPaint();
scoreText();
gameover();
}
},100)
</script>
</body>
</html>
- 可能還有一些bug(遊戲體驗)
- 程式碼沒有優化到最佳。只是按照我最能理解的方式寫的。
- 還沒有形成那種很嚴密的邏輯思維,還需要多多努力。
- 感覺語法上的錯誤還好,邏輯上錯誤很不容易找出來,我還不能特別熟練通過自己調試出程式碼錯誤。
- 在應用熟練中學習了js中物件的建立,以及構造器的使用,函式呼叫等。