js 简单实现飞机大战

废话不多说直接上代码,素材来源均为网络

飞机大战素材 - 简书

 

<!DOCTYPE html>
<html lang="cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <!-- <script src="./plane.js"></script> -->
  <title>飞机大战</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    canvas {
      position: absolute;
      top: 0;
      left: 0;
    }
    body{
      width: 100vw;
      height: 100vh;
    }
  </style>
  </style>
</head>
<body>
  <audio src="https://webfs.hw.kugou.com/202312131420/1e8f640849ef748768e19499779b74d9/v2/178f890105e2305438245ad23fd96ffe/G232/M0A/0A/04/yJQEAF9RI-uAb0OhAB8GCj15-JQ512.mp3"></audio>
  <canvas id="canvas"></canvas>
  <script>
// 获取画布比例9:16
// 先创建素材

const bgImage = new Image();bgImage.src = "https://upload-images.jianshu.io/upload_images/14016888-07ab84dec27c6612.png?imageMogr2/auto-orient/strip|imageView2/2/w/480/format/webp";
const userplane = new Image();userplane.src = "https://upload-images.jianshu.io/upload_images/14016888-e3d2f21c6e2cc265.png?imageMogr2/auto-orient/strip|imageView2/2/w/46/format/webp";
const enemyplane = new Image();enemyplane.src = "https://upload-images.jianshu.io/upload_images/14016888-6b9624c1deb561ae.png?imageMogr2/auto-orient/strip|imageView2/2/w/69/format/webp";
const bullet1 = new Image();bullet1.src = "https://upload-images.jianshu.io/upload_images/14016888-ff389915bee91bc6.png?imageMogr2/auto-orient/strip|imageView2/2/w/5/format/webp";
const bullet2 = new Image();bullet2.src = "https://upload-images.jianshu.io/upload_images/14016888-8fe683430d5cd233.png?imageMogr2/auto-orient/strip|imageView2/2/w/5/format/webp";
const enemydown1 = new Image();enemydown1.src = "https://upload-images.jianshu.io/upload_images/14016888-5de26da9ac6b826d.png?imageMogr2/auto-orient/strip|imageView2/2/w/69/format/webp";
const enemydown2 = new Image();enemydown2.src = "https://upload-images.jianshu.io/upload_images/14016888-36c5ade8705d1b12.png?imageMogr2/auto-orient/strip|imageView2/2/w/69/format/webp";
const enemydown3 = new Image();enemydown3.src = "https://upload-images.jianshu.io/upload_images/14016888-70f509fa1fcb2c06.png?imageMogr2/auto-orient/strip|imageView2/2/w/69/format/webp";
const enemydown4 = new Image();enemydown4.src = "https://upload-images.jianshu.io/upload_images/14016888-ca62513fe0687c25.png?imageMogr2/auto-orient/strip|imageView2/2/w/69/format/webp";
const zd = new Image();zd.src = "https://upload-images.jianshu.io/upload_images/14016888-a75c7fdeac527b6b.png?imageMogr2/auto-orient/strip|imageView2/2/w/60/format/webp";
const zdnum = new Image();zdnum.src = "https://upload-images.jianshu.io/upload_images/14016888-78f4b7ce15c74a8c.png?imageMogr2/auto-orient/strip|imageView2/2/w/63/format/webp";
/**
 * 判断是否碰撞
 * 因为只记录的飞机的x,y坐标所有要加上size,才是飞机的面积
 */

