javascript飛機大戰,你也能寫!
作者簡介
作者名:編程界明世隱
簡介:CSDN博客專家,從事軟體開發多年,精通Java、JavaScript,博主也是從零開始一步步把學習成長、深知學習和積累的重要性,喜歡跟廣大ADC一起打野升級,歡迎您關注,期待與您一起學習、成長、起飛!
系列目錄
1. JavaScript 貪吃蛇游戲
2. JavaScript 俄羅斯方塊
3. JavaScript 掃雷小游戲
4. JavaScript 網紅太空人表盤
引言:
之前我有用Java寫過一個飛機大戰,感覺挺受歡迎的,有的小伙伴想用Javasript寫,我這次就按我的思路寫了一個JS版本,
效果圖

實作思路
- 分2張畫布來實作,畫布1僅僅用來繪制背景圖,畫布2用來繪制游戲相關的動態內容,
- 創建我方飛機,
- 定時創建敵機,
- 子執行緒來更新各種游戲元素,
- 開啟主執行緒,用來重繪畫布2,
代碼實作
撰寫頁面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>飛機大戰</title>
<style>
#box{
width:520px;
height:620px;
position:absolute;
margin:0 auto;
left:0;
right:0;
top:1px;
bottom:0;
border:solid 1px rosybrown;
}
.rebutton{
position: absolute;
top:0px;
left:31%;
}
</style>
</head>
<body>
<div id='box'></div>
<button onclick="restart()" class='rebutton'>重開</button>
<audio id="bgMusic" src="music/bg.wav" ></audio>
<audio id="boomMusic" src="music/boom.wav" ></audio>
<audio id="shootMusic" src="music/shoot.wav" ></audio>
</body>
<script src="js/util.js"></script>
<script src="js/plane.js"></script>
<script type="text/javascript">
</script>
</html>

添加畫布
在plane.js撰寫代碼
- 創建函式
function Plane(){
this.renderArr=[];//渲染陣列
this.renderArr2=[];//渲染陣列2
this.urlObj={};//圖片路徑物件
this.imgObj={};//圖片物件
this.myPlane=null;//我的飛機
this.bullets=[];//子彈陣列
this.enemyPlanes=[];//敵機陣列
this.count=1000;//1000分結束游戲,每一個10分,打死100個敵機獲得勝利
this.curCount=0;
//游戲標記
this.flag='start';
}
- 創建加載圖片和音樂的方法
//組裝圖片路徑
Plane.prototype.loadUrl=function(){
//組裝普通圖片路徑
var nameArr=['bg','bullet','myplane1','bullet','enemy1','enemy2','enemy3','enemy4','win','lost'];
var commonObj={};
for(var i=0;i<nameArr.length;i++){
commonObj[nameArr[i]]="images/"+nameArr[i]+".png";
}
//分組方便取
this.urlObj['common']=commonObj;
var boom1Obj={};
//組裝爆炸圖片路徑
for(var j=1;j<=9;j++){
boom1Obj[j]="images/myplane1boom/myplane1boom"+j+".png";
}
this.urlObj['myplane']=boom1Obj;
for (var index = 1; index <= 4; index++) {
var boomObj={};
//組裝爆炸圖片路徑
for(var i=1;i<=6;i++){
boomObj[i]="images/enemy"+index+"boom/enemy"+index+"boom"+i+".png";
}
//分組方便取
this.urlObj['boom'+index]=boomObj;
}
}
//組裝音樂物件
Plane.prototype.initMusic=function(musicObj){
var keys = Object.keys(musicObj);
var key;
for(var i=0;i<keys.length;i++)
{
key=keys[i];
this[key]=musicObj[key];
}
}
- 初始化和繪制背景代碼
//初始化
Plane.prototype.init=function(el,musicObj){
if(!el) return ;
this.el=el;
this.loadUrl();
this.initMusic(musicObj);
var canvas = document.createElement('canvas');//創建畫布
canvas.style.cssText="background:white;";
var W = canvas.width = 520; //設定寬度
var H = canvas.height = 620;//設定高度
el.appendChild(canvas);//添加到指定的dom物件中
this.ctx = canvas.getContext('2d');
this.canvas=canvas;
this.w=W;
this.h=H;
var canvas2 = document.createElement('canvas');//創建畫布
canvas2.style.cssText="position:absolute;left:0px;";//設定樣式
canvas2.width = W; //設定寬度
canvas2.height = H;//設定高度
el.appendChild(canvas2);//添加到指定的dom物件中
this.ctx2 = canvas2.getContext('2d');
this.canvas2=canvas2;
//加載圖片,并回呼繪制出圖片(因為圖片是異步加載的,所以要用回呼)
_.imageLoad(this.urlObj,this.imgObj,this.draw.bind(this));
}
//渲染圖形
Plane.prototype.render=function(){
var context=this.ctx;
this.clearCanvas();
_.each(this.renderArr,function(item){
item && item.render(context);
});
}
//清洗畫布
Plane.prototype.clearCanvas=function() {
this.ctx.clearRect(0,0,parseInt(this.w),parseInt(this.h));
}
//繪制入口
Plane.prototype.draw=function(){
this.drawBG();
this.render();//渲染到頁面上
}
//繪制背景
Plane.prototype.drawBG=function(){
var image,img,sx=0,sy=0,sWidth=520,sHeight=620,dx=0,dy=0,dWidth=520,dHeight=620;
//背景
image = this.imgObj['common']['bg'];
img = new _.ImageDraw({image:image,sx:sx,sy:sy,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight});
this.renderArr.push(img);
}
global.plane=plane;
- 頁面再加上代碼
var box = document.getElementById('box');
var bgMusic = document.getElementById('bgMusic');
var boomMusic = document.getElementById('boomMusic');
var shootMusic = document.getElementById('shootMusic');
plane.init(box,{
bgMusic:bgMusic,
boomMusic:boomMusic,
shootMusic:shootMusic
}
);
運行效果

