TypeError: Cannot read properties of undefined (reading 'width')

193 views Asked by At

I am trying to make my own world editor in Javascript. I am using a folder of individual tiles and placing them on screen. Then when you press on the tile you basically choose it. That works, but when the pointerdown switches from tile picking to tile placing I get this error.

I have checked if everything is defined but I might be missing something

Update: I have changed the arrow functions to use normal ones but it still doesnt work and gives the same error and am now providing the whole file code

i have encountered this console.log(this.map) VM10362:1 undefined undefined

These are the errors aka stack trace i think

this is my code

class WorldEditor extends Phaser.Scene {
    constructor() {
        super("editWorld")
        this.tileSize = 48
        this.tileIndex = 0
        this.tileFolder = 'Assets/Tiles/'
        this.tilesPerRow = 37
        this.totalTiles = 160
        this.tilePicked = false
    }

    preload() {
        // Load tile images from the folder
        for (let i = 0; i < this.totalTiles; i++) {
            this.load.image('tile (' + i + ')', this.tileFolder + 'tile (' + i + ').png')
        }
    }

    create() {
        const tileWidth = this.tileSize
        const tileHeight = this.tileSize
        const tilesPerRow = this.tilesPerRow
    
        if(!this.map){
            this.map = this.make.tilemap({ 
                tileWidth: this.tileSize, 
                tileHeight: this.tileSize,
                width: 1200,
                height: 720
            })  
        }
        
        for (let i = 0; i < this.totalTiles; i++) {
            const x = ((i % tilesPerRow) * tileWidth)/1.48
            const y = (Math.floor(i / tilesPerRow) * tileHeight)/1.48
    
            this.tile = this.add.image(x, y, 'tile (' + i + ')')
            this.tile.setOrigin(0)
            this.tile.setScale((tileWidth / this.tile.width)/1.48, (tileHeight / this.tile.height)/1.48)
        }
    
        // Set up pointer events
        this.setupPointerEvents()
        
        // Keyboard event to switch back to tile selection
        this.input.keyboard.on('keydown-F', () => {
            this.tilePicked = false
            console.log('Switched back to tile selection')
            this.setupPointerEvents() // Update pointer events after switching back
        })
    }
    
    setupPointerEvents() {
        this.input.off('pointerdown')
    
        if (!this.tilePicked) {
            const tiles = this.children.list.filter(child => child.texture && child.texture.key.startsWith('tile ('))
            tiles.forEach((tile, i) => {
                tile.setInteractive()
                tile.on('pointerdown', function() {
                    this.tileIndex = i
                    this.tilePicked = true
                    tile.setTint(0xff0000)
                    console.log('Selected tile index:', this.tileIndex)
                    this.setupPointerEvents()
                }, this)
            })
        } else {
            this.input.on('pointerdown', function(pointer) {
                this.tileX = Math.floor(pointer.x / this.tileSize)
                this.tileY = Math.floor(pointer.y / this.tileSize)
                this.placeTile(this.tileX, this.tileY)
            }, this)
        }
    }
    // Other methods...
    
    placeTile(tileX, tileY) {
        this.map.putTileAt(this.tileIndex, tileX, tileY) 
    }

    // Implement saveMap and loadMap functions to save and load level data
}

2

There are 2 answers

1
Duckpedia On BEST ANSWER

I added a tileset image i dont use and a layer and now it works thank you for all of the help!

if(!this.map){
            this.map = this.make.tilemap({ 
                tileWidth: this.tileSize, 
                tileHeight: this.tileSize,
                width: 1200,
                height: 720
            })  

            const tileset = this.map.addTilesetImage('tiles', 'tiles', this.tileSize, this.tileSize)
            const groundLayer = this.map.createBlankLayer('groundLayer', tileset)
        }
1
winner_joiner On

The main issue is that there is no layer created. create a layer (with a tileset) and there should be no errors.

btw.: the arrow functions and the this scopes are in this specific case (phaser development) correct.

Mini Example that solves the issue:
(I might update it to make it look better)
Just click the white square. and check the Browser console

document.body.style = 'margin:0;';
console.clear();

class MainScene extends Phaser.Scene{
    create () {
        this.add.text(10,10, 'DEMO TEXT')
        const tileHeight = 10
        const tilesPerRow = 3
        
        // just for the demo -- START
        let graphics  = this.make.graphics();
        graphics.fillStyle(0xffffff);
        graphics.fillRect(0, 0, 100, 100);
        graphics.generateTexture('tileset', 100, 100);
        // just for the demo -- END        
        
        if(!this.map){
            this.map = this.make.tilemap({ 
                tileWidth: tileHeight, 
                tileHeight: tileHeight,
                width: config.width,
                height: config.height
            })  
            
            const tileset = this.map.addTilesetImage('tileset', null, 10, 10);
            this.layer = this.map.createBlankLayer('layer1', tileset);
        }
        
        for (let i = 0; i < 9 ; i++) {
            const x = ((i % tilesPerRow) * tileHeight)
            const y = (Math.floor(i / tilesPerRow) * tileHeight)
    
            // just for the demo
            graphics.generateTexture('tile (' + i + ')', 10, 10);
            
            this.tile = this.add.image(x, y, 'tile (' + i + ')')
            this.tile.setOrigin(0)
            this.tile.setScale(10, 10)
        }
    
        // Set up pointer events
        this.setupPointerEvents()
        
        // Keyboard event to switch back to tile selection
        this.input.keyboard.on('keydown-F', () => {
            this.tilePicked = false
            console.log('Switched back to tile selection')
            this.setupPointerEvents() // Update pointer events after switching back
        })
   
    }
    
     setupPointerEvents() {
        this.input.off('pointerdown')
    
        if (!this.tilePicked) {
            const tiles = this.children.list.filter(child => child.texture && child.texture.key && (/tile \(/).test(child.texture.key))
            tiles.forEach((tile, i) => {
                tile.setInteractive()
                tile.on('pointerdown', () => {
                    this.tileIndex = i
                    this.tilePicked = true
                    tile.setTint(0xff0000)
                    console.log('Selected tile index:', this.tileIndex)
                    this.setupPointerEvents()
                })
            })
        } else {
            this.input.on('pointerdown', (pointer) => {
                const tileX = Math.floor(pointer.x / 10)
                const tileY = Math.floor(pointer.y / 10)
                this.placeTile(tileX, tileY)
            })
        }
    }
    
    placeTile(tileX, tileY) {
        console.info('tileplaced')
        this.map.putTileAt(this.tileIndex, tileX, tileY, this.layer) 
    }
}

var config = {
    width: 536,
    height: 183,
    scene: MainScene
}; 



new Phaser.Game(config);
<script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>