Saya mencoba memperbarui rentetan peluru setiap frame dalam JavaScript, tetapi sepertinya itu tidak berfungsi dengan baik. Inilah kode yang saya taruh di JSFiddle.

https://jsfiddle.net/Reverseblade/co2mpLnv/5/

var ctx;
    var hero;
    var enemy;
    var beams = [];
    var beam_hitting = false;
    var continuous_hit = false;
    var count = 0;
    var canvas_w = 500, canvas_y= 700;
    var keycode = NaN;
    var laser_degree = 200;

    function init(){
      createCanvas();
      createMainHero();
      createEnemy();
      draw();
      mainRoutine();
    }

    function mainRoutine(){
      count++;
      ctx.clearRect(0, 0, canvas_w, canvas_y);
      // laserTest();

      hero.move(keycode);
      enemyUpdate();
      // hero.setBullets();
      // hero.moveBullets();
      draw();

      window.setTimeout(mainRoutine, 7);
    }

    function createCanvas(){
      var canvas = document.createElement("canvas");
      canvas.id = "canvas";
      canvas.width = canvas_w;
      canvas.height = canvas_y;
      document.body.appendChild(canvas);
    }

    function createMainHero(){
      hero = new Hero();
    }

    function createEnemy(){
      enemy = new Enemy;
    }


    function Hero(){
      this.w = this.h = 25,
      this.x = canvas_w/2 - this.w/2,
      this.y = canvas_y - this.h,
      this.dx = this.dy = 2.5;
      this.bullets = [];
      this.move = function(key){
        switch(key){
          case 37: if (hero.x > 0) {hero.x -= this.dx;} break;
          case 38: if (hero.y > 0) {hero.y -= this.dy;} break;
          case 39: if (hero.x < canvas_w - hero.w) {hero.x += this.dx;} break;
          case 40: if (hero.y < canvas_y - hero.h) {hero.y += this.dy;} break;
        }
      };
      this.setBullets = function(){
        if (count % 20 === 0) {
          var w = h = 8;
          var dx = dy = 5;
          var x = hero.x + hero.w/2 - w/2;
          var y = hero.y;
          hero.bullets.push({x: x, y: y, w: w, h: h, dx: dx, dy: dy, moving:true});
        }
      };
      this.moveBullets = function(){
      for (var i = 0; i < this.bullets.length; i++) {
        if (this.bullets[i].y < 0) {
          this.bullets[i].moving = false;
          continue;
        }
        if (this.bullets[i].moving === false) {
          continue;
        }
        this.bullets[i].y -= this.bullets[i].dy;
        if (this.bullets[i].y < -100) {this.bullets.splice(i, 1)}
      }
    }

    }

    function Enemy(){
      this.w = this.h = 25;
      this.x = canvas_w/2 - this.w/2;
      this.y = 50;
      this.bullets = [];
      this.moving = false;
      this.move_to = 0;
      this.bullets_count = 0;
      this.bullets_angle = 0;
      this.current_skill = 1;
      this.barrage_count = 0;
      this.skill = function(){
        enemySkill();
      };
    }


    function enemyUpdate(){

      function move(){

        function changeDirection(){
          var options = ["left", "right", "up", "down"];
          var id;
          if (enemy.x <= 50) {id = options.indexOf("left"); options.splice(id, 1);}
          if (enemy.x >= 450) {id = options.indexOf("right");options.splice(id, 1);}
          if (enemy.y <= 50) {id = options.indexOf("up");options.splice(id, 1);}
          if (enemy.y >= 200) {id = options.indexOf("down");options.splice(id, 1);}

          var rand = Math.floor(Math.random() * options.length);
          enemy.moving = options[rand];

          switch(enemy.moving){
            case "left": enemy.move_to = enemy.x - 150 ; break;
            case "right": enemy.move_to = enemy.x + 150 ; break;
            case "up": enemy.move_to = enemy.y - 150 ; break;
            case "down": enemy.move_to = enemy.y + 150 ; break;
          }
        } /* end changeDirection() */

        if (count % 800 === 0) {changeDirection(); console.log("changing");}


        switch(enemy.moving){
          case "left": if (enemy.x > 50 && enemy.x > enemy.move_to) {enemy.x -= 0.5;} break;
          case "right": if (enemy.x < 450 && enemy.x < enemy.move_to) {enemy.x += 0.5;} break;
          case "up": if (enemy.y > 50 && enemy.y > enemy.move_to) {enemy.y -= 0.5; } break;
          case "down": if (enemy.y < 200 && enemy.y < enemy.move_to) {enemy.y += 0.5; } break;
        }

      } /* end move()*/

      move();
      enemy.skill();
    } /* end enemyUpdate() */

    function enemySkill(){
      // console.log("enemy skill");
      function setBullets(){
        var prev_status = enemy.bullets_count === 0 ? 500 : enemy.bullets[enemy.bullets.length - 1]["radius"];
        if (prev_status >25) {
           // console.log("bullets set");
          var center_x = enemy.x + enemy.w/2;
          var center_y = enemy.y + enemy.h/2;
          var radius = 20;
          var ceil = enemy.bullets.length === 0 ? 0 : enemy.bullets.length -1;
          for (var angle = enemy.bullets_angle, i= ceil; angle < enemy.bullets_angle + 360; angle += 40, i++ ) {
            // console.log("i: " + i);
            var radian = angle * Math.PI / 180;
            var set_x = center_x + radius * Math.cos(radian);
            var set_y = center_y + radius * Math.sin(radian);
            // console.log("angle: " + /angle + "set_x: " + set_x + "set_y: " + set_y);
            enemy.bullets.push({"x": set_x, "y": set_y, "moving": true, "radius": radius, "center_x": center_x, "center_y": center_y, "w": 25, "h": 25, "radian": radian});
            if (enemy.bullets_count === 0) {enemy.bullets_count++;}
            // console.log(enemy.bullets[0][i]["x"]);
         }
         enemy.bullets_angle += 10;
         enemy.barrage_count ++;
         if (enemy.barrage_count % 100 === 0) {
           enemy.current_skill = 0;
         }
        }

      } /* end setBullets */


      function moveBullets(){
        if (count % 4 ===0) {
          for (var i = 0; i < enemy.bullets.length; i++) {
            if (enemy.bullets[i]["moving"] === true) {
              var radian = enemy.bullets[i]["radian"];
              var center_x = enemy.bullets[i]["center_x"];
              var center_y = enemy.bullets[i]["center_y"];
              enemy.bullets[i]["radius"] += 5;
              var radius = enemy.bullets[i]["radius"];
              var set_x = center_x + radius * Math.cos(radian);
              var set_y = center_y + radius * Math.sin(radian);
              // console.log(set_y);
              enemy.bullets[i]["x"] = set_x;
              enemy.bullets[i]["y"] = set_y;
              if (enemy.bullets[i]["x"] < -100 || enemy.bullets[i]["x"] > canvas_w + 100 || enemy.bullets[i]["y"] < -100 || enemy.bullets[i]["y"] > canvas_y + 100 ) {
                // enemy.bullets[i]["moving"] = false;
                enemy.bullets.splice(i, 1);
              }

              }
            }
          }
        }

      if (enemy.current_skill === 1) {
        setBullets();
      }
      moveBullets();
    }

    


    function draw(){
      var canvas = document.getElementById("canvas");
      ctx = canvas.getContext("2d");

      //hero
      //ctx.fillStyle = "blue";
      //ctx.fillRect(hero.x, hero.y ,hero.w, hero.h);

      //enemy
      //ctx.fillStyle = "red";
      //ctx.fillRect(enemy.x, enemy.y ,enemy.w, enemy.h);

      //heroの弾
      ctx.fillStyle = "blue";
      for (var i = 0; i < hero.bullets.length; i++) {
        if (hero.bullets[i].moving === false) {
          continue;
        }
        ctx.fillRect(hero.bullets[i].x, hero.bullets[i].y ,hero.bullets[i].w, hero.bullets[i].h);
      }

      //敵の弾
      ctx.fillStyle = "red";
      for (var i = 0; i < enemy.bullets.length; i++) {
         ctx.fillStyle = "green";

        if (enemy.bullets[i]["moving"] === false) {
          continue;
        }

          ctx.beginPath();
          ctx.arc(enemy.bullets[i]["x"], enemy.bullets[i]["y"], 15, 0, 2 * Math.PI, false);
          ctx.closePath();
          ctx.fill();

      }
    }





    window.addEventListener("keydown", function(e){
      switch(e.keyCode){
        case 37: keycode = 37; break;
        case 38: keycode = 38; break;
        case 39: keycode = 39; break;
        case 40: keycode = 40; break;
      }
    }, false);

    window.addEventListener("keyup", function(e){
      switch(e.keyCode){
        case 37: keycode = NaN; break;
        case 38: keycode = NaN; break;
        case 39: keycode = NaN; break;
        case 40: keycode = NaN; break;
      }
    }, false);


    init();