繪制我方飛機
//創建我機
Plane.prototype.createMyPlane=function() {
var image,myPlane,sx=0,sy=0,sWidth=132,sHeight=86,dx=200,dy=530,dWidth=132,dHeight=86;
image = this.imgObj['common']['myplane1'];
myPlane = new _.ImageDraw({image:image,sx:sx,sy:sy,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight});
this.renderArr2.push(myPlane);
this.myPlane=myPlane;
//清除自己
var obj=this;
myPlane.destory=function(){
clearInterval(this.timmer);
clearInterval(this.boomTimmer);
obj.myPlane=null;
//游戲結束
obj.flag='end';
}
//爆炸函式
myPlane.boomIndex=1;
myPlane.boom=function(){
obj.boomMusic.play();
//切換圖片,切換完成,清除定時器
myPlane.boomTimmer = setInterval(doboom,100);
}
function doboom(){
if(myPlane.boomIndex>9){//爆炸完成
//清除當前飛機
myPlane.destory();
}
myPlane.image = obj.imgObj['myplane'][myPlane.boomIndex++];
}
}
修改繪制方法
//繪制入口
Plane.prototype.draw=function(){
this.drawBG();
this.render();//渲染到頁面上
this.createMyPlane();
this.render2();
}
運行效果

滑鼠事件(飛機跟隨)
- 右鍵事件屏蔽
- 加入滑鼠移動事件監聽
- 滑鼠移入飛機后,飛機跟隨滑鼠移動
- 邊界判斷,不允許超出上、下、左、右、邊界,
//右鍵事件
Plane.prototype.contextMenu=function(e){
var e = e||window.event;
//取消右鍵默認事件
e.preventDefault && e.preventDefault();
}
//滑鼠移動事件
Plane.prototype.mouseMove=function(e){
var w=132,h=86
var pos = _.getOffset(e);//獲取滑鼠位置
var plane=this.myPlane;
if(!plane) return ;
//滑鼠在飛機范圍內,才會跟隨
if(plane.isPoint(pos)){
if(isOut.call(this,pos,w,h)){
return ;
}
plane.dx=pos.x-w/2;
plane.dy=pos.y-h/2;
}
//判斷超出邊界
function isOut(pos,w,h){
if(pos.x+w/2>=this.w){//超出右邊
return true;
}
if(pos.x-w/2<=0){//超出左邊
return true;
}
if(pos.y+h/2>=this.h){//超出下邊
return true;
}
if(pos.y-h/2<=0){//超出上邊
return true;
}
return false;
}
}
- 在init方法中加入滑鼠監聽
//給canvas2畫布添加滑鼠移動事件(因為畫布2在上面)
canvas2.addEventListener('mousemove',this.mouseMove.bind(this));
//給canvas2畫布添加滑鼠右鍵事件
canvas2.addEventListener('contextmenu',this.contextMenu.bind(this));
- 加入重繪方法
//重新繪制
Plane.prototype.reDraw=function(){
if(this.flag=='start'){
this.render2();
}
}
- 加入主執行緒,用來重繪重繪
//繪制入口
Plane.prototype.draw=function(){
this.drawBG();
this.render();//渲染到頁面上
this.createMyPlane();
this.render2();
//開啟主執行緒
this.timmer = setInterval(this.reDraw.bind(this),100);
}
運行效果

