找到你要的答案

Q:Optimize looping through 2 arrays javascript canvas game

Q:优化循环2阵列的JavaScript帆布游戏

I'm working on my first javascript canvas game, and I wonder is there a better way for comparing collisons between objects in 2 arrays. For example i have an array with rockets, and array with enemies, the code is working, but i think when arrays length becomes much larger it will have effect on the performance. Example 100 rockets through 100 enemies is 10000 iterations per frame

for (i in rockets){ 
    rockets[i].x+=projectile_speed;
    for (j in enemies){
        if(collision(rockets[i], enemies[j])){ 
            enemies[j].health-=5;
            sound_hit[hit_counter-1].play();
            hit_counter--;
            if (hit_counter==0){
                hit_counter=5;
            }
            rockets.splice(i,1);
            if (enemies[j].health <= 0) {
                score += enemies[j].score;
                sound_explode[Math.floor(Math.random()*25)].play();
                enemies[j].isDead = true;
            }
        } else if(rockets[i].x >= width){
                rockets.splice(i,1);
        }    
    }
}

我在做我的第一个JavaScript帆布的游戏,我想知道有没有比较2个数组对象之间的碰撞更好的方式。例如,我有一个数组与火箭,并与敌人阵列,代码正在工作,但我认为当数组长度变得更大,它会影响性能。例子100火箭通过100个敌人是每帧10000次迭代

for (i in rockets){ 
    rockets[i].x+=projectile_speed;
    for (j in enemies){
        if(collision(rockets[i], enemies[j])){ 
            enemies[j].health-=5;
            sound_hit[hit_counter-1].play();
            hit_counter--;
            if (hit_counter==0){
                hit_counter=5;
            }
            rockets.splice(i,1);
            if (enemies[j].health <= 0) {
                score += enemies[j].score;
                sound_explode[Math.floor(Math.random()*25)].play();
                enemies[j].isDead = true;
            }
        } else if(rockets[i].x >= width){
                rockets.splice(i,1);
        }    
    }
}
answer1: 回答1:

If you want to test every rocket on every player its not really possible to do differently, without knowing more about position of players and rockets.

If you keep the collision function fast, this should though be no problem at all.

I can only think of two easy improvements on this:

  • when a collision is found use continue since looping over the rest of players should not be necessary (unless players is allowed to collide)
  • instead of splice'ing the rockets array multiple times, build a new one, excluding all "dead" rockets.

You should also consider using forEach, map and filter to make the code a bit easier to read:

rockets = rockets.filter(function(rocket) { 
    rocket.x+=projectile_speed;
    if(rocket.x >= width) {
        return false;
    }
    var enemy = enemies.find(function(enemy) { return collision(rocket, enemy) });
    if(enemy) {
        enemy.health-=5;
        sound_hit[--hit_counter].play();
        if (hit_counter==0){
            hit_counter=5;
        }
        if (enemy.health <= 0) {
            score += enemy.score;
            sound_explode[Math.floor(Math.random()*25)].play();
            enemy.isDead = true;
        }
        return false;
    }
    return true; 
});

如果你想在每个球员身上测试每一个火箭,就不可能做不同的事情,而不需要更多的了解球员和火箭的位置。

如果您保持碰撞功能快速,这应该是没有问题的。

我只能想到两个简单的改进:

  • when a collision is found use continue since looping over the rest of players should not be necessary (unless players is allowed to collide)
  • instead of splice'ing the rockets array multiple times, build a new one, excluding all "dead" rockets.

你也应该考虑使用foreach,地图和滤波器使代码更便于阅读:

rockets = rockets.filter(function(rocket) { 
    rocket.x+=projectile_speed;
    if(rocket.x >= width) {
        return false;
    }
    var enemy = enemies.find(function(enemy) { return collision(rocket, enemy) });
    if(enemy) {
        enemy.health-=5;
        sound_hit[--hit_counter].play();
        if (hit_counter==0){
            hit_counter=5;
        }
        if (enemy.health <= 0) {
            score += enemy.score;
            sound_explode[Math.floor(Math.random()*25)].play();
            enemy.isDead = true;
        }
        return false;
    }
    return true; 
});
answer2: 回答2:

What you could try to do is to reduce the number of tests by grouping the enemies and rockets, so that you only have to test the elements in the same group.

Here is a simple implementation to show what I mean, this only partitions in X-direction, because your rockets only seem to travel horizontally:

var groupWidth = 100; // do some experiments to find a suitable value
var rocketGroups = [];
var enemyGroups = [];

// initalize groups, not shown (array of array of rocket/enemy),
// but here are some other function examples...

function addToGroups(element, groups) {
  groups[element.x / groupWidth].push(element);
}

function move(element, groups, distance) {
  if (element.x / groupWidth != (element.x + distance) / groupWidth) {
    // remove element from the old group and put it in the new one
  }
  element.x += distance;
}

// Note: this is only to show the idea, see comments about length
function checkCollisions() {
  var i,j,k;
  for (i = 0; i < rocketGroups.length; i++) {
    for (j = 0; j < rocketGroups[i].length; j++) {
      for (k = 0; k < enemyGroups[i].length; k++) {
        // only compares elements in the same group
        checkPossibleCollision(rocketGroups[i], enemyGroups[i], j, k);
      }
    }
  }
}

你可以做的是通过分组敌人和火箭来减少测试次数,这样你只需要测试同一组中的元素。

这是一个简单的实现说明我的意思,这只是分区向,因为你的火箭似乎只是旅游水平:

var groupWidth = 100; // do some experiments to find a suitable value
var rocketGroups = [];
var enemyGroups = [];

// initalize groups, not shown (array of array of rocket/enemy),
// but here are some other function examples...

function addToGroups(element, groups) {
  groups[element.x / groupWidth].push(element);
}

function move(element, groups, distance) {
  if (element.x / groupWidth != (element.x + distance) / groupWidth) {
    // remove element from the old group and put it in the new one
  }
  element.x += distance;
}

// Note: this is only to show the idea, see comments about length
function checkCollisions() {
  var i,j,k;
  for (i = 0; i < rocketGroups.length; i++) {
    for (j = 0; j < rocketGroups[i].length; j++) {
      for (k = 0; k < enemyGroups[i].length; k++) {
        // only compares elements in the same group
        checkPossibleCollision(rocketGroups[i], enemyGroups[i], j, k);
      }
    }
  }
}
javascript  arrays  canvas