Physics character with weapon in separate sprite: Best approach?

Hi,

I’m looking for the best Phaser way to implement a game character holding a weapon:

  • the character is an animated sprite.
  • the chosen weapon’s position must be kept in sync with the arms.
  • see below: the character’s hands are actually part of the weapon’s sprite.

I’ve seen several approaches:

  1. using a Phaser.GameObjects.Container
    In this case, we subclass Container, and attach its children: character and weapon sprites. The weapon and sprite move well together as they remain relative to their parents. I’ve failed to add physics properly though - flipping horizontally creates a mess so far in my attempts.

  2. Creating separate sprites, syncing the absolute positions
    Both sprites are attached to the parent scene. The character’s sprite is smart and keeps adjusting the weapon’s position.
    Physics and flipping are much easier to handle, but unless the hands/weapons wielding data isn’t fit for symmetry, the X offset is based on being flipped or not.

  3. TBD
    Maybe there’s a series of other approaches :slight_smile:

What would be your recommendation to solve this?

Screenshot 2020-06-13 at 15.49.11

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