*{
  margin:0;
  padding:0;
}

#canvas{
  background:url("../img/seamles_space.jpg");
  animation: mainBack 5s linear infinite;
  animation-play-state:paused;
  background: black;
  display: block;
  position:relative;
  margin:50px auto;
}
<body>
<script src="js/main.js"></script>
</body>
var ctx;
var hero;
var enemy;
var beams = [];
var beam_hitting = false;
var continuous_hit = false;
var count = 0;
var canvas_w = 500, canvas_y= 700;
var keycode = NaN;
var laser_degree = 200;

function init(){
  createCanvas();
  createMainHero();
  createEnemy();
  draw();
  mainRoutine();
}

function mainRoutine(){
  count++;
  ctx.clearRect(0, 0, canvas_w, canvas_y);
  // laserTest();

  hero.move(keycode);
  enemyUpdate();
  // hero.setBullets();
  // hero.moveBullets();
  draw();

  window.setTimeout(mainRoutine, 7);
}

function createCanvas(){
  var canvas = document.createElement("canvas");
  canvas.id = "canvas";
  canvas.width = canvas_w;
  canvas.height = canvas_y;
  document.body.appendChild(canvas);
}

function createMainHero(){
  hero = new Hero();
}

function createEnemy(){
  enemy = new Enemy;
}


