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

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,物理体初始化会使用默认尺寸,极端情况可能引发碰撞异常。
修复方案
  1. 直接使用物理系统提供的this.physics.add.staticTileSprite()方法创建带正确物理体的平铺地面,不要用渲染层创建后再加入物理组。
  2. 创建玩家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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 17:09:12