Canvas Context.drawImage() 在 UpdatePosition() 上留下重复的 Sprite

Canvas Context.drawImage() is leaving behind duplicates of Sprite on UpdatePosition()

我一直在用 HTML 的 Canvas 元素试验一个基本的游戏循环。许多在线教程没有详细介绍渲染和 canvas.ctx(上下文)的概念。

我想做的事情非常简单:在 canvas 元素上渲染图像,然后在按下键时更新它的位置并在新位置渲染它,使它在屏幕上移动。 基本上,每个视频游戏都用它的精灵做什么。

我通过这些教程得知 ctx.drawImage(image, x, y, ...) 将适用于此。然而,在我的版本中最终发生的事情本质上是当你在 windows 上赢得单人纸牌游戏时发生的事情。 它会重复精灵的图像,就好像它在每次游戏循环时都在创建一个全新的精灵。精灵本身不会移动,一个新的精灵似乎会生成到 left/right/etc 原来的。我知道我每次在游戏循环中迭代时都会调用 ctx.drawImage(...)。但是,当我使用 ctx.clearRect(...) 时,这并没有发生。它完全按照我的预期工作。我不太确定为什么用 ctx 创建矩形有效,而创建图像却无效。

我的问题是:有没有一种方法可以简单地更新精灵的位置,而无需在每个循环中都创建一个全新的版本?

这是我的相关代码:

let lastRender = 0; // For the general loop

let image = new Image();
image.src = "/img/image.png";

let state = {
    pressedKeys: {
        // left, right, up, down: false
    },

    position: {
        x: canvas.width / 2,
        y: canvas.width / 2
    },

    speed: 20
}

let pepsi = new Sprite({
    img: image,
    width: 100,
    height: 100
)};


function Sprite (options) {
    this.img = options.img;
    this.width = options.width;
    this.height = options.height;

    this.render = function(){
        ctx.drawImage(
        this.img,
        state.position.x,
        state.position.y
        )
    }
}

function updatePosition(progress) {
        //pressedKeys is just an object that relates WASD to the key codes 
        //     and their respective directions, it's ignorable

        if (state.pressedKeys.left) {
            state.position.x -= state.speed;
        }

        if (state.pressedKeys.right) {
            state.position.x += state.speed;
        }

        if (state.pressedKeys.up) {
            state.position.y -= state.speed;
        }

        if (state.pressedKeys.down) {
            state.position.y += state.speed;
        }
}


function draw() {
    pepsi.render();
}

function loop(timestamp) {
    let progress = timestamp - lastRender;

    update(progress) // <-- Updates position, doesn't touch draw()

    draw(); // <-- Runs pepsi.render(); each loop

    lastRender = timestamp;             
    window.requestAnimationFrame(loop); 
}

window.requestAnimationFrame(loop); // for the general loop 

如果您对这个项目的设置方式有任何疑虑(例如,对 each Sprite 使用 state.position),那么我会很高兴除了解决我的问题之外,还要听听他们的意见。不是孤立的。我从无上下文、非特定的在线教程中获得了大部分代码,但我理解了大部分代码,除了渲染。

此外,如果您以前见过此类问题并且对说 "Possible duplicate of {Borderline Tangentially-Related Post from Four Years Ago}" 持观望态度,那么这里有一些建议:请再次回答问题.它确实对你没有任何负面影响。

您获得的纸牌涂抹效果来自于每一帧都被绘制在最后一帧的顶部这一事实。 canvas 不会在帧之间自动清除。

你提到你使用过clearRect,clearRect的作用是清除指定矩形内的所有像素。

因此,如果您在 pepsi.render() 之前将 ctx.clearRect(0, 0, canvas.width, canvas.height) 放入绘制函数中,那应该会在绘制下一帧之前清除 canvas。