【问题标题】:Splice (index,1) is removing more than one element from arraySplice (index,1) 正在从数组中删除多个元素
【发布时间】:2021-05-04 05:19:59
【问题描述】:

我正在用 HTML Canvas 制作一个简单的游戏。作为其中的一部分,我想在背景中制作流星,创造旅行的幻觉。在星星到达画布的末端后,我想将其移除。每颗恒星都是 Star 类的一个实例,并且根据它的半径,它具有不同的速度。这就是问题开始的地方。当对每颗恒星使用恒定速度时,它们会像应有的那样一个接一个地消失。当速度改变时,“超越”较慢恒星的恒星,当到达画布的尽头时,不仅会消失,还会移除在它们之前排列的每颗恒星。 我尝试了以下描述的许多解决方案:

let canvas = document.querySelector("canvas");
let c = canvas.getContext('2d');

星级声明:

class Star{
    constructor(x, y, radius, color, velocity){
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.color = color;
        this.velocity = velocity
    }

    draw(){
        c.globalCompositeOperation='destination-over'
        c.beginPath()
        c.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
        c.fillStyle = this.color;
        c.shadowColor= "white"
         c.shadowBlur=12
        c.fill();
        c.shadowBlur=0
    }
   
    update(){
        this.draw();
        this.y = this.y + this.radius/2;
    
    }
}

创建星星并将其添加到数组中

let stars = [];
function createStar(){
    setInterval(()=>{
    //Create random radius and X position
    let randomRadius = Math.floor((Math.random()*5))
    let randomXPosition = Math.floor((Math.random()*780)+15)
    let velocity = 1;
    stars.push(new Star(randomXPosition, -randomRadius, randomRadius, "white",velocity));
    console.log("stars:"+ stars.length);
    },300)
}

下面我使用一个调用自身的函数来清除和刷新星图。我尝试使用 forEach 方法循环遍历 stars 数组,反向循环(如下例所示),我尝试将带有拼接函数的 if 语句放在单独的 setTimeout(()=>{},0) 或 setTimeout(()=>{ },10)。我尝试使用类似的条件

(forEach 方法会移除星星,但活跃星星的数量不会保持不变。它会不断缓慢增加)

function animate(){
  c.clearRect(0, 0, canvas.width, canvas.height);

   for(let i = stars.length-1; i>=0; i--){
        stars[i].update()
        if (stars[i].y > canvas.height +stars[i].radius ){
            stars.splice(stars[i], 1)
       }
    } 
requestAnimationFrame(animate);
}

animate();
createStar();

我尝试使用以下条件:

if (stars[i].y > 5000 ){
    stars.splice(stars[i], 1)
}

但这不是应该如何解决的,因为星星的寿命要长 5000 像素,这让游戏变得迟钝。

只是为了明确问题。 如果我每 300 毫秒生成 5 颗星并将其推入名为“星”的数组中,我会得到例如 [星1,星2,星3,星4,星5]

假设star1 和star 2 的半径很小,它们移动缓慢。 Star3 更大,因此它移动得更快,超过了前两个。当star3 到达canvas.height + star[i].radius 时,它会在画布上方消失,但它也会使数组中star3 之前的每颗星都消失(在本例中是star1 和star2)。

这是我在 stackoverflow 上的第一篇文章。对于所有的轻描淡写,我提前道歉。

HTML 画布


<body>
    <div class="container">
        <button class="logOutButton">Log Out</button>
        <button class="topScores">Top 10</button>
        <header>
            <p class="hello">Hello <span id="spanName"></span></p>
            <p class="topScore">Your TOP score: <span id="score"></span></p>
        </header>

        <div class="gameTitle">
            <h2 class="title">Space Warrior</h2>
        </div>
        
        <canvas class="canvas" width="800" height="500"></canvas>
       
    </div>
    <script src="User.js"></script>
</body>

编辑 我将 stars.splice(stars[i], 1) 更改为 stars.splice(i, 1) - 不工作

我尝试添加另一个像下面这样的删除数组,但数组 stars 只是慢慢变大(即使有些元素被删除了)

 var removeStar = [];
    // stars.forEach((star,starIndex)=>{
    let total = stars.length-1;
    
    for(let i = total; i>=0; i--){
        stars[i].update();

        if (stars[i].y > canvas.height + stars[i].radius){
            removeStar.push(i);
        }
    };

    for(let i = removeStar.length; i>0; i--){
        stars.splice(removeStar[i-1],1)
    }

【问题讨论】:

  • 你能分享你的画布html吗?
  • c 未定义
  • 我的错,忘记添加到问题中
  • 添加变量let total = stars.length - 1,而不是在for循环中使用动态长度。
  • 像上面一样改了,但是不行

标签: javascript html arrays animation canvas


【解决方案1】:

你写道: stars.splice(stars[i], 1) 第一个参数应该是您要删除的索引...只是索引stars.splice(i, 1) ..另一个问题是您在循环中更改数组,这是个坏主意。

