Hi everyone,
I’m relatively new to Phaser and working on my first project. I’m trying to allow users to draw sketches in the game, which will then become part of the game terrain or item. To achieve this, I’ve created a “sketchpad” where users can draw, and I’m attempting to merge the current tileset with the new sketch.
Here’s a simplified overview of what I’m doing:
- The user draws a sketch in the game.
- I convert the sketch to a base64 image.
- I merge this image with the existing tileset using canvas operations.
- I add this new tileset to Phaser using
textures.addBase64()
. - I update the Tileset data using
tileset.setImage()
. - Finally, I attempt to place the new tile using
groundLayer.putTileAt()
.
However, I’m encountering an error at the last step. Despite seeing that the Tileset has been updated correctly, I’m getting the following error:
phaser.esm.js:223298 Uncaught TypeError: Cannot read properties of undefined (reading '2')
at Object.PutTileAt (phaser.esm.js:223298:25)
at TilemapLayer2.putTileAt (phaser.esm.js:219405:34)
at TextureManager2.<anonymous> (Game.ts:109:32)
at TextureManager2.emit (phaser.esm.js:187:35)
at image.onload (phaser.esm.js:211023:23)
And where should render the new tile, it’s rendering anything, a transparent space.
I’ve read the Phaser documentation and searched the forum but couldn’t find a solution. If anyone has experience with this or can suggest an alternative approach, I’d greatly appreciate your help.
Here’s the relevant code from my scene for reference:
export class GameScene extends Phaser.Scene {
private map!: Phaser.Tilemaps.Tilemap;
private tileset!: Phaser.Tilemaps.Tileset;
private groundLayer!: Phaser.Tilemaps.TilemapLayer;
constructor() {
super({ key: 'GameScene', active: true });
}
preload() {
this.load.image('tileset', 'assets/Terrains/tileset.png');
}
create() {
const mapWidth = 25 * 40;
const mapHeight = 19 * 40;
const tileSize = 16;
this.map = this.scene.scene.make.tilemap({ key: 'map', width: mapWidth, height: mapHeight, tileWidth: tileSize, tileHeight: tileSize });
this.tileset = this.map.addTilesetImage('tileset')!;
this.groundLayer = gameTable.groundLayer = this.map.createBlankLayer('layer1', this.tileset, 0, 0) as Phaser.Tilemaps.TilemapLayer;
generateTerrainMap(this.map, this.groundLayer); // just populate the layer using a noise and putTIleAt();
if (this.input.mouse) {
this.input.mouse.disableContextMenu();
}
this.setupInputListeners();
}
processSketch(params: { imageData: string) {
const canvas = document.createElement('canvas');
const context: any = canvas.getContext('2d');
const generatedImage = new Image();
generatedImage.src = imageData; // base64, 512x512 px
generatedImage.onload = () => {
const targetWidth = 16;
const targetHeight = 16;
// resize sketch
canvas.width = targetWidth;
canvas.height = targetHeight;
context.drawImage(generatedImage, 0, 0, targetWidth, targetHeight);
const resizedBase64 = canvas.toDataURL('image/png');
const resizedImage = new Image();
resizedImage.src = resizedBase64;
resizedImage.onload = () => {
console.log(this.map.getTileset('tileset'))
console.log(this.tileset.total);
let tilesetBase64 = this.textures.getBase64('tileset');
console.log(tilesetBase64);
const tilesetImage= new Image();
tilesetImage.src = tilesetBase64;
tilesetImage.onload = () => {
console.log('original', tilesetImage.width, tilesetImage.height);
console.log('new', resizedImage.width, resizedImage.height);
// clear canvas and resize to new tileset size
context.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = tilesetImage.width + targetWidth;
canvas.height = Math.max(tilesetImage.height, targetHeight);
// draw the new tileset appending the new tile
context.drawImage(tilesetImage, 0, 0);
context.drawImage(resizedImage, tilesetImage.width, 0);
const mergedBase64Image = canvas.toDataURL('image/png');
console.log(mergedBase64Image); // we are getting the correct image
const mergedImage = new Image();
mergedImage.src = mergedBase64Image;
mergedImage.onload = () => {
this.textures.addBase64('mergedTexture', mergedBase64Image);
this.textures.on('onload', () => {
let mergedTexture = this.scene.systems.textures.get('mergedTexture');
console.log(mergedTexture);
this.tileset.setImage(mergedTexture);
console.log(this.tileset.total); // here we are getting the correct value, the old tileset total + 1
let tilesetLen = this.tileset.total;
this.groundLayer.putTileAt(tilesetLen - 1, 0, 0); // the error happens here
});
};
}
};
}
}
Thank you in advance for your assistance. Please let me know if there’s any additional information I can provide.
Edit: remove ‘async’ from function and add description of transparent ‘tile’ after error