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

Phaser 3子弹功能异常求助:点击触发游戏崩溃

问题分析与修复方案

核心错误点

  1. 子弹无物理体:用game.add.image()创建子弹,该方法生成的对象默认不带物理组件,导致bul.body未定义,触发点击崩溃。
  2. 提前启用未初始化对象的物理体:create()中调用game.physics.arcade.enable(bul)时,bul还未创建(值为undefined),属于无效操作。
  3. 点击坐标错误:使用game.input.x/y获取的是屏幕像素坐标,而非游戏世界坐标,若游戏有缩放或相机移动会导致方向计算偏差。
  4. 未初始化对象属性访问:enemy_move()中直接访问de3.body,但de3仅在enemy_spawn3()中创建,初始阶段未执行该函数,会触发初始化报错。

修复后的完整代码

'use strict'

const game = new Phaser.Game(1675, 800, Phaser.AUTO, 'game-canvas', { preload, create, update })

let tma
let hp
let dude
let fon
let cursor
let bot
let de1
let de2
let de3
let counter = 0
let epc = 0
// 用数组管理子弹,避免单变量覆盖旧子弹
let bullets = []

function preload() {
    game.load.image("de1", "de1.png")
    game.load.image("de2", "de2.png")
    game.load.image("de3", "de3.png")
    game.load.image("ba", "ba.png")
    game.load.image("ts", "16x16_tileset_platfprms_sunset(1).png")
    game.load.image("bul", "bullet.png")
    game.load.tilemap("tm", 'tm1.json', null, Phaser.Tilemap.TILED_JSON)
    game.load.spritesheet('dude', 'dude-green.288x40.9x1.png', 288 / 9, 40 / 1)
    // 移除重复的子弹资源加载
}

function create() {
    crmap()
    dudee()
    enemy_spawn()
    // 移除无效的物理体启用代码
}

function update() {
    game.physics.arcade.collide(dude, bot)
    dude_move()
    counter++

    if (game.input.activePointer.isDown && counter >= 100) {
        bullet()
        counter = 0
    }

    // 自动销毁超出游戏边界的子弹,避免内存泄漏
    bullets.forEach((bullet, index) => {
        if (!bullet.world.bounds.contains(bullet.x, bullet.y)) {
            bullet.destroy()
            bullets.splice(index, 1)
        }
    })
}

function crmap(params) {
    tma = game.add.tilemap('tm')
    tma.addTilesetImage("tileset", 'ts')

    bot = tma.createLayer("bot")

    tma.setCollisionBetween(1, 999, true)
}

function dudee() {
    dude = game.add.sprite(game.width / 2, game.height / 2, "dude")
    dude.scale.setTo(2)
    dude.frame = 4
    dude.animations.add('left', [0, 1, 2, 3], 10, false)
    dude.animations.add("right", [5, 6, 7, 8], 10, false)
    cursor = game.input.keyboard.createCursorKeys()

    game.physics.arcade.enable(dude)
    dude.body.gravity.y = 400
    dude.body.collideWorldBounds = true
    game.physics.arcade.collide(dude, bot)
}

function dude_move() {
    if (cursor.right.isDown) {
        dude.body.velocity.x = 200
        dude.animations.play("right")
    } else if (cursor.left.isDown) {
        dude.body.velocity.x = -200
        dude.animations.play("left")
    } else {
        dude.body.velocity.x = 0
        dude.frame = 4
    }

    if (cursor.up.justDown) {
        dude.body.velocity.y = -200
    }
}

function enemy_spawn() {
    de1 = game.add.sprite(game.rnd.integerInRange(0, 1675 - 94.5), 0, 'de1')
    de1.scale.setTo(0.3)
    game.physics.arcade.enable(de1)
    de1.body.collideWorldBounds = true
    game.physics.arcade.collide(de1, bot)
    // 敌人移动逻辑移到生成函数内,避免访问未初始化对象
    de1.body.velocity.setTo(game.rnd.integerInRange(100, 200), 0)
    de1.body.bounce.setTo(1)
}

function enemy_spawn2() {
    de2 = game.add.sprite(game.rnd.integerInRange(0, 1675 - 94.5), 0, 'de2')
    de2.scale.setTo(0.3)
    game.physics.arcade.enable(de2)
    de2.body.collideWorldBounds = true
    game.physics.arcade.collide(de2, bot)
    de2.body.velocity.setTo(game.rnd.integerInRange(150, 250), 0)
    de2.body.bounce.setTo(1)
}

function enemy_spawn3() {
    de3 = game.add.sprite(game.rnd.integerInRange(0, 1675 - 94.5), 0, 'de3')
    de3.scale.setTo(0.3)
    game.physics.arcade.enable(de3)
    de3.body.collideWorldBounds = true
    game.physics.arcade.collide(de3, bot)
    de3.body.velocity.setTo(game.rnd.integerInRange(400, 500), 0)
    de3.body.bounce.setTo(1)
}

function enemy_poof1() {
    de1.destroy()
    epc += 1
    enemy_spawn2()
}

function enemy_poof2() {
    de2.destroy()
    epc += 1
    enemy_spawn3()
}

function enemy_poof3() {
    de3.destroy()
    epc += 1
}

function bullet() {
    // 用sprite创建子弹,默认支持物理体
    let bul = game.add.sprite(dude.x, dude.y, "bul")
    bullets.push(bul)
    game.physics.arcade.enable(bul)

    const playerX = dude.x;
    const playerY = dude.y;

    // 使用游戏世界坐标,而非屏幕坐标
    const inputX = game.input.activePointer.worldX;
    const inputY = game.input.activePointer.worldY;

    // 计算方向向量并归一化
    const directionX = inputX - playerX;
    const directionY = inputY - playerY;

    const length = Math.sqrt(directionX * directionX + directionY * directionY);
    // 防止点击玩家自身位置时除以0
    if (length === 0) return;

    const normalizedDirectionX = directionX / length;
    const normalizedDirectionY = directionY / length;

    // 设置子弹速度,调高数值更符合游戏体验
    const bulletSpeed = 500;
    bul.body.velocity.setTo(normalizedDirectionX * bulletSpeed, normalizedDirectionY * bulletSpeed);
}

function bullet_destroy(bullet) {
    // 传入具体子弹对象销毁,避免依赖全局变量
    bullet.destroy()
    const index = bullets.indexOf(bullet)
    if (index !== -1) bullets.splice(index, 1)
}

额外优化说明

  • 改用数组bullets管理所有子弹,避免全局变量被新子弹覆盖,同时方便批量处理(如自动销毁、碰撞检测)
  • 简化dude_move中的静止状态判断逻辑,移除冗余的cursor创建操作
  • 将敌人移动逻辑拆分到对应生成函数中,避免访问未初始化对象
  • 添加子弹自动销毁逻辑,防止内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 02:21:03