Physics character with weapon in separate sprite: Best approach?

I think I’ve found a solution, I’ll confirm it here once I’ve tested it enough. My problem was basically linked to the default size of a container (0x0) resulting in a 64x64 body.

The 64x64 body would have its upper-left corner centered on my sprite.

To solve this, I simply had to set the container’s size to match the sprite’s BEFORE creating the physics body:

export default class GameCharacter extends Phaser.GameObjects.Container {
  private bodyWidthFlipOffsetX = 0.4;
  body: Phaser.Physics.Arcade.Body;

  private charBody: Phaser.GameObjects.Sprite;
  private charWeapon: Phaser.GameObjects.Sprite;

  constructor(scene: Phaser.Scene, x: number, y: number, characterId: string) {
    super(scene, x, y);

    this.charBody = scene.add.sprite(0, 0, "astro", "astro-red-idle_armed-6.png");
    this.charBody.anims.play("astro-red-idle");
    this.setSize(this.charBody.width, this.charBody.height); // DO THIS
    this.add(this.charBody);

    this.charWeapon = scene.add.sprite(0, 0, "astro", "astro-gun02_still1.png");
    this.add(this.charWeapon);
  }

The same class would then simply align the weapon on the sprite in the preUpdate() method:

  preUpdate(t: number, dt: number) {
      this.charWeapon.x = 12; // hard coded for the example
      this.charWeapon.y = 19; // hard coded for the example
  }

Then in case it can help, I’ve implemented the horizontal flipping in the update() method (called by the scene’s update method):

  update(cursors: Phaser.Types.Input.Keyboard.CursorKeys) {
    if (!cursors) {
      return;
    }

    if (cursors.left.isDown) {
      this.setFlippedX(true);
      this.body.setVelocity(-100, 0);
    } else if (cursors.right.isDown) {
      this.setFlippedX(false);
      this.body.setVelocity(100, 0);
    } else {
      this.body.setVelocity(0, 0);
    }
  }

The set flippedX method will simply handle the physics offset based on the object’s bodyWidthFlipOffsetX property:

  setFlippedX(flipped: boolean) {
    if (flipped) {
      this.scaleX = -1;
      this.body.offset.x = this.body.width + this.body.width * this.bodyWidthFlipOffsetX;
    } else {
      this.scaleX = 1;
      this.body.offset.x = this.body.width * this.bodyWidthFlipOffsetX;
    }
  }
2 Likes