class Game {
  constructor() {
    this.Images = {
      0:enemyplane,
      1:enemydown1,
      2:enemydown2,
      3:enemydown3,
      4:enemydown4,
      zd:zd,
      zdnum:zdnum
    }
    this.size = 50; // 格子大小
    this.bulletSize = 10; // 子弹大小
    this.requestAFId = null;
    this.isGameOver = false; // 是否开始游戏
    this.isGameOver = false;
    this.cvs = document.getElementById("canvas"),
    this.ctx = document.getElementById("canvas").getContext("2d"),
    this.config = {
      width: window.innerWidth,
      height: window.innerHeight,
      offsetLeft:this.cvs.offsetLeft,
      offsetTop:this.cvs.offsetTop,
    };

    this.planeSpeed = 2;  // 飞机移动速度
    this.userBulletSpeed = 10; // 玩家子弹移动速度
    this.enemyBulletSpeed = 3; // 敌人子弹移动速度
    this.userBulletStep = 200; // 玩家子弹间隔时间
    this.enemyBulletStep = 1500; // 敌人子弹间隔时间
    this.bullets = {user:[],enemy:[]}; // 子弹数组 enemy弃用
    this.lifeNum = 3;
    this.life = this.lifeNum;
    this.userPointer = {x: this.size *4,y: document.body.clientHeight / 1.5,width:this.size,height:this.size}; // 玩家飞机坐标
    this.enemyPointer = [{
      x: this.size * 4,
      y: 0,is:false,
      width:this.size,
      height:this.size,
      destroyStep:0,
      image:this.Images[0],
      bullet:[]}
    ]  // 敌人飞机坐标
    this.enemyPlaneNum = 50;     // 场上敌人飞机数量
    this.enemyPlaneStep = 500;   // 生成敌人飞机间隔
    this.touchEnter = false;     // 是否点击到玩家飞机
    this.collision = false;      // 是否碰撞
    this.score = 0;              // 分数
    this.enemyDestroyTime = 200; // 敌人飞机销毁时间

    this.enemyBulletSetI = null; // 敌人子弹定时器
    this.userBulletSetI = null;  // 玩家子弹定时器
    this.enemyCreateSetI = null; // 生成敌机定时器

    // 炸弹
    this.zdSpeed = 5;
    this.zdStep = 1;
    this.zdnum = 0;
    this.zd = [];

    // min/max
    this.min = 0;
    this.max = document.body.clientWidth / this.size;
      console.dir(document.body);
    this.startBg();
    this.addEvent();
  }
  gameStart(){
    if(!this.isActive){
      this.isActive = true;
      this.init();
    }
  }
  startBg(){
    const cvs = this.cvs;
    const ctx = this.ctx;
    this.BG(cvs,ctx)
    ctx.font = '50px 宋体';
    ctx.fillText("点击开始",cvs.width / 2 - 100 , cvs.height / 2);
  }
  //重新开始
  restart(){
    this.clearEnemyCreateSetI();
    this.isActive = false;
    this.isGameOver = false;
    this.life = this.lifeNum;
    this.zdStep = 1;
    this.zdnum = 0;
    this.zd = [];
    clearInterval(this.enemyBulletSetI);
    clearInterval(this.userBulletSetI);
    clearInterval(this.enemyCreateSetI);
    this.score = 0;
    this.enemyPointer = [];
    this.userPointer = {x: this.size *4,y: document.body.clientHeight / 1.5,width:this.size,height:this.size}; // 玩家飞机坐标
  }
  restartBg(){
    this.restart();
    const cvs = this.cvs;
    const ctx = this.ctx;
    cvs.removeEventListener("mousedown", this.mousedownFn);
    this.mouseupFn();
    ctx.clearRect(0, 0, cvs.width, cvs.height);
    this.BG(cvs,ctx);
    ctx.font = '50px 宋体';
    console.log(this.userPointer)
    ctx.fillText("点击重新开始",cvs.width / 2 - 100 - 50 , cvs.height / 2);
    this.addEvent();
  }

