【问题标题】:Adding and removing to an array添加和删​​除数组
【发布时间】:2019-04-09 00:54:13
【问题描述】:

我不知道该怎么说,因为我实际上并不知道究竟是什么导致了这个错误。我正在尝试制作一个简单的小行星仿冒品。

当玩家射击时,使用array.push(...) 创建一个新对象(子弹)。一旦此项目符号超出画布(超出范围),就会使用 array.splice(...); 删除它

问题在于子弹以不可预测的方式移动。我不知道如何措辞所以这里是完整的代码(工作,包括 html/css):https://pastebin.com/tKiSnDzX 按住空格键几秒钟(拍摄),您会清楚地看到问题。您也可以使用 A/D 转弯和 W 前进。

这就是我认为正在发生的事情。只要屏幕上(数组中)只有一个项目符号,代码就可以正常运行。这意味着要么不正确的元素被删除,要么进入对象构造函数的值在途中的某个地方搞砸了。

Exhibit A(bullet 构造函数及其方法):

function Bullet(x,y,rot,vel) {
    this.x = x;
    this.y = y;
    this.rot = rot;
    this.vel = (vel+5);

    this.move = function() {
        this.x += this.vel*Math.cos(this.rot-Math.PI/2);
        this.y += this.vel*Math.sin(this.rot-Math.PI/2);
    }

    this.draw = function() {
        engine.circle(this.x, this.y, 4, "black");

        var c = engine.canvas.getContext('2d');
        c.translate(this.x, this.y);
        c.rotate(this.rot);
        c.beginPath();
        c.strokeStyle="#00FF00";
        c.strokeRect(-5, -5, 10, 10);
        c.closePath();
        c.stroke();
    }
}

Exhibit B(创建/删除项目符号的函数):

shoot: function() {
            if(engine.keyDown.sp == true) {
                if(this.fire > 20) {
                    engine.bullets.unshift(new Bullet(this.x, this.y, this.rot, this.velocity));
                    this.fire = 0;
                } else {
                    this.fire++
                }
            }
            for(i = 0; i < engine.bullets.length; i++) {
                engine.bullets[i].move();
                engine.bullets[i].draw();
                if(engine.bullets[i].x > engine.canvas.width+5 || engine.bullets[i].x < -5 
                || engine.bullets[i].y > engine.canvas.height+5  || engine.bullets[i].y < -5) {
                    console.log('bullet gone, '+i);
                    engine.bullets.splice(i, 1);
                }
            }
        }

数组声明如下:bullets: []

感谢您的任何回答。

【问题讨论】:

  • for 循环中,每次缩短数组时,都需要注意数组的长度。因此,您可能会考虑将 bullets.length 分配给一个变量,在循环条件中使用该变量,然后每次从数组中删除项目符号时递减该变量。
  • 感谢您的回答,但事实证明,这不是问题所在。我尝试将context.save();context.restore(); 放在子弹发生平移/旋转的位置。现在没有问题
  • @JanProcházka 您应该将其作为答案并将其标记为正确 - 特别是如果您知道为什么这是正确答案并且可以解释它!
  • 永远不要在转发for 循环中使用splice。当i-th 元素被splice 移除时,所有从i+1 到最后一个元素都向左移动并分别改变它们的索引。这就是为什么你会得到不可预测的行为。

标签: javascript arrays push splice


【解决方案1】:

当你在循环中遇到需要死亡的子弹时,只需用engine.bullets[i].dead = true; 之类的东西标记它们怎么样?然后,在循环外的最后,用engine.bullets = engine.bullets.filter(b =&gt; !b.dead); 过滤掉死子弹

【讨论】:

    【解决方案2】:

    您在 for 循环中使用拼接。当您删除元素索引 1 时,元素索引 2 变为索引 1,元素索引 3 变为索引 2。但是您的 i 变量已经为 1,因此循环的下一次迭代转到新索引2.但是新的索引2是原来的索引3,跳过了原来的索引2。链表可能会更好地为您服务:

    var bulletList = null;
    function bullet(){
      this.next = bulletList;//if there was a list, record that
      if (this.next)//and set that one to point back to this
       this.next.prev = this;
      bulletList = this;//place new bullet at start of list
      this.previous = null;
    
      this.draw = function(){
        //do stuff here
        this.next && this.next.draw();//call next item in the list, if any
        if (shouldIBeRemoved){//placeholder, obviously
          if (this.next && this.prev){//remove from middle of list
            this.prev.next = this.next;
            this.next.prev = this.prev;
          }
          else if (this.next && !this.prev){//remove from beginning of list
            bulletList = this.next;
            this.next.prev = null;
          }
          else if (this.prev && !this.next){//remove from end of list
            this.prev.next = null;
          }
          else if (!this.prev && !this.next){//remove only item in list
            bulletList = null;
          }
        }
      }
    }
    

    然后要绘制每个子弹,只需调用:

    if (bulletList)
      bulletList.draw();
    

    【讨论】:

    • 感谢您的建议,但我实际上不需要走那么远。我只是在bullets.splice(i, 1); 之后执行此操作i-=1。这设置了 for 循环 1 的索引并解决了一个小错误(它在没有它的情况下工作)
    【解决方案3】:

    问题在于,每次创建新项目符号时,我都在翻译所有项目符号所在的上下文。这意味着每个子弹都会从前一个子弹移动 x 和 y。这使得子弹似乎是在不应该出现的地方制造的。 “不可预测性”是由于子弹承担了玩家的旋转,因此每当创建新子弹时,它的旋转增加了玩家在新子弹发射之前旋转了多少,在前一个子弹的旋转之上.

    context.save();放在子弹的hitbox的平移/旋转之前和context.restore();完美解决问题之后:

    Bullet.draw = function() {
        engine.circle(this.x, this.y, 4, "black");
        if(hitbox == true) {
            var c = engine.canvas.getContext('2d');
            c.save();
            c.translate(this.x, this.y);
            //c.rotate(this.rot);
            c.beginPath();
            c.strokeStyle="#00FF00";
            c.strokeRect(-5, -5, 10, 10);
            c.closePath();
            c.stroke();
            c.restore();
        }
    }
    

    有人提到我在 for 循环中使用了 array.splice();。这使得当一个项目符号 (i) 被删除时,紧接在 (i+1) 之后的项目符号将向后移动一个索引 (i)。所以那个子弹基本上被跳过了。

    当一个被删除的子弹时,我有时会注意到这一点——它们“跳”在前面。

    解决方案是将i -= 1 放在bullets.splice(i, 1); 之后。这使得循环的下一次迭代返回一个索引,解决了子弹偶尔卡顿的问题:

    shoot: function() {
            if(engine.keyDown.sp == true) {
                if(this.fire > 5) {
                    engine.bullets.unshift(new Bullet(this.x, this.y, this.rot, this.velocity));
                    this.fire = 0;
                }
            }
            if(this.fire<=5) {
                this.fire++
            }
            for(i = 0; i < engine.bullets.length; i++) {
                engine.bullets[i].move();
                engine.bullets[i].draw();
                if(engine.bullets[i].x > engine.canvas.width+5 || engine.bullets[i].x < -5 
                || engine.bullets[i].y > engine.canvas.height+5  || engine.bullets[i].y < -5) {
                    engine.bullets.splice(i, 1);
                    i-=1;
                }
            }
        }
    

    感谢大家的回答。

    【讨论】:

      猜你喜欢
      • 2014-12-23
      • 1970-01-01
      • 2021-03-14
      • 2022-01-23
      • 2017-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-07
      相关资源
      最近更新 更多