function Hero(){
  this.w = this.h = 25,
  this.x = canvas_w/2 - this.w/2,
  this.y = canvas_y - this.h,
  this.dx = this.dy = 2.5;
  this.bullets = [];
  this.move = function(key){
    switch(key){
      case 37: if (hero.x > 0) {hero.x -= this.dx;} break;
      case 38: if (hero.y > 0) {hero.y -= this.dy;} break;
      case 39: if (hero.x < canvas_w - hero.w) {hero.x += this.dx;} break;
      case 40: if (hero.y < canvas_y - hero.h) {hero.y += this.dy;} break;
    }
  };
  this.setBullets = function(){
    if (count % 20 === 0) {
      var w = h = 8;
      var dx = dy = 5;
      var x = hero.x + hero.w/2 - w/2;
      var y = hero.y;
      hero.bullets.push({x: x, y: y, w: w, h: h, dx: dx, dy: dy, moving:true});
    }
  };
  this.moveBullets = function(){
  for (var i = 0; i < this.bullets.length; i++) {
    if (this.bullets[i].y < 0) {
      this.bullets[i].moving = false;
      continue;
    }
    if (this.bullets[i].moving === false) {
      continue;
    }
    this.bullets[i].y -= this.bullets[i].dy;
    if (this.bullets[i].y < -100) {this.bullets.splice(i, 1)}
  }
}

}

function Enemy(){
  this.w = this.h = 25;
  this.x = canvas_w/2 - this.w/2;
  this.y = 50;
  this.bullets = [];
  this.moving = false;
  this.move_to = 0;
  this.bullets_count = 0;
  this.bullets_angle = 0;
  this.current_skill = 1;
  this.barrage_count = 0;
  this.skill = function(){
    enemySkill();
  };
}


function enemyUpdate(){

  function move(){

    function changeDirection(){
      var options = ["left", "right", "up", "down"];
      var id;
      if (enemy.x <= 50) {id = options.indexOf("left"); options.splice(id, 1);}
      if (enemy.x >= 450) {id = options.indexOf("right");options.splice(id, 1);}
      if (enemy.y <= 50) {id = options.indexOf("up");options.splice(id, 1);}
      if (enemy.y >= 200) {id = options.indexOf("down");options.splice(id, 1);}

      var rand = Math.floor(Math.random() * options.length);
      enemy.moving = options[rand];

      switch(enemy.moving){
        case "left": enemy.move_to = enemy.x - 150 ; break;
        case "right": enemy.move_to = enemy.x + 150 ; break;
        case "up": enemy.move_to = enemy.y - 150 ; break;
        case "down": enemy.move_to = enemy.y + 150 ; break;
      }
    } /* end changeDirection() */

    if (count % 800 === 0) {changeDirection(); console.log("changing");}


    switch(enemy.moving){
      case "left": if (enemy.x > 50 && enemy.x > enemy.move_to) {enemy.x -= 0.5;} break;
      case "right": if (enemy.x < 450 && enemy.x < enemy.move_to) {enemy.x += 0.5;} break;
      case "up": if (enemy.y > 50 && enemy.y > enemy.move_to) {enemy.y -= 0.5; } break;
      case "down": if (enemy.y < 200 && enemy.y < enemy.move_to) {enemy.y += 0.5; } break;
    }

  } /* end move()*/

  move();
  enemy.skill();
} /* end enemyUpdate() */

