Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using HTML5 Canvas to parse an image into a tileset

I'm attempting to parse a tileset image (of type .png) into an array. I've got a test canvas that I'm using to draw it to first, then I am extracting the image info from it. When I run this code, it throws "Uncaught TypeError: Type error." I was able to log that this.mainTiles is empty. Any ideas? I don't know all the subtleties of Canvas yet, so I'm stuck. Thanks for the help! (Also, you can ignore the last line at the end, I was just using it to test--but I wanted to illustrate that it doesn't work).

function TileSet() {
    this.tileSheets = [];
    this.mainTiles = [];
    this.tileHeight = 32;
    this.tileWidth = 32;




    this.addSpriteSheet = function (spriteSheetLoc, name) {

        var tileSheet = new Image();
        try {
            tileSheet.src = spriteSheetLoc;
        }
        catch(err) {
            dMode.Log("Invalid TileSheet Src ( TileSet.setSpriteSheet() Failed ); ERR: " +  err);
        }


        tempContext.drawImage(tileSheet, 0, 0);


        var tilesX = tempContext.width / this.tileWidth;
        var tilesY = tempContext.height / this.tileHeight;

        for(var i=0; i<tilesY; i++) {
            for(var j=0; j<tilesX; j++) {
                // Store the image data of each tile in the array.
                this.mainTiles.push(tempContext.getImageData(j*this.tileWidth, i*this.tileHeight, this.tileWidth, this.tileHeight));
            }
        }


        context.putImageData(this.mainTiles[0], 5, 5);



    }

Edit: Here are how the canvases and such are defined:

var tempCanvas = document.getElementById("tempCanvas");
var tempContext = tempCanvas.getContext("2d");

var canvas = document.getElementById("gameCanvas");
var context = canvas.getContext("2d");


var Tiles = new TileSet;
//this is the line it gets stuck at


Tiles.addSpriteSheet("resources/tiles/tilea2.png");

Edit 2: After markE's answer, here's the latest update. Still feel as though I'm missing a fundamental property regarding .onload.

function TileSet() {
    this.Tiles = [];
    this.tileHeight = 32;
    this.tileWidth = 32;
    this.tileCount = 4;




    this.addSpriteSheet = function (spriteSheetLoc, name) {

        var tileSheet = new Image();
        tileSheet.onload = function() {
            tempCanvas.width = tileSheet.width;
            tempCanvas.height = tileSheet.height;
            tempContext.drawImage(tileSheet, 0, 0);



           for (var t=0;t<this.tileCount;t++) {
       this.Tiles[t]=tempContext.getImageData(t*this.tileWidth,t*32,this.tileWidth,tileSheet.height);
               dMode.Log(this.Tiles);
           }


            context.putImageData(this.Tiles[this.Tiles.length-1],0,0);
            dMode.Log(this.Tiles);
        }

        tileSheet.src = spriteSheetLoc;
}
like image 467
Taylor Hill Avatar asked Dec 04 '25 15:12

Taylor Hill


2 Answers

Here's how to parse a spritesheet into an array of separate canvas imageData

I have some working code that does what you want to do.

I didn't have your "resources/tiles/tilea2.png" so I used my own "monstersArun.png" which is a 10 across spritesheet of 64x64 tiles.

You can modify this code to fit your spritesheet layout ( rows x columns and tile size).

Here is the code:

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    var tileCount=10;
    var tiles=new Array();
    var tileWidth;
    var t;
    var img=new Image();
    img.onload=function(){
      canvas.width=img.width;
      canvas.height=img.height;
      tileWidth=img.width/tileCount;
      ctx.drawImage(img,0,0);

      for(var t=0;t<tileCount;t++){
            tiles[t]=ctx.getImageData(t*tileWidth,0,tileWidth,img.height);
      }

      // just a test
      // draw the last tiles[] into another canvas
      var canvasTile=document.getElementById("canvasTile");
      var ctxTile=canvasTile.getContext("2d");
      canvasTile.width=tileWidth;
      canvasTile.height=canvas.height;
      ctxTile.putImageData(tiles[tiles.length-1],0,0);
    }
    img.src="monsterarun.png";

}); // end $(function(){});
</script>

</head>

<body>

    <canvas id="canvas" width=300 height=300></canvas><br/>
    <canvas id="canvasTile" width=64 height=64></canvas>

</body>
</html>

[Edit--to include Juan Mendes good idea give help on coding problem]

Also, as I look at your code...here:

tileSheet.src = spriteSheetLoc;

This causes your image to load. That loading takes time to do, so javascript starts loading the image, but it also immediately goes on to your next line of code. As a result, your code below tries to use the image before it's available--no good!

So you should give javascript a chance to fully load your image before processing the rest of your code. You do this using the onload method of Image like this:

var tileSheet = new Image();
tileSheet.onload=function(){

    // put ALL you code that depends on the image being fully loaded here

}
tileSheet.src = spriteSheetLoc;

Notice how you put tileSheet.src after the onload function. In reality, javascript executes tilesheet.src and then goes back to execute all the code in the onload block!

[Edit again -- complete code]

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var tempCanvas = document.getElementById("tempCanvas");
    var tempContext = tempCanvas.getContext("2d");

    var canvas = document.getElementById("gameCanvas");
    var context = canvas.getContext("2d");


    // create a new TileSet
    var Tiles = new TileSet();

    // parse a spritesheet into tiles
    //Tiles.addSpriteSheet("resources/tiles/tilea2.png","anyName");
    Tiles.addSpriteSheet("houseIcon.png","anyName");


    function TileSet() {
        this.Tiles = [];
        this.tileHeight = 32;
        this.tileWidth = 32;
        this.tileCount = 4;

        this.addSpriteSheet = function (spriteSheetLoc, name) {

            var me=this;  // me==this==TileSet

            var tileSheet = new Image();
            tileSheet.onload = function() {

                // calculate the rows/cols in the spritesheet
                // tilesX=rows, tilesY=cols
                var tilesX = tileSheet.width / me.tileWidth;
                var tilesY = tileSheet.height / me.tileHeight;

                // set the spritesheet canvas to spritesheet.png size
                // then draw spritesheet.png into the canvas
                tempCanvas.width = tileSheet.width;
                tempCanvas.height = tileSheet.height;
                tempContext.drawImage(tileSheet, 0, 0);
                for(var i=0; i<tilesY; i++) {

                    for(var j=0; j<tilesX; j++) {

                        // Store the image data of each tile in the array.
                        me.Tiles.push(tempContext.getImageData(j*me.tileWidth, i*me.tileHeight, me.tileWidth, me.tileHeight));
                    }
                }

                // this is just a test
                // display the last tile in a canvas
                context.putImageData(me.Tiles[me.Tiles.length-1],0,0);

            }
            // load the spritesheet .png into tileSheet Image()
            tileSheet.src = spriteSheetLoc;
        }
    }

}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="tempCanvas" width=300 height=300></canvas><br/>
    <canvas id="gameCanvas" width=300 height=300></canvas>
</body>
</html>
like image 62
markE Avatar answered Dec 07 '25 04:12

markE


Try replacing tempContext.width and tempContext.height with tempCanvas.width and tempCanvas.height, respectively. I don't believe contexts have width/height.

like image 33
Saulo Silva Avatar answered Dec 07 '25 05:12

Saulo Silva



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!