【问题标题】:canvas drawImage loop issue画布drawImage循环问题
【发布时间】:2017-01-05 17:29:08
【问题描述】:

我正在尝试通过将 fillRect 替换为图像来修改 this 画布游戏。

spaceinvaders.js 第 529 行更改船舶代码时没有问题:

ship = new Image();
ship.src = "images/ship.svg";

ctx.drawImage(ship, this.ship.x - (this.ship.width / 2), this.ship.y - (this.ship.height / 2), this.ship.width, this.ship.height);

但是入侵者循环有问题:

for(var i=0; i<this.invaders.length; i++) {

    var invader = this.invaders[i] = new Image();
    this.invaders[i].src = "images/space_invader.png";

    //ctx.fillRect(invader.x - invader.width/2, invader.y - invader.height/2, invader.width, invader.height);

    this.invaders[i].onload = function(){

        var thisX = invader.x * 18;
        var thisY = invader.y * 14;

        return function() {
            ctx.drawImage(invader,invader.x - invader.width/2, invader.y - invader.height/2, invader.width, invader.height);
            //ctx.drawImage(this, thisX, thisY, invader.width, invader.height);
        };

    };
}

当我最终显示入侵者图像时,它会将它们全部堆叠在一起。 - 即使使用 thisX 和 thisY 变量。

编辑:

img = new Image();

img.onload = function(){

    for(var i=0; i<this.invaders.length; i++) {
        for(var j=0; j<this.invaders.length; j++) {
            var invader = this.invaders[i];
            ctx.drawImage(img,j*18, i*14, invader.width, invader.height);
        }
    }
}

img.src = "images/space_invader.png";

给出:无法读取未定义的属性“长度”。

编辑 2:

img = new Image();

img.onload = function(){

    for(var i=0; i<5; i++) {
        for(var j=0; j<10; j++) {
            ctx.drawImage(img,j*22, i*16, 18, 14);
        }
    }
}

img.src = "images/space_invader.png";

这样我至少可以看到入侵者,但它们是静态的:

编辑 3:

var invaders=[{x:0,y:0},{x:20,y:0},{x:40,y:0},{x:60,y:0},{x:80,y:0},{x:100,y:0},{x:120,y:0},{x:140,y:0},{x:160,y:0},{x:180,y:0},{x:0,y:16},{x:20,y:16},{x:40,y:16},{x:60,y:16},{x:80,y:16},{x:100,y:16},{x:120,y:16},{x:140,y:16},{x:160,y:16},{x:180,y:16},{x:200,y:16},{x:0,y:32},{x:20,y:32},{x:40,y:32},{x:60,y:32},{x:80,y:32},{x:100,y:32},{x:120,y:32},{x:140,y:32},{x:160,y:32},{x:180,y:32},{x:200,y:32},{x:0,y:48},{x:20,y:48},{x:40,y:48},{x:60,y:48},{x:80,y:48},{x:100,y:48},{x:120,y:48},{x:140,y:48},{x:160,y:48},{x:180,y:48},{x:200,y:48},{x:0,y:64},{x:20,y:64},{x:40,y:64},{x:60,y:64},{x:80,y:64},{x:100,y:64},{x:120,y:64},{x:140,y:64},{x:160,y:64},{x:180,y:64},{x:200,y:64}];

for (var i=0; i<this.invaders.length; i++) {

    invaders[i] = new Image();

    invaders[i].onload = function() {
        ctx.drawImage(this, x, y);         
    };

    invaders[i].src = "images/space_invader.png";
}

给出 x 未定义。

编辑 4:

function createImage(i){   

var image = new Image();  
    image.src = "images/space_invader.png";
    image.onload = function(){  
        ctx.drawImage(image,imagePos[i][0],imagePos[i][1], 20, 16);
    } 
}

var imagePos=[[0,0],[20,0],[40,0],[60,0],[80,0],[100,0],[120,0],[140,0],[160,0],[180,0],[0,16],[20,16],[40,16],[60,16],[80,16],[100,16],[120,16],[140,16],[160,16],[180,16],[0,32],[20,32],[40,32],[60,32],[80,32],[100,32],[120,32],[140,32],[160,32],[180,32],[0,48],[20,48],[40,48],[60,48],[80,48],[100,48],[120,48],[140,48],[160,48],[180,48],[0,64],[20,64],[40,64],[60,64],[80,64],[100,64],[120,64],[140,64],[160,64],[180,64]];

for(var i = 0; i <this.invaders.length; i += 1){
    createImage(i); 
}
return

显示入侵者,但他们一直在闪烁。 (主游戏循环?)

【问题讨论】:

  • 你用你的 Image 实例覆盖了入侵者。使用var invader = new Image() 之类的局部变量,然后使用invaders[i].x 获取坐标。