  // 清除敌人飞机定时器
  clearEnemyCreateSetI(){
    for(let i = 0; i < this.enemyPointer.length; i++){
      const item = this.enemyPointer[i];
      clearInterval(item.setInterval);
    }
  }
  // 奖励
  reward(){
  }
  init() {
    const { width, height } = this.config;
    const cvs = this.cvs;
    cvs.width = width;
    cvs.height = height;
    
    this.render();
    this.enemyCreate();
    this.enemyBullet();
    this.userBullet();
    this.enemyBulletSetI = setInterval(() => {
      this.enemyBullet();
    }, this.enemyBulletStep);
    this.userBulletSetI = setInterval(() => {
      this.userBullet();
    }, this.userBulletStep);
  }
  // 生成炸弹道具
  zdCreate() {
    const step = this.score / this.zdStep;
    if(step == 100){
      ++this.zdStep;
      const obj = {
        x:this.getRandom(this.min,this.max) * this.size,
        y:0,
        width:this.size,
        height:this.size
      }
      this.zd.push(obj);
    }
  }
  // 判断是否触碰到炸弹道具
  touchzd(){
    this.zdMove();
    for(let i = 0; i < this.zd.length; i++){
      const item = this.zd[i];
      if(this.isCollisionFn(item,this.userPointer)){
        this.zd.splice(i,1);
        this.zdnum++;
      }
    }
  }
  zdMove(){
    for(let i = 0; i < this.zd.length; i++){
      const item = this.zd[i];
      if(item.y < this.cvs.height){
        item.y += this.zdSpeed;
      }else{
        this.zd.splice(i,1);
      }
    }
  }
  // 绘制炸弹
  drawzd() {
    this.touchzd();
    for(let i = 0; i < this.zd.length; i++){
      const item = this.zd[i];
      this.ctx.drawImage(this.Images.zd, item.x, item.y, this.size, this.size);
    }
  }
  // 绘制炸弹数量
  drawzdnum() {
    this.ctx.drawImage(this.Images.zdnum, this.cvs.width -this.size*2, 20, this.size, this.size);
    this.ctx.font = "30px 宋体";
    this.ctx.fillStyle = "yellow";
    this.ctx.fillText(this.zdnum, this.cvs.width -this.size + 10, this.size);
  }
  // 使用炸弹
  usezd() {
    if(this.zdnum <= 0) return;
    this.zdnum--;
    this.usezdAll()
  }
  // 使用炸弹请出屏幕所有敌人
  usezdAll() {
    this.isBulletCollision(true);
  }
  // 获取随机数
  getRandom(min, max) {
    return Math.random() * (max - min + 1) + min;
  }
  drawScore(){
    this.ctx.font = "20px 宋体";
    this.ctx.fillStyle = "yellow";
    this.ctx.fillText(this.score + '分', 10, 20);
  }
  // 背景
  BG(cvs,ctx) {
    const { width, height } = this.config;
    ctx.clearRect(0, 0, width, height);
    cvs.width = width;
    cvs.height = height;
    ctx.drawImage(bgImage, 0, 0, width, height);
  }
  // 生成玩家发射子弹
  userBullet() {
    const { x, y } = this.userPointer;
    this.bullets.user.push({ x:x + this.size / 2 - 3.5, y:y - 25,width:this.bulletSize,height:this.bulletSize*2});
  }
  // 生成敌人子弹
  enemyBullet() {
    if(this.enemyPointer.length <= 0) return;
    for(let i = 0; i < this.enemyPointer.length; i++){
      if(this.enemyPointer[i].is) continue;
      const { x, y } = this.enemyPointer[i];
      const item = this.enemyPointer[i];
      item.bullet.push({bulletX:x + this.size / 2 - 3.5,bulletY:y + 25,bulletWidth:this.bulletSize,bulletHeight:this.bulletSize*2})
    }
  }
  // 绘制所有子弹
  bulletDraw(ctx) {
    for (let i = 0; i < this.bullets.user.length; i++) {
      const { x, y,width,height } = this.bullets.user[i];
      ctx.drawImage(bullet1, x, y, width, height);
    }
    for (let i = 0; i < this.enemyPointer.length; i++) {
      const item = this.enemyPointer[i];
      for(let j = 0; j < item.bullet.length; j++){
        const { bulletX:x,bulletY:y,bulletWidth:width,bulletHeight:height } = item.bullet[j];
        ctx.drawImage(bullet2, x, y, width, height);
      }
    }
  }
  // 子弹移动
  bulletMove() {
    for (let i = 0; i < this.bullets.user.length; i++) {
      const { y } = this.bullets.user[i];
      this.bullets.user[i].y -= this.userBulletSpeed;
      if (y <= 0) {
        this.bullets.user.splice(i, 1);
        i--;
      }
    }
    for (let i = 0; i < this.enemyPointer.length; i++) {
      const item = this.enemyPointer[i];
      if(item.is == true && item.bullet.length == 0 && !item.setInterval){
        this.enemyPointer.splice(i, 1);
        continue;
      }
      for(let j = 0; j < item.bullet.length; j++){
        const itemJ = item.bullet[j];
        itemJ.bulletY += this.enemyBulletSpeed;
        if (itemJ.bulletY >= this.config.height) {
          item.bullet.splice(i, 1);
        }
      }
    }
    this.bulletHit();
  }
  // 子弹抵消
  bulletHit() {
    this.enemyPointer.map(itemp => {
      itemp.bullet.map((itemb,indexb) => {
        this.bullets.user.map((itemu,indexu) => {
          if(this.isCollisionFn(itemu,{x:itemb.bulletX,y:itemb.bulletY,width:itemb.bulletWidth,height:itemb.bulletHeight})){
            itemp.bullet.splice(indexb, 1);
            this.bullets.user.splice(indexu, 1);
          }
        })
      })
    })
  }
  // 生成敌人飞机
  enemyCreate(){
    this.enemyCreateSetI = setInterval(() => {
      this.enemyCreateFn();
    }, this.enemyPlaneStep);
  }
  enemyCreateFn(){
    if(this.enemyPointer.length >= this.enemyPlaneNum) return;
    const x = this.getRandom(this.min,this.max);
    this.enemyPointer.push({x:x * this.size,y:0,is:false,width:this.size,height:this.size,destroyStep:0,image:this.Images[0],bullet:[]});
  }
  // 绘制敌对飞机
  enemy(ctx) {
    for(let i = 0; i < this.enemyPointer.length; i++){
      const item = this.enemyPointer[i];
      if(item.is && item.destroyStep >= 4) {
        continue;
      };
      const { x, y,image } = this.enemyPointer[i];
      ctx.drawImage(image, x, y, this.size, this.size);
    }
  }
  // 绘制被击毁的动画
  enemyDestroy() {
    for(let i = 0; i < this.enemyPointer.length; i++){
      const item = this.enemyPointer[i];
      if(!item.is)continue;
      if(item.destroyStep >= 4){
        clearInterval(item.setInterval);
        item.setInterval = null;
        continue;
      };
      item.destroyStep++;
      item.image = this.Images[item.destroyStep];
    }
  }