function enemySkill(){
  // console.log("enemy skill");
  function setBullets(){
    var prev_status = enemy.bullets_count === 0 ? 500 : enemy.bullets[enemy.bullets.length - 1]["radius"];
    if (prev_status >25) {
       // console.log("bullets set");
      var center_x = enemy.x + enemy.w/2;
      var center_y = enemy.y + enemy.h/2;
      var radius = 20;
      var ceil = enemy.bullets.length === 0 ? 0 : enemy.bullets.length -1;
      for (var angle = enemy.bullets_angle, i= ceil; angle < enemy.bullets_angle + 360; angle += 40, i++ ) {
        // console.log("i: " + i);
        var radian = angle * Math.PI / 180;
        var set_x = center_x + radius * Math.cos(radian);
        var set_y = center_y + radius * Math.sin(radian);
        // console.log("angle: " + /angle + "set_x: " + set_x + "set_y: " + set_y);
        enemy.bullets.push({"x": set_x, "y": set_y, "moving": true, "radius": radius, "center_x": center_x, "center_y": center_y, "w": 25, "h": 25, "radian": radian});
        if (enemy.bullets_count === 0) {enemy.bullets_count++;}
        // console.log(enemy.bullets[0][i]["x"]);
     }
     enemy.bullets_angle += 10;
     enemy.barrage_count ++;
     if (enemy.barrage_count % 100 === 0) {
       enemy.current_skill = 0;
     }
    }

  } /* end setBullets */


  function moveBullets(){
    if (count % 4 ===0) {
      for (var i = 0; i < enemy.bullets.length; i++) {
        if (enemy.bullets[i]["moving"] === true) {
          var radian = enemy.bullets[i]["radian"];
          var center_x = enemy.bullets[i]["center_x"];
          var center_y = enemy.bullets[i]["center_y"];
          enemy.bullets[i]["radius"] += 5;
          var radius = enemy.bullets[i]["radius"];
          var set_x = center_x + radius * Math.cos(radian);
          var set_y = center_y + radius * Math.sin(radian);
          // console.log(set_y);
          enemy.bullets[i]["x"] = set_x;
          enemy.bullets[i]["y"] = set_y;
          if (enemy.bullets[i]["x"] < -100 || enemy.bullets[i]["x"] > canvas_w + 100 || enemy.bullets[i]["y"] < -100 || enemy.bullets[i]["y"] > canvas_y + 100 ) {
            // enemy.bullets[i]["moving"] = false;
            enemy.bullets.splice(i, 1);
          }

          }
        }
      }
    }

  if (enemy.current_skill === 1) {
    setBullets();
  }
  moveBullets();
}




function draw(){
  var canvas = document.getElementById("canvas");
  ctx = canvas.getContext("2d");

  //hero
  //ctx.fillStyle = "blue";
  //ctx.fillRect(hero.x, hero.y ,hero.w, hero.h);

  //enemy
  //ctx.fillStyle = "red";
  //ctx.fillRect(enemy.x, enemy.y ,enemy.w, enemy.h);

  //heroの弾
  ctx.fillStyle = "blue";
  for (var i = 0; i < hero.bullets.length; i++) {
    if (hero.bullets[i].moving === false) {
      continue;
    }
    ctx.fillRect(hero.bullets[i].x, hero.bullets[i].y ,hero.bullets[i].w, hero.bullets[i].h);
  }

  //敵の弾
  ctx.fillStyle = "red";
  for (var i = 0; i < enemy.bullets.length; i++) {
     ctx.fillStyle = "green";

    if (enemy.bullets[i]["moving"] === false) {
      continue;
    }

      ctx.beginPath();
      ctx.arc(enemy.bullets[i]["x"], enemy.bullets[i]["y"], 15, 0, 2 * Math.PI, false);
      ctx.closePath();
      ctx.fill();

  }
}





window.addEventListener("keydown", function(e){
  switch(e.keyCode){
    case 37: keycode = 37; break;
    case 38: keycode = 38; break;
    case 39: keycode = 39; break;
    case 40: keycode = 40; break;
  }
}, false);

window.addEventListener("keyup", function(e){
  switch(e.keyCode){
    case 37: keycode = NaN; break;
    case 38: keycode = NaN; break;
    case 39: keycode = NaN; break;
    case 40: keycode = NaN; break;
  }
}, false);


init();

Tidak ada masalah pada awalnya, tetapi kemudian beberapa peluru akan mulai bertingkah aneh seolah-olah setelah beberapa saat.

0
Yuta 10 Maret 2017, 09:24

2 jawaban

Jawaban Terbaik

