Phaser主角内置小型Hitbox与墙体碰撞检测失效问题排查
我需要实现主角(protagonist)内部的小型hitbox与墙体的碰撞检测,以此实现主角能够贴近墙体行走的效果,但当前碰撞检测失效了。核心涉及walls图层与hitbox精灵,相关代码如下:
preload(){ //all the assets here } create() { console.log('create method executed'); const characterPositionInWorldMap = 3700; this.hitbox = this.physics.add.sprite(characterPositionInWorldMap, 250, null); // we need collision for this this.protagonist = this.physics.add.sprite(characterPositionInWorldMap, 250, 'protagonist'); // Initialize the hitbox sprite for collision detection this.hitbox.setSize(10, 10); // Set hitbox size (adjust as needed) this.protagonist.setDepth(3); this.hitbox.setDepth(3); const map = this.make.tilemap({ key: 'map' }); this.animatedTiles.init(map); this.physics.world.createDebugGraphic(); // Add the tilesets... // Create layers from tilemap data using different tilesets const groundLayer = map.createLayer('Groundlayer', darkTileset, 0, 0); const waterLayer = map.createLayer('waterlayer', darkTileset, 0, 0); const grassLayer = map.createLayer('grass_and_tree_layer', darkTileset, 0, 0); const stoneLayer = map.createLayer('stones_n_stuff', darkTileset, 0, 0); // Create hut layer using the hut tileset const hutLayer = map.createLayer('hutLayer', hutTileset, 0, 0); const transportlayer = map.createLayer('transportlayer', hutTileset, 0, 0); const walls = map.createLayer('walls', hutTileset, 0, 0); // Adjust the depth of layers as needed groundLayer.setDepth(1); waterLayer.setDepth(0); grassLayer.setDepth(1); stoneLayer.setDepth(2); hutLayer.setDepth(3); walls.setDepth(4); transportlayer.setDepth(10); // Enable collision detection between player and walls layer const collidableTileIndexes = [ 1279,1278,1244,1225,1257,1267,1302,1302,1320,1305,1277,1298,1307,1288,1319,1320,1321,1225,1223,1244 ]; collidableTileIndexes.forEach(index => { map.setCollision(index, true, walls); }); //animations.... // Enable physics for the protagonist this.physics.world.setBounds(0, 0, map.widthInPixels, map.heightInPixels); // Listen for collision between hitbox and walls this.physics.add.collider(this.hitbox, walls, () => { // Disable movement controls for the protagonist //this.protagonist.setVelocity(0); // Stop the protagonist's movement console.log("hitbox hit") }); // Listen for when hitbox stops colliding with walls this.physics.add.collider(this.hitbox, walls, () => { // Re-enable movement controls for the protagonist // You may need to adjust this depending on how your movement controls are implemented // For example, if you're using velocity-based movement, reset the velocity to allow movement this.protagonist.setVelocity(0); // Reset velocity }); this.groundLayer = groundLayer; this.waterLayer = waterLayer; this.grassLayer = grassLayer; this.stoneLayer = stoneLayer; this.hutLayer = hutLayer; this.walls = walls; this.transportlayer = transportlayer; // Set up camera to follow the player this.cameras.main.startFollow(this.protagonist); } update() { // Reset velocity this.protagonist.setVelocity(0); this.physics.collide(this.hitbox, this.walls, this.handleWallCollision, null, this); // Update the hitbox sprite position to match the player sprite this.hitbox.setPosition(this.protagonist.x, this.protagonist.y); ... }
移除重复的碰撞器注册:
你在create方法里两次调用this.physics.add.collider绑定hitbox和walls,且在update里又重复调用this.physics.collide,这会导致碰撞逻辑混乱。只需要在create中添加一次碰撞器即可,update里无需重复调用。调整Hitbox位置更新顺序:
在update中,要先更新hitbox的位置到主角当前坐标,再让物理引擎处理碰撞。把this.hitbox.setPosition(this.protagonist.x, this.protagonist.y)移到this.physics.collide之前。修正碰撞回调逻辑:
第二次添加的碰撞器会导致重复触发,且回调里的this.protagonist.setVelocity(0)逻辑错误(碰撞时重置速度会导致主角无法移动)。如果需要监听碰撞开始和结束,应该使用Phaser的碰撞事件:// 在create中替换原有的两个collider const collider = this.physics.add.collider(this.hitbox, walls, () => { console.log("hitbox hit"); // 这里处理碰撞时的逻辑,比如限制主角向墙体方向的移动 }); // 监听碰撞结束事件 collider.events.on('collisionend', () => { console.log("hitbox left wall"); // 恢复对应方向的移动权限 });优化Hitbox的物理属性:
由于hitbox完全跟随主角移动,不需要物理引擎驱动它的位置,所以可以关闭其自动移动:this.hitbox.body.moves = false;验证墙体碰撞设置:
利用this.physics.world.createDebugGraphic()生成的调试图形,检查walls图层的碰撞体是否正确显示,以及hitbox的碰撞框是否和预期一致。同时确认collidableTileIndexes中的tile索引确实是walls图层中需要碰撞的瓦片。
内容的提问来源于stack exchange,提问作者Aftersun Lotion