繪制子彈
思路:
- 飛機定時創建子單
- 每個子單有單獨的執行緒,往上移動
- 每次移動后要判斷與飛機的碰撞
//創建子彈
Plane.prototype.createBullet=function(plane){
this.shootMusic.play();
var image,bullet,sx=0,sy=0,sWidth=20,sHeight=30,dx=0,dy=0,dWidth=20,dHeight=30;
//計算子彈的位置
dx=plane.dx+plane.dWidth/2-10;
dy=plane.dy;
image = this.imgObj['common']['bullet'];
bullet = new _.ImageDraw({image:image,sx:sx,sy:sy,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight});
this.renderArr2.push(bullet);
this.bullets.push(bullet);
bullet.timmer = setInterval(move.bind(this),80);
var obj=this;
bullet.destory=function(){
clearInterval(bullet.timmer);
obj.clear(obj.renderArr2,bullet);
obj.clear(obj.bullets,bullet);
}
//子彈的移動
function move(){
if(obj.flag!='start'){
clearInterval(bullet.timmer);
}
bullet.dy-=20;
if(bullet.dy<0){
//洗掉當前子彈
bullet.destory();
return;
}
isHitEnemy(bullet);
}
//子彈擊中敵機
function isHitEnemy(bullet){
var enemys = obj.enemyPlanes;
var enemy;
for(var i=0;i<enemys.length;i++){
enemy=enemys[i];
if(hitEnemy(bullet,enemy)){//如果某個敵機被擊中
obj.curCount+=10;
obj.countObj.text=obj.curCount;
//洗掉當前子彈
bullet.destory();
//飛機爆炸
enemy.boom();
if(obj.curCount>obj.count){//勝利
clearInterval(obj.myPlane.timmer);
obj.endShow('suc');
obj.flag='end';
}
break;
}
}
}
function hitEnemy(bullet,enemy){
//因為子彈比飛機小,所以只需要判斷子彈的4個點是否在飛機范圍內,如果有則表示碰撞了
//左上角
var x1 = bullet.dx;
var y1 = bullet.dy;
//右上角
var x2 = x1+bullet.dWidth;
var y2 = y1;
//右下角
var x3 = x1+bullet.dWidth;
var y3 = y1+bullet.dHeight;
//左下角
var x4 = x1;
var y4 = y1+bullet.dHeight;
//只要有一個點在范圍內,則判斷為碰撞
if(comparePoint(x1,y1,enemy)|| comparePoint(x2,y2,enemy)||comparePoint(x3,y3,enemy)||comparePoint(x4,y4,enemy) ){
return true;
}
return false;
}
//根據坐標判斷是否在指定的范圍內
function comparePoint(x,y,plane){
//大于左上角,小于右下角的坐標則肯定在范圍內
if(x>plane.dx && y >plane.dy
&& x<plane.dx+plane.dWidth && y <plane.dy+plane.dHeight ){
return true;
}
return false;
}
}

定時繪制敵機
- 500毫秒創建一個敵機
- 有4種敵機,采用隨機的方式來獲取,創建不同的敵機,
- 敵機的x坐標是隨機的、y坐標固定為負的圖片的寬度,
- 飛機創建后開啟定時任務向下移動,
- 當移動到最下方后,重新回到上方,
- 每次移動后會判斷是否撞擊了我放飛機,
//初始化敵機
Plane.prototype.initEnemyPlane=function(){
//定時創建敵機
this.eTimmer = setInterval(this.createEnemyPlane.bind(this),500);
}
//創建敵機
Plane.prototype.createEnemyPlane=function(){
if(this.flag!='start'){
clearInterval(this.eTimmer);
}
if(this.enemyPlanes.length>10) return ;
var image,enemyPlane,sx=0,sy=0,sWidth=0,sHeight=0,dx=200,dy=0,dWidth=0,dHeight=0;
var index = _.getRandom(1,5);
image = this.imgObj['common']['enemy'+index];
sWidth=dWidth=image.width;
sHeight=dHeight=image.height;
dx = _.getRandom(0,this.w-dWidth);
dy = -dHeight;
enemyPlane = new _.ImageDraw({image:image,sx:sx,sy:sy,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight});
//this.renderArr2.push(enemyPlane);
this.renderArr2.unshift(enemyPlane);
this.enemyPlanes.push(enemyPlane);
//清除自己
var obj=this;
enemyPlane.destory=function(){
clearInterval(enemyPlane.boomTimmer);
clearInterval(enemyPlane.timmer);
obj.clear(obj.renderArr2,enemyPlane);
obj.clear(obj.enemyPlanes,enemyPlane);
}
//爆炸函式
enemyPlane.boomIndex=1;
enemyPlane.boom=function(){
obj.boomMusic.play();
//切換圖片,切換完成,清除定時器
enemyPlane.boomTimmer = setInterval(doboom,100);
}
function doboom(){
if(enemyPlane.boomIndex>6){//爆炸完成
//清除當前飛機
enemyPlane.destory();
}
enemyPlane.image = obj.imgObj['boom'+index][enemyPlane.boomIndex++];
}
enemyPlane.timmer = setInterval(move.bind(this),50);
var obj = this;
//移動
function move(){
if(obj.flag!='start'){
clearInterval(enemyPlane.timmer);
clearInterval(enemyPlane.boomTimmer);
}
enemyPlane.dy+=2;
if(enemyPlane.dy>obj.h){//出界后重新回到上方
enemyPlane.dx = _.getRandom(0,obj.w-dWidth);
enemyPlane.dy = -dHeight;
return ;
}
//判斷與我機碰撞
if(obj.myPlane && !obj.myPlane.hitFlag && hitMyPlane(enemyPlane,obj.myPlane)){
obj.myPlane.hitFlag=true;
//清除子彈發射定時器
clearInterval(obj.myPlane.timmer);
enemyPlane.boom();
obj.myPlane.boom();
obj.endShow('end');
}
}
function hitMyPlane(enemy,myPlane){
if(!enemy||!myPlane){
return ;
}
//因為子彈比飛機小,所以只需要判斷子彈的4個點是否在飛機范圍內,如果有則表示碰撞了
//左上角
var x1 = enemy.dx;
var y1 = enemy.dy;
//右上角
var x2 = x1+enemy.dWidth;
var y2 = y1;
//右下角
var x3 = x1+enemy.dWidth;
var y3 = y1+enemy.dHeight;
//左下角
var x4 = x1;
var y4 = y1+enemy.dHeight;
//只要有一個點在范圍內,則判斷為碰撞
if(comparePoint(x1,y1,myPlane)|| comparePoint(x2,y2,myPlane)||comparePoint(x3,y3,myPlane)||comparePoint(x4,y4,myPlane) ){
return true;
}
return false;
}
//根據坐標判斷是否在指定的范圍內
function comparePoint(x,y,plane){
//大于左上角,小于右下角的坐標則肯定在范圍內
if(x>plane.dx && y >plane.dy
&& x<plane.dx+plane.dWidth && y <plane.dy+plane.dHeight ){
return true;
}
return false;
}
}
運行效果

碰撞分析(補充)



從上面幾個圖可看出什么?因為圖片是方形的,他們的4個頂點一定至少有一個在對方的范圍內,再看一下從左邊撞擊的圖:



最后加上計分的、勝利、失敗等提示就完成了!
看到這里的大佬,動動發財的小手 點贊 + 回復 + 收藏,能【 關注 】一波就更好了,
想要代碼的私聊我!
相關閱讀
1. Java俄羅斯方塊
2. 老Java程式員花2天寫了個連連看
3. 老Java程式員花一天時間寫了個飛機大戰
4. JavaWeb圖書管理系統
5. JavaWeb學生宿舍管理系統
6. JavaWeb在線考試系統
為了幫助更多小白從零進階 Java 工程師,從CSDN官方那邊搞來了一套 《Java 工程師學習成長知識圖譜》,尺寸 870mm x 560mm,展開后有一張辦公桌大小,也可以折疊成一本書的尺寸,原件129元現價 29 元,先到先得,有興趣的小伙伴可以了解一下!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/292028.html
標籤:java