Penyebabnya adalah item yang dihilangkan saat berada di for-loop, menyebabkan satu peluru dilompati, lebih tepatnya, baris ini:

enemy.bullets.splice(i, 1);

Saya akan menyarankan pendekatan yang berbeda - buat array baru yang hanya terdiri dari peluru aktif (moving===true), lalu setelah loop ganti array dengan yang baru.

Sebagai contoh:

  function moveBullets(){
    if (count % 4 ===0) {

      // will hold active bullets in current pass
      var newBullets = [];

      for (var i = 0; i < enemy.bullets.length; i++) {
          // cut code for clarity

          if (!(enemy.bullets[i].x < -100 || enemy.bullets[i].x > canvas_w + 100 || 
                enemy.bullets[i].y < -100 || enemy.bullets[i].y > canvas_y + 100 )) {
            newBullets.push(enemy.bullets[i]);
          }
      }

      // replace array with only active bullets
      enemy.bullets = newBullets;
    }
 }

Array baru hanya akan menyimpan referensi ke poin aktif yang ada.

Biola yang Dimodifikasi

1
user1693593user1693593 10 Maret 2017, 10:09

Kumpulan partikel dan objek.

Membuat array baru bukanlah strategi yang baik ketika berhadapan dengan sistem partikel. Itulah efek peluru.

Setiap peluru yang dihapus, perlu dibersihkan oleh GC (Garbage Collection), setiap peluru yang ditambahkan perlu dibuat dan membutuhkan alokasi memori. Overhead ini dapat memiliki efek negatif pada permainan. GC dapat menyebabkan animasi hang secara acak.

Untuk animasi yang konsisten dan mulus, Anda harus menargetkan alokasi dan penghapusan nol (yang dimungkinkan karena asm.js dan Majelis web tidak mengalokasikan atau menghapus di dalam modula yang sedang berjalan).

Kolam objek

Di Vanilla JS itu mungkin tetapi kodenya terlalu rumit untuk jawaban ini. Solusi terbaik berikutnya adalah dengan menggunakan kumpulan objek.

Karena peluru pertama kali dibuat seperti biasa, tetapi ketika peluru tidak lagi diperlukan daripada dereferensi, Anda memindahkannya ke larik lain (disebut kumpulan), lain kali peluru diperlukan, Anda terlebih dahulu memeriksa apakah ada yang tersedia di kumpulan dan gunakan itu daripada membuat objek baru.

Ini memastikan bahwa GC hanya perlu membersihkan ukuran larik yang berubah dan bukan data yang digunakan oleh setiap butir.

var bullets = [];
var bulletPool = [];

function createBullet(x,y,dir,whatNot){
    var newBullet;
    if(bulletPool.length > 0){
        newBullet = bulletPool.pop();  // reuse old bullet memory
        newBullet.x = x;  
        newBullet.y = y;  
        newBullet.dir = dir;  
        newBullet.whatNot = whatNot;  
        newBullet.active = true;
    }else{
        newBullet = {x,y,dir,whatNot,active:true};  // create only if needed

    }
    return newBullet;
}
function fire(){
    bullets.push(createBullet(10,10,0,0)); /// add a bullet
}

Dalam kode Anda ketika peluru tidak lagi diperlukan, cukup atur bendera aktif ke false. Di akhir putaran permainan, hapus semua peluru yang tidak aktif.

Saat Anda menghapus peluru, pindahkan saja dari larik peluru ke kumpulan

function cleanBulletsArray(){
     for(var i = 0; i < bullets.length; i ++){
         if(!bullets[i].active){
              bulletPool.push(bullets.splice(i--,1)[0]);
         }
     }
}

Susunan curah hujan

Disebut demikian karena item aktif jatuh ke bagian bawah array

Metode yang lebih baik adalah dengan menggunakan hanya satu array. Saat peluru tidak aktif, peluru tetap berada dalam larik, tetapi saat Anda mengulangi larik, Anda menukar peluru aktif menjadi tidak aktif, dengan peluru aktif bergerak ke bawah dan tidak aktif ke atas. Ada paling banyak satu swap per iterasi. Anda juga menghitung jumlah peluru aktif. Saat Anda membutuhkan peluru baru, Anda cukup mengatur ulang properti peluru pada hitungan indeks +1 dan kemudian menambah hitungan satu per satu.

Metode ini (jika Anda mengalokasikan semua peluru di awal permainan) memiliki nol GC dan overhead Memori dan secara signifikan lebih cepat daripada metode pembuatan, penghancuran, dan penggantian array.

1
Blindman67 11 Maret 2017, 11:29