Jump to content

Help with tilemap collision and camera LERP


okaybenji
 Share

Recommended Posts

Hi, there! Hope this is the right place to seek Phaser help. If not, I'm happy to move my post. :)

I have three issues I'm trying to troubleshoot:

First, the camera LERP system centers my player when I'm walking left or upward, but when I'm walking right or downward, it allows the player to get and stay close to the right/bottom edges of the screen.

Second, I can't seem to get my character to collide with the layer I created in Tiled specifically for collisions. The character just walks right over the boundaries as though they are not there.

Third, when I load up my game for the first time, the player character (and also any other entities which have sprite-based animations) shows up as his entire spritesheet rather than just one (animated) frame. After refreshing the browser, the character and other entities look normal and their animations work correctly. I would guess it has something to do with loading order/caching of assets? Has anyone seen this issue before?

I haven't included the player here, but it's basically just a sprite which gets some custom properties and methods attached to it and then is returned. I'm happy to include more code if it's helpful!

Thanks for taking a look!

// Play state.
const Play = (game) => {
  let player;
  let boundary;

  const factories = {
    entities: {
      player: require('../factories/entities/player'),
    },
    keys: require('../factories/keys')
  };

  const play = {
    create() {
      const map = game.add.tilemap('dungeon');
      map.addTilesetImage('dungeon', 'dungeon');
      map.createLayer('Base');
      boundary = map.createLayer('Bounds');
      map.createLayer('Decorations');
      map.createLayer('Foreground');

      game.physics.startSystem(Phaser.Physics.ARCADE);
      map.setCollisionBetween(1, 100000, true, 'Bounds');

      const bounds = {x: 0, y: 0, width: 512, height: 512};

      player = factories.entities.player({
        sprite: game.add.sprite(250, 250),
        keys: factories.keys(game),
        game
      });
      game.physics.arcade.enable(player);

      // camera lerp
      game.world.setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
      game.camera.follow(player, Phaser.Camera.FOLLOW_LOCKON, 0.02, 0.02);
    },

    update() {
      game.physics.arcade.collide(player, boundary);
    }
  };
  
  return play;
};

module.exports = Play;

 

Edited by okaybenji
remove potentially distracting code
Link to comment
Share on other sites

1) I never really completely understood the camera, but play around with the values that you are passing to it. Worst case, take a look at the code ;D


2) you should use:

map.setCollisionByExclusion([], true, boundary);

3) It is possible that the assets haven't finished loading, and you are starting the game too soon.
At any rate, you should have a menu screen (or at least a button that starts the game.) All games do this, to some extend or another, so you are just playing it safe. If you are using webpack2, you can use a flag to autostart the game inside a setTimeout

if(__DEV__){
  setTimeout( () => {this.state.start('Play');}, 0);
}

 

so that it doesn't feel slow while developing the game.

Link to comment
Share on other sites

Thanks so much for taking the time to respond!

1) As obvious as it seems in hindsight, 'take a look at the code' is actually great advice. I will do that.

2) Ah yes, I see setCollisionByExclusion makes a lot more sense, thanks! However, I'm still getting the same result... Can you tell whether I am doing something else wrong?Here's what my code looks like currently:

const Play = (game) => {
  let player;
  let boundary;

  const factories = {
    entities: {
      player: require('../factories/entities/player'),
    },
    keys: require('../factories/keys')
  };

  const play = {
    create() {
      const map = game.add.tilemap('dungeon');
      map.addTilesetImage('dungeon', 'dungeon');
      map.createLayer('Base');
      boundary = map.createLayer('Bounds');
      map.createLayer('Decorations');
      map.createLayer('Foreground');

      game.physics.startSystem(Phaser.Physics.ARCADE);
      map.setCollisionByExclusion([], true, boundary);

      const bounds = {x: 0, y: 0, width: 512, height: 512};

      player = factories.entities.player({
        sprite: game.add.sprite(250, 250),
        keys: factories.keys(game),
        gamepad: game.input.gamepad.pad1,
        game
      });
      game.physics.arcade.enable(player);
    },

    update() {
      // collide entities
      game.physics.arcade.collide(player, boundary, () => {
        console.log('colliding!'); // <- Never gets logged.
      });
    }
  };
  
  return play;
};

module.exports = Play;

3) I actually do have a title screen, though I've tracked the problem to my trying to be clever and calculate the width and height of my animation frames. My filenames include the name of the animation and the number of frames in it, and I create a temporary image to get its width and height in pixels. But on the first load, the width and height come through as 0 for some reason. I wonder if there's any way to make this work without causing the issue... Here's the code if you care to take a look.

const spritesheets = [
  // TODO: access the filesystem to build this array automatically
  'player/walk_e-2.png',
  'player/walk_n-2.png',
  'player/walk_s-2.png',
  'player/walk_w-2.png',
].map((asset, i) => {
  const slash = asset.indexOf('/');
  const underscore = asset.indexOf('_');
  const dash = asset.indexOf('-');
  const dot = asset.indexOf('.');

  const entity = asset.slice(0, slash);
  const name = entity + '_' + asset.slice(slash + 1, dash);
  const frameCount = Number(asset.slice(dash + 1, dot));
  const path = '../../assets/images/' + asset;

  const image = (function() {
    const image = new Image();
    image.src = path;
    return image;
  }());

  return { entity, name, frameCount, image };
});

      spritesheets.forEach(({ name, image, frameCount }) => {
        const path = image.src;
        const width = image.width / frameCount;
        const height = image.height;

        game.load.spritesheet(name, path, width, height);
      });

 

 

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...