Phaser v3 TileSprite碰撞异常 玩家未接触地面即被阻挡
问题描述
我正在开发一款Phaser游戏,由于纹理需要重复平铺,我将地面设置为TileSprite对象,但玩家移动到距离地面一定距离时就会触发碰撞,无法继续向下移动。
按住S键控制玩家向下移动,最终玩家会触发碰撞无法继续下移,此时玩家并未实际接触到地面。
原有实现代码
游戏逻辑代码(JS)
let config = { type: Phaser.AUTO, width: 800, height: 500, physics: { default: 'arcade', arcade: { debug: false } }, scene: { preload: preload, create: create, update: update } }; const game = new Phaser.Game(config); function preload() { this.load.spritesheet('players', 'assets/players.png', { frameWidth: 16, frameHeight: 16 }); this.load.spritesheet('terrain', 'assets/terrain.png', { frameWidth: 16, frameHeight: 16 }); } function create() { this.anims.create({ key: 'blue_idle', frames: this.anims.generateFrameNumbers('players', { frames: [ 0, 1, 2, 3 ] }), frameRate: 8, repeat: -1 }); this.anims.create({ key: 'blue_walk', frames: this.anims.generateFrameNumbers('players', { frames: [ 32, 33, 34, 35, 36, 37, 38, 39 ] }), frameRate: 8, repeat: -1 }); this.key_W = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W); this.key_A = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A); this.key_S = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S); this.key_D = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D); this.platforms = this.physics.add.staticGroup(); const downPlat = this.add.tileSprite(512, 450, 1024, 16, 'terrain', 177); this.platforms.add(downPlat); this.spawnPlayer = ()=> { this.player = this.physics.add.sprite(400, 250); this.physics.add.collider(this.player, this.platforms); this.player.setScale(2); this.player.play('blue_idle'); } this.spawnPlayer(); } function update() { if (this.key_W.isDown) { this.player.setVelocityY(-50); } else if (this.key_S.isDown) { this.player.setVelocityY(50); } else { this.player.setVelocityY(0); } if (this.key_A.isDown) { this.player.setVelocityX(-150); this.player.play('blue_walk', true); this.player.flipX = true; } else if (this.key_D.isDown) { this.player.setVelocityX(150); this.player.play('blue_walk', true); this.player.flipX = false; } else { this.player.setVelocityX(0); this.player.play('blue_idle', true); } }
页面代码(HTML)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="path/to/phaser-arcade-physics.min.js"></script> </head> <body> </body> </html>
问题原因
碰撞提前触发核心是TileSprite创建方式错误:
- 用
this.add.tileSprite()在渲染层创建普通TileSprite后再加入物理静态组时,Arcade物理系统无法正确识别TileSprite的自定义平铺尺寸、原点偏移,会生成尺寸、位置完全错误的碰撞体。开启物理debug可看到,平台碰撞体是从画布顶部延伸到地面位置的巨型矩形,远大于实际地面尺寸,因此会提前触发碰撞。 - 次要问题:创建玩家Sprite时未指定初始纹理key,物理体初始化会使用默认尺寸,极端情况可能引发碰撞异常。
修复方案
- 直接使用物理系统提供的
this.physics.add.staticTileSprite()方法创建带正确物理体的平铺地面,不要用渲染层创建后再加入物理组。 - 创建玩家Sprite时传入初始纹理key,保证物理体初始化尺寸正确。
修复后的核心代码(create方法部分)
function create() { this.anims.create({ key: 'blue_idle', frames: this.anims.generateFrameNumbers('players', { frames: [ 0, 1, 2, 3 ] }), frameRate: 8, repeat: -1 }); this.anims.create({ key: 'blue_walk', frames: this.anims.generateFrameNumbers('players', { frames: [ 32, 33, 34, 35, 36, 37, 38, 39 ] }), frameRate: 8, repeat: -1 }); this.key_W = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W); this.key_A = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A); this.key_S = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S); this.key_D = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D); this.platforms = this.physics.add.staticGroup(); // 改用物理系统自带的staticTileSprite方法创建地面 const downPlat = this.physics.add.staticTileSprite(512, 450, 1024, 16, 'terrain', 177); this.platforms.add(downPlat); this.spawnPlayer = ()=> { // 创建玩家时传入初始纹理key this.player = this.physics.add.sprite(400, 250, 'players'); this.physics.add.collider(this.player, this.platforms); this.player.setScale(2); this.player.play('blue_idle'); } this.spawnPlayer(); }
替换后重新运行,玩家碰撞会精准匹配地面显示位置,不会再出现提前碰撞悬浮的问题。
内容的提问来源于stack exchange,提问作者LuisAFK
相关产品推荐
相关产品推荐

