You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Phaser世界编辑器切换放置模式触发TypeError问题求助

问题:Phaser世界编辑器切换到瓦片放置模式时出现TypeError错误

我用JavaScript基于Phaser框架开发自定义世界编辑器,能加载文件夹里的独立瓦片并展示,点击选瓦片功能正常,但切换到瓦片放置模式时,出现错误:TypeError: Cannot read properties of undefined (reading 'width')。

已经检查过变量定义但可能有遗漏,把箭头函数换成普通函数后问题依旧,执行console.log(this.map)输出undefined。

完整代码如下:

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
}

问题分析与解决方案

核心问题

this.map虽然被创建,但未关联任何瓦片集(Tileset),Phaser的putTileAt方法需要依赖瓦片集的尺寸信息才能正常工作,导致读取width属性时触发undefined错误。同时原代码中if(!this.map)的判断多余,Scene初始化时this.map必然是undefined,可能引发不必要的逻辑混乱。

具体修复步骤

  1. 创建瓦片集并关联图层
    在create方法中,创建tilemap后必须添加瓦片集,并创建用于放置瓦片的图层:

    // 替换原create方法中的map初始化部分
    this.map = this.make.tilemap({ 
        tileWidth: this.tileSize, 
        tileHeight: this.tileSize,
        width: 1200,
        height: 720
    })  
    // 关联已加载的瓦片图像到瓦片集,第一个参数用任意已加载的tile纹理名即可
    this.tileset = this.map.addTilesetImage('tile (0)', null, this.tileSize, this.tileSize)
    // 创建图层用于显示放置的瓦片
    this.layer = this.map.createLayer(0, this.tileset, 0, 0)
    
  2. 修正placeTile方法
    确保putTileAt指定目标图层,同时增加基础校验:

    placeTile(tileX, tileY) {
        if (!this.map || !this.tileset) return;
        this.map.putTileAt(this.tileIndex, tileX, tileY, true, this.layer)
    }
    
  3. 优化瓦片选择逻辑
    避免循环中覆盖this.tile全局属性,同时取消之前选中瓦片的 tint:

    // 替换原create方法中的瓦片循环部分
    for (let i = 0; i < this.totalTiles; i++) {
        const x = ((i % tilesPerRow) * tileWidth)/1.48
        const y = (Math.floor(i / tilesPerRow) * tileHeight)/1.48
    
        // 用局部变量tile,避免覆盖全局属性
        const tile = this.add.image(x, y, 'tile (' + i + ')')
        tile.setOrigin(0)
        tile.setScale((tileWidth / tile.width)/1.48, (tileHeight / tile.height)/1.48)
    }
    
    // 替换setupPointerEvents中tile的pointerdown事件逻辑
    tiles.forEach((tile, i) => {
        tile.setInteractive()
        tile.on('pointerdown', function() {
            this.tileIndex = i
            this.tilePicked = true
            // 清除所有瓦片的tint,避免多个瓦片变红
            tiles.forEach(t => t.clearTint())
            tile.setTint(0xff0000)
            console.log('Selected tile index:', this.tileIndex)
            this.setupPointerEvents()
        }, this)
    })
    

内容的提问来源于stack exchange,提问作者Duckpedia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 23:53:18