【问题标题】:drawImage() works outside of loop, but not inside?drawImage() 在循环外工作,但不在循环内?
【发布时间】:2021-06-24 04:16:55
【问题描述】:

我正在制作一个飞扬的小鸟游戏。问题是,图像不会在 draw() 函数内的循环中加载。如果我将 drawImage() 放在循环之外(就像其他图像一样),那么它们就可以工作了。

控制台没有错误,for循环内的两个图像没有出现,我只是不明白为什么>:/

HTML:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas" width="512" height="512">
</canvas>

这是 JS 代码,我在一个类中编写的:

const canvas = document.querySelector("#canvas");
class Game {
    constructor(c) {

        this.c = c; //canvas
        this.ctx = c.getContext("2d"); // context
        this.sprites = 5;
        this.loadedSprites = 0;
        this.score = 0;
        this.gravity = 1.5

        this.player = new Image(); // define images
        this.player.reference = this;
        this.bg = new Image();
        this.bg.reference = this;
        this.fg = new Image();
        this.fg.reference = this;
        this.north = new Image();
        this.north.reference = this;
        this.south = new Image();
        this.south.reference = this;

        //obstacle coordinates
        this.obstacle = [];
        this.obstacle[0] = {
            x: this.c.width,
            y: 0,
            // reference: this
        }
        this.obstacle.reference = this;

        this.pX = 10; // player starting location
        this.pY = 150;

        // set sprite locations and load.
        this.player.onload = this.draw;
        this.player.src = "images/crab.png";

        this.bg.onload = this.draw;
        this.bg.src = "images/bg.png";

        this.fg.onload = this.draw;
        this.fg.src = "images/fg.png";

        this.north.onload = this.draw;
        this.north.src = "images/obstacle.png";

        this.gap = 85;
        this.constant = this.north.height + this.gap;

        this.south.onload = this.draw;
        this.south.src = "images/obstacle.png";


        console.log(`AAA: ${this.obstacle[0].x}`);
        console.log(`LAUNCH is: ${this.obstacle[0]}`);



        $(document).on('keydown', (e) => { this.moveUp(e.key) });
        console.log("Game constructor:", this);
    }

    loadObstacle() {
        return this.obstacle;
    }

    saveObstacle(obstacle) {
        this.obstacle = obstacle;
    }

    draw() {
        let ref = this.reference;
        ref.loadedSprites += 1;
        if (ref.loadedSprites >= ref.sprites) {
            let constant = ref.gap + ref.north.height;
            let obstacle = ref.loadObstacle(); // this is actually not needed, since ref.obstacle odes the same thing. Just thought I'd try something else
            for (var i = 0; i < obstacle.length; i++) {
                ref.ctx.drawImage(ref.north, obstacle[i].x, obstacle[i].y);
                ref.ctx.drawImage(ref.south, obstacle[i].x, obstacle[i].y + constant);
                // ref.ctx.drawImage(ref.north, 250, 0);    even adding these two from below, they do not show up
                // ref.ctx.drawImage(ref.south, 250, 0 + constant);
                obstacle[i].x--;

                if (obstacle[i].x == 125) {
                    obstacle.push({
                        x: 512,
                        y: Math.floor(Math.random() * ref.north.height)
                    });
                }
                ref.saveObstacle(obstacle);
                console.log(obstacle[0].x); // the values are right, but they do not appear?
            }
            ref.ctx.drawImage(ref.bg, 0, 0);
            // ref.ctx.drawImage(ref.north, 250, 0); uncommenting this makes it show up
            // ref.ctx.drawImage(ref.south, 250, 0 + constant);
            ref.ctx.drawImage(ref.fg, 0, ref.c.height - ref.fg.height);
            ref.ctx.drawImage(ref.player, ref.pX, ref.pY);

            ref.pY += ref.gravity

            window.requestAnimationFrame(ref.draw.bind(this));
        }

    }

    moveUp(e) {
        if (e == "w" || e == " " || e == "ArrowUp") {
            this.pY -= 30;
            console.log(this.obstacle);
        }

    }
}
let game = new Game(canvas);

How it looks like with my code

How the working code should look like

【问题讨论】:

  • 在你的 draw 方法中,你有 let ref = this.reference; 但它没有设置任何东西。而且你的 draw 方法一开始也不会被调用。
  • @ChrisG 我之前遇到过类似的问题,并在这里得到了答案:stackoverflow.com/questions/66816536/…。 ref 是在 requestAnimationFrame 之后设置的,因为它会再次循环它(至少我是这么理解的) Draw 方法在 onload 事件中至少被调用了五次。除了循环 drawImage() 之外,一切似乎都有效,我不明白为什么 >:/
  • 使用console.log() 调试一切。函数是否被调用,vars 实际包含什么等等。应该很容易找到问题
  • @ChrisG 我以前做过,但想不通。如果我将 imageDraw() 移到循环内部,它们也不会工作,但在外部它们会起作用,这很奇怪,因为循环中的其他所有内容(例如 console.log())都可以工作。有没有办法跟踪循环内的两个 drawImage() 的去向?在帖子中添加图片
  • 看起来 onload 绘制调用没有绑定 this 是问题,尝试将 this.draw = this.draw.bind(this); 添加到构造函数

标签: javascript html canvas html5-canvas


【解决方案1】:

首先也是最重要的:你在这里混合了两种完全不同的东西。

this.player.onload = this.draw;

window.requestAnimationFrame(ref.draw.bind(this));

两者都执行相同的功能。从技术上讲,这当然是可能的,但它让事情变得有点混乱。我建议为 onload 事件设置一个附加函数,以便在所有图像加载完成后立即触发 requestAnimationFrame。

this.player = new Image(); 
this.player.reference = this;
this.player.onload = this.loaded;

  loaded() {
    let ref = this.reference;
    ref.loadedSprites += 1;
    if (ref.loadedSprites >= ref.sprites) {
      // continue
    }
  }

其次,需要将 this 关键字的正确“上下文”传递给 .bind() 函数。它应该始终是您的 Game 类的对象。所以将上面的// continue替换为:

window.requestAnimationFrame(ref.draw.bind(ref));

现在draw() 函数需要一些修改。由于我们为 this 关键字提供了正确的上下文,我们不再需要使用引用 - 我们现在可以简单地使用 this

这是完整的例子:

const canvas = document.querySelector("#canvas");
class Game {
  constructor(c) {

    this.c = c; //canvas
    this.ctx = c.getContext("2d"); // context
    this.sprites = 5;
    this.loadedSprites = 0;
    this.score = 0;
    this.gravity = 1.5

    this.player = new Image();
    this.player.reference = this;
    this.bg = new Image();
    this.bg.reference = this;
    this.fg = new Image();
    this.fg.reference = this;
    this.north = new Image();
    this.north.reference = this;
    this.south = new Image();
    this.south.reference = this;

    //obstacle coordinates
    this.obstacle = [];
    this.obstacle[0] = {
      x: this.c.width,
      y: 0,
      // reference: this
    }
    this.obstacle.reference = this;

    this.pX = 10; // player starting location
    this.pY = 150;

    // set sprite locations and load.
    this.player.onload = this.loaded;
    this.player.src = "https://picsum.photos/id/237/20/20";

    this.bg.onload = this.loaded;
    this.bg.src = "https://picsum.photos/id/22/20/20";

    this.fg.onload = this.loaded;
    this.fg.src = "https://picsum.photos/id/30/20/20";

    this.north.onload = this.loaded;
    this.north.src = "https://picsum.photos/id/37/20/20";

    this.gap = 85;
    this.constant = this.north.height + this.gap;

    this.south.onload = this.loaded;
    this.south.src = "https://picsum.photos/id/137/20/20";


    console.log(`AAA: ${this.obstacle[0].x}`);
    console.log(`LAUNCH is: ${this.obstacle[0]}`);



    $(document).on('keydown', (e) => {
      this.moveUp(e.key)
    });
    console.log("Game constructor:", this);
  }

  loadObstacle() {
    return this.obstacle;
  }

  saveObstacle(obstacle) {
    this.obstacle = obstacle;
  }

  loaded() {
    let ref = this.reference;
    ref.loadedSprites += 1;
    if (ref.loadedSprites >= ref.sprites) {
      window.requestAnimationFrame(ref.draw.bind(ref));
    }
  }

  draw() {

    let constant = this.gap + this.north.height;
    let obstacle = this.loadObstacle();
    for (var i = 0; i < obstacle.length; i++) {
      this.ctx.drawImage(this.north, obstacle[i].x, obstacle[i].y);
      this.ctx.drawImage(this.south, obstacle[i].x, obstacle[i].y + constant);

      obstacle[i].x--;

      if (obstacle[i].x == 125) {
        obstacle.push({
          x: 512,
          y: Math.floor(Math.random() * this.north.height)
        });
      }
      this.saveObstacle(obstacle);

    }
    this.ctx.drawImage(this.bg, 0, 0);

    this.ctx.drawImage(this.fg, 0, this.c.height - this.fg.height);
    this.ctx.drawImage(this.player, this.pX, this.pY);

    this.pY += this.gravity

    window.requestAnimationFrame(this.draw.bind(this));


  }

  moveUp(e) {
    if (e == "w" || e == " " || e == "ArrowUp") {
      this.pY -= 30;
      console.log(this.obstacle);
    }

  }
}
let game = new Game(canvas);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas" width="512" height="512">
</canvas>

【讨论】:

  • 背景图片bg后面好像有障碍物。有什么办法把它移到后面?
  • 只需要在循环之前移动另一个 imagedraw() :) 你太棒了!
  • 呵呵,谢谢 Dr1nKy - 很高兴我能帮上忙! =)
猜你喜欢
  • 2013-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多