  // 判断是否碰撞
  isCollision(){
    for(let i = 0; i < this.enemyPointer.length; i++){
      const item = this.enemyPointer[i];
      if(item.is) continue;
      if(this.isCollisionFn(this.userPointer,item)){
        --this.life;
        this.enemyPointer.splice(i, 1);
        if(this.life == 0){
          this.isGameOver = true;
        }
      }
    }
  }
  // 判断矩形是否相交
  isCollisionFn(rectangle1, rectangle2) {
    const x1 = rectangle1.x;
    const y1 = rectangle1.y;
    const width1 = rectangle1.width;
    const height1 = rectangle1.height;

    const x2 = rectangle2.x;
    const y2 = rectangle2.y;
    const width2 = rectangle2.width;
    const height2 = rectangle2.height;

    const left1 = x1;
    const right1 = x1 + width1;
    const top1 = y1;
    const bottom1 = y1 + height1;

    const left2 = x2;
    const right2 = x2 + width2;
    const top2 = y2;
    const bottom2 = y2 + height2;

    return !(right1 < left2 || left1 > right2 || bottom1 < top2 || top1 > bottom2);
  }
  // 判断子弹是否击中敌人或被击中
  // is:清空敌人
  isBulletCollision(is = false){
    for(let i = 0; i < this.enemyPointer.length; i++){
      const item = this.enemyPointer[i];
      if(is){
        this.score += 10;
        item.is = true;
        item.destroyStep = 1;
        item.image = this.Images[item.destroyStep];
        item.bullet = [];
        if(item.setInterval)return;
        item.setInterval = setInterval(() => {
          this.enemyDestroy();
        }, this.enemyDestroyTime);
        continue;
      }
      if(item.is) continue;
      if(this.isBulletCollisionFn(item)){
        this.score += 10;
        item.is = true;
        item.destroyStep = 1;
        item.image = this.Images[item.destroyStep];
        
        if(item.setInterval)return;
        item.setInterval = setInterval(() => {
          this.enemyDestroy();
        }, this.enemyDestroyTime);
      }
    }
    for(let i = 0; i < this.enemyPointer.length; i++){
      const item = this.enemyPointer[i];
      for(let j = 0; j < item.bullet.length; j++){
        const itemJ = item.bullet[j];
        const obj = {
          x:itemJ.bulletX,
          y:itemJ.bulletY,
          width:itemJ.bulletWidth,
          height:itemJ.bulletHeight
        }
        if(this.isCollisionFn(this.userPointer,obj)){
          --this.life;
          item.bullet.splice(j,1)
          if(this.life == 0){
            this.isGameOver = true;
          }
        }
      }
    }
  }
  // 是否击中敌人
  isBulletCollisionFn(plane) {
    for (let i = 0; i < this.bullets.user.length; i++) {
      if(this.isCollisionFn(this.bullets.user[i],{x:plane.x,y:plane.y,width:plane.width,height:plane.height})){
        return true;
      }
    }
  }
  // 生成玩家
  player(ctx) {
    const { x, y } = this.userPointer;
    ctx.beginPath();
    ctx.drawImage(userplane, x, y, this.size, this.size);
    ctx.rect(x,y,this.size, this.size);
    ctx.stroke()
    ctx.closePath();
    this.drawLife();
  }
  // 绘制生命条
  drawLife() {
    const { x, y } = this.userPointer;
    const ctx = this.ctx;
    const step = this.lifeNum / this.life;
    ctx.beginPath();
    ctx.rect(x,y - 10,this.size,10);
    ctx.stroke();
    ctx.closePath();
    ctx.beginPath();
    ctx.rect(x + 1,y - 9,this.size / step,8)
    ctx.fillStyle = "red";
    ctx.fill();
    ctx.closePath();
  }
  // 敌对飞机移动
  enemyMove() {
    for (let i = 0; i < this.enemyPointer.length; i++) {
      const item = this.enemyPointer[i];
      if(item.is)continue;

      item.y += this.planeSpeed;
      if(item.y >= this.size * 16){

        this.enemyPointer.splice(i,1);
      }
    }
    this.isBulletCollision();
    this.isCollision();
  }
  // 渲染
  render() {
    const cvs = this.cvs;
    const ctx = this.ctx;
    this.BG(cvs,ctx);
    this.player(ctx);
    this.enemy(ctx);
    this.bulletMove();
    this.bulletDraw(ctx);
    this.enemyMove();
    this.zdCreate();
    this.drawzd();
    this.drawScore();
    this.drawzdnum();
    if(this.isGameOver){
      cancelAnimationFrame(this.requestAFId);
      alert("游戏结束");
      this.restartBg();
    }else{
      this.requestAFId = requestAnimationFrame(() => {
        this.render();
      })
    }
  }
  // 判断是否点击到飞机
  isTouchPlane = (init) => {
    const { x: ux, y: uy } = this.userPointer;
    const { x, y } = init;
    if (x >= ux && x <= ux + this.size && y >= uy && y <= uy + this.size) {
      this.touchEnter = true;
    } else {
      this.touchEnter = false;
    }
  };
  // 添加事件
  mousedownFn = (event) => {
    // openFullscreen(document.documentElement);
    if(!this.isActive){
      this.gameStart();
      return;
    }
    const {x,y} = this.eventValue(event);
    const {offsetLeft:left,offsetTop:top} = this.cvs;
    const e = { x: x - left, y: y - top };
    this.isTouchPlane(e);
    if (this.touchEnter) {
      document.addEventListener(emove, this.mousemoveFn,false);
      document.addEventListener(eup, this.mouseupFn);
    }
  };
  mousemoveFn = (event) => {
    if (!this.touchEnter) return;
    const { x:offsetX, y:offsetY } = this.eventValue(event);
    const { offsetLeft: left, offsetTop: top } = this.cvs;
    const x = offsetX - left - this.size / 2;
    const y = offsetY - top - this.size / 2;
    this.userPointer.x = x;
    this.userPointer.y = y
  };
  mouseupFn = () => {
    document.removeEventListener(emove, this.mousemoveFn);
    document.removeEventListener(eup, this.mouseupFn);
  };
  // 判断pc还是移动端使用不同的值
  eventValue(e) {
    let x,y;
    if(e.touches){
      x = e.touches[0].clientX;
      y = e.touches[0].clientY;
    }else{
      x = e.clientX;
      y = e.clientY;
    }
    return {x,y}
  }
  // 空格按键
  keydownFn = (e) => {
    if (e.keyCode == 32) {
      this.usezd();
    }
  };
  addEvent = () => {
    const canvas = this.cvs;
    document.addEventListener('keydown',this.keydownFn)
    canvas.addEventListener(edown, this.mousedownFn);
  };
}
let game;
function isMobileDevice() {
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}

function openFullscreen(elem) {
  if (elem.requestFullscreen) {
    elem.requestFullscreen();
  } else if (elem.mozRequestFullScreen) { /* Firefox */
    elem.mozRequestFullScreen();
  } else if (elem.webkitRequestFullscreen) { /* Chrome, Safari 和 Opera */
    elem.webkitRequestFullscreen();
  } else if (elem.msRequestFullscreen) { /* IE/Edge */
    elem.msRequestFullscreen();
  }
}
let edown,emove,eup;
if(isMobileDevice()){
  edown = "touchstart";
  emove = "touchmove";
  eup = "touchend";
}else{
  edown = "mousedown";
  emove = "mousemove";
  eup = "mouseup";
}
function start() {
  if (game) {
    game.re();
  }
  game = new Game();
}
window.onload = () => {
  game = new Game();
};

  </script>
</body>
</html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值