标签: javascript canvas


【解决方案1】:

一个变量只能保存一个值。

有一个简单的规则可以帮助您避免此类问题。永远不要在循环中声明函数。

为什么?

让我们看一个循环

var imagePos = [[10,10],[10,20],[10,30],[10,40],[10,50],[20,10],[20,20],[20,30],[20,40],[20,50]];
for(var i = 0; i < 10; i += 1){
    var image = new Image();
    image.src = "imagename";
    image.onload = function(){
         ctx.drawImage(image,imagePos[i][0],imagePos[i][1]);
    } 
}
// end
return

您预计开始加载 10 张图片,当每张图片加载完毕后,您预计它们将被绘制在正确的位置。但这永远不会发生。

在 for 循环完成并返回执行之前,onload 函数不会触发。当第一个 onload 事件触发 i = 10 时,您有 2 个变量,iimage,并且每次加载都会保持这种状态。此外,您在 onload 函数中引用的图像也只有一个变量,不能容纳多个图像。即使每个onload 都正确绑定到图像,image 变量将指向循环中创建的最后一个图像。

你有点期待iimage 分别持有 10 个不同的值,并且知道什么时候需要什么值。

正确的做法如下

function createImage(i){   // load an image function 
                           // creates a new i each time it is called
    var image = new Image();  // create a new image variable 
    image.src = "imagename";
    image.onload = function(){  // now the onload will have the correct variables 
         ctx.drawImage(image,imagePos[i][0],imagePos[i][1]);
    } 
 }
var imagePos = [[10,10],[10,20],[10,30],[10,40],[10,50],[20,10],[20,20],[20,30],[20,40],[20,50]];
for(var i = 0; i < 10; i += 1){
    createImage(i); // call the function so it can create unique variables for each image
}
// end
return

您也可以使用循环内的函数完成此操作,但您需要使用函数绑定对象来存储您想要的值。

var imagePos = [[10,10],[10,20],[10,30],[10,40],[10,50],[20,10],[20,20],[20,30],[20,40],[20,50]];
for(var i = 0; i < 10; i += 1){
    var image = new Image();
    image.src = "imagename";
    image.posIndex = i; // attach the index to the image
    image.onload = function(){ // instead of using image use the image bound to the function 
                                // and referenced via the this token
         ctx.drawImage(this, imagePos[this.posIndex][0], imagePos[this.posIndex][1]);
    } 
    
}
// end
return

这样做并没有错,除了你发现的陷阱。这就是为什么一般规则是,如果您不熟悉 javascript,请不要在循环中声明函数。

【讨论】:

  • 感谢您提供的信息。但事实证明,这种特殊的画布游戏太让人头疼了。闪烁的图像,FF中没有出现的图像等我觉得从头开始更容易。 -- 用图片替换fillRects,他们说的很简单! :)
【解决方案2】:

您可能不应该使用 Image 对象的 x 和 y 属性。首先它是只读的,其次它可能没有设置并且始终为 0,因为您正在创建一个新的 Image 实例。

https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Imagehttps://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement

【讨论】:

  • 好的,有图片对象循环的例子吗?
  • 你能告诉我包括this.invaders的设置的代码吗?
  • 我添加的只是第 533 行 // 绘制入侵者下的 for 循环。 spaceinvaders.js 中的其他内容基本相同。
【解决方案3】:

太棒了!现在你所需要的只是某种游戏循环来让它们移动。我会将 x 和 y 位置(从您当前的 for 循环中)移动到单独的对象中,这样您就可以在每个(更新)循环中修改每个入侵者。

var invaders = [ {x: 100, y: 100, speed: 10}, {x: 140, y: 100, speed: 10}, {x: 180, y: 100, speed: 10}, // and so on ];

尝试在此处阅读有关游戏循环及其工作原理的更多信息:https://www.isaacsukin.com/news/2015/01/detailed-explanation-javascript-game-loops-and-timing

编辑:像这样的东西......

for(var i = 0; i < invaders.length; i++) {

    var invader = new Image();

    invader.onload = function() {
        ctx.drawImage(this, invaders[i].x, invaders[i].y);         
    };

    invaders[i].src = "images/space_invader.png";
}

【讨论】:

  • 没有错误但没有显示船只。我仍然可以看到他们开火。
  • 也许您需要再次添加高度和宽度?无法测试 sn-p tbh。
  • 试过了,没有效果。
  • 还有什么想法吗?
  • 正如您在其他评论中所写的那样,我将使用画布游戏教程或现有框架重新开始,这会让您的生活更轻松!祝你好运:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-16
  • 2012-11-30
  • 2015-03-02
  • 2012-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多