看到这个非常相似的问题的答案: https://stackoverflow.com/a/65725703/3054380

在修复了上面提到的错误并添加了minVelocity 和星形限制条件之后(因为你添加了间隔并在星形离开画布时移除 - 我们需要限制以防间隔变快)......现在一切看起来都很好- 在下面工作 sn-p(添加 maxStars minVelocity

let canvas = document.querySelector(".mycanvas");
let c = canvas.getContext('2d');
canvas.width = 300;
canvas.height = 150;
let maxStars = 60;
let minVelocity = 0.5;
let stars = [];

class Star {
  constructor(x, y, radius, color, velocity) {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.color = color;
    this.velocity = velocity
  }
  draw() {
    c.globalCompositeOperation = 'destination-over'
    c.beginPath()
    c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
    c.fillStyle = this.color;
    c.shadowColor = "white"
    c.shadowBlur = 12
    c.fill();
    c.shadowBlur = 0
  }
  update() {
    this.draw();
    this.y = this.y + Math.max(minVelocity, this.velocity * this.radius / 2);

  }
}

function createStar() {
  if (stars.length < maxStars) {
    //Create random radius and X position
    let randomRadius = Math.floor((Math.random() * 5));
    let randomXPosition = Math.floor((Math.random() * (canvas.width - 20)) + 10);
    let velocity = 1;
    stars.push(new Star(randomXPosition, -randomRadius, randomRadius, "white", velocity));
    console.log("stars:" + stars.length);
  }
}

function animate() {
  c.clearRect(0, 0, canvas.width, canvas.height);
  for (let i = stars.length - 1; i >= 0; i--) {
    stars[i].update()
    if (stars[i].y > canvas.height) {
      stars.splice(i, 1)
    }
  }
  requestAnimationFrame(animate);
}

setInterval(createStar, 50);
animate();
.mycanvas {
  width: 300px;
  height: 150px;
  border: 1px solid #f00;
}
<body style="background-color:#000;color:#fff;">
  <div class="container">

    <canvas class="mycanvas"></canvas>

  </div>
  <script src="User.js"></script>
</body>

【讨论】:

  • 感谢您的帮助,但它仍然无法正常工作。我将stars.splice(stars[i], 1) 更改为stars.splice(i, 1) 但没有帮助,所以我添加了另一个类似问题的数组。星星慢慢地彼此相加。拼接删除了一些,但它们的数量仍在增加
  • @Tomasz1996 好的......让我们让它工作......现在有两个问题 - 删除元素循环方向错误......或者如果你的主循环向后移动可能根本不需要......并且您添加带有间隔的星星 - 这可能比删除更快。在实时示例上调试会更容易 - 让我们尝试让它运行
  • @Tomasz1996 希望你能在我的工作示例中找到你需要的东西(在我的答案中编辑)
  • maxStars 想法是我将使用的东西,非常感谢您抽出宝贵时间!
【解决方案2】:

我想我发现了导致您的数组不断扩展的问题。它与您的 splice 函数无关,正如他的回答中提到的 webdev-dan 那样,该函数的实现不正确。

遵循这个逻辑

  • 半径已设置为Math.floor((Math.random()*5))。这意味着半径可以为0。
  • 在您的更新方法中,您将基于this.y = this.y + this.radius/2 中的radius 增加y。所以这可能是用 0 改变 y。
  • if (stars[i].y &gt; 5000 ) 中,您正在删除超过 5000 的 y 值。

所以如果半径是0y 不变,星星永远不会被移除。你也看不到它们。

解决方案

您可以保证最低速度为 1.this.y += Math.max(1, this.radius/2)

PS:我不得不做很多重构来解决这个问题。您正在以过于复杂的方式编写代码。几种逻辑混在一起。

你真的想把你的渲染逻辑从星星对象的管理中分离出来。

记住星星数组也很困难,因为你可以从任何地方修改它;在 for 循环中,带有异步代码(间隔)。

这就是我在尝试清理您的代码时最终得到的结果:https://jsfiddle.net/5hk0vscg/1/ 希望它有用。请注意,这不是完全清理,而是对您当前代码的改进。

【讨论】:

  • 我改变了半径,你是对的——有时它是 0。但这并没有解决计数问题。我使用了上面答案中的 maxStars 方法,但是您添加了 Stars 类的代码审查令人印象深刻。谢谢你帮助我,我真的很感激!
  • @Tomasz1996 没问题。我希望maxStars 可以在画布变得太大时创建一条没有星星的腰带。您可能需要根据画布大小考虑更长的时间间隔。
  • 如果计数问题是splice 的问题,则在阵列上使用filter 可能会为您解决问题。
猜你喜欢
  • 1970-01-01
  • 2018-03-12
  • 2021-09-23
  • 2014-07-30
  • 1970-01-01
  • 2021-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多