Sign in to follow this  
bclindner

Camera issues when following object moving around Tilemap

Recommended Posts

Hello! I'm not well versed in game development but I write a lot of JS for my classes and my job, so I'd like to think I have some semblance of knowing what I'm doing, but the issue I'm having with Phaser right now is completely beyond me.

It's hard to accurately describe my problem in words, so I've left the entire project as an attachment for you to see for yourself. (Cursor keys to control.) Essentially, I'm trying to write a simple platformer using a tilemap larger than the screen. However, when the camera is set to follow an object, and that object crosses the center of the window in a given direction on either axis, the camera slides in that direction at high speed - it seems to depend on how far you've crossed the threshold -  until it reaches the stage boundary. The most difficult part is that the object seems to move properly relative to the camera - meaning it moves across the stage itself at very high speeds, confusing the arcade physics system and causing clips and zips galore if you're in the wrong places (try jumping under the spawn platform, for instance - you'll zip to the left or right)

The problem doesn't happen at all when the stage size is equal to the game window size, nor does it happen if the camera is not following an object.

Here are some things I've tried to resolve the issue:

  • Removing game window scaling code
  • Rewriting of Tilemap import code & use of different maps and tilesets
  • Refactoring/simplification of the Player object (as well as forgoing the use of a class and extending Phaser.Sprite)
  • Applying "layer.fixedToCamera = false" and "layer.fixedToCamera = true"
  • Restarting the project and rewriting all code (this happened on a prior test project as well)
  • Rolling back to some prior versions of Phaser

I'm not certain whether the issue is with my Tilemap setup, my use of the camera, the general jankiness of the arcade physics system, or maybe something else I haven't taken into account, but given half of my Google searches lead here, I'm sure someone's got some kind of answer for me.

For convenience, here's the important bits of my code, in case anything looks off at first glance:

(game.js, the main game code)

'use strict'
/* global Phaser, Player */
var game = new Phaser.Game(
    161, // width
    145, // height
    Phaser.AUTO, // idek
    'game', // game name
    { create: create, update: update, preload: preload, render: render } // functions
)

game.antialias = false

function preload () {
  game.load.tilemap('mapTest', 'assets/map/testmap.json', null, Phaser.Tilemap.TILED_JSON)
  game.load.image('tileGrass', 'assets/img/grass.png')
  game.load.image('bgClouds', 'assets/img/bg.png')
  game.load.spritesheet('spritePlayer', 'assets/img/player.png', 9, 9)
}

let bg, player, map, layer

function create () {
  // display background
  bg = game.add.image(1, 1, 'bgClouds')
  bg.fixedToCamera = true
  //  instantiate the map
  map = game.add.tilemap('mapTest')
  map.addTilesetImage('tileGrass', 'tileGrass')
  layer = map.createLayer('terrain')
  map.setCollisionBetween(1, 10, 'terrain')
  layer.resizeWorld()
  layer.debug = true
  // initialize physics and set gravity
  game.physics.startSystem(Phaser.Physics.ARCADE)
  game.physics.arcade.gravity.y = 257
  // set up the player
  player = new Player(game, 33, 49)
  game.physics.arcade.enable(player)
  // set game scale
  game.scale.scaleMode = Phaser.ScaleManager.USER_SCALE
  game.scale.setUserScale(6, 6)
  game.renderer.renderSession.roundPixels = true
  Phaser.Canvas.setImageRenderingCrisp(game.canvas)
  // camera: follow the player
  game.camera.follow(player)
}

function update () {
  game.physics.arcade.collide(player, layer)
}

function render () {
  game.debug.body(player)
  game.debug.body(layer)
  game.debug.text(Math.round(player.body.position.x), 9, 17)

}

game.antialias = false

(player.js, the Player object file)

/* global Phaser */
class Player extends Phaser.Sprite {
  constructor (game, x, y) {
    // set up the player
    super(game, x, y, 'spritePlayer')

    // physics
    game.physics.enable(this, Phaser.Physics.ARCADE)
    this.velCap = {x: 64, y: 150}
    this.jumpVel = -100
    this.acceleration = 500 // units per second

    // animations
    this.anims = {
      'idle': this.animations.add('idle', [0, 1, 2, 3], 6, true),
      'walk': this.animations.add('walk', [8, 9, 10, 11, 12, 13, 14, 15], 15, true),
      'down': this.animations.add('down', [21, 22], 15, false),
      'jump': this.animations.add('jump', [16, 17, 18, 19], 15, false),
      'fall': this.animations.add('fall', [19], 20, true),
      'land': this.animations.add('land', [20, 21, 22, 23], 10, false)
    }
    this.animations.play('idle')
    this._facing = true
    this.anchor.setTo(0.5, 1)

    this.body.setSize(6,8,1,0)
    this.body.tilePadding.x = 12
    this.body.tilePadding.y = 12

    // input
    this.keys = game.input.keyboard.createCursorKeys()

    // add the player to the stage
    game.stage.addChild(this)
  }
  // facing code
  set face (bool) {
    if (bool) {
      this.scale.x = 1
    } else { this.scale.x = -1 }
    this._facing = bool
  }
  get face () {
    return this._facing
  }

  update () {
    let airborne = !(this.body.blocked.down || this.body.touching.down)
    let dt = game.time.physicsElapsed
    if(this.keys.right.isDown) {
      this._move(dt, true, airborne)
    }
    else if(this.keys.left.isDown) {
      this._move(dt, false, airborne)
    } else{
      this._decelerate(airborne)
    }
    if(this.keys.up.isDown && !airborne) {
      this._jump()
    } else if(this.keys.down.isDown && !airborne) {
      this._crouch()
    }
    this._capMovement()
  }

  _move (dt, right, airborne) {
    if(this.face != right){
      this.face = right
      if(!airborne){
      this.body.velocity.x = -this.body.velocity.x
      }
    } else {
      this.body.velocity.x += Math.floor(this.acceleration * (right ? 1 : -1) * dt)
      if (airborne) {
        this.animations.play('fall')
      }
      else {
        this.animations.play('walk')
      }
    }
  }

  _jump () {
    this.body.velocity.y = this.jumpVel
    this.animations.play('jump')
  }

  _crouch() {
    // stop all movement and crouch the player
    this.body.velocity.x = 0
    if(this.animations.currentAnim.name != 'down'){
    this.animations.play('down')
    }
  }
  _decelerate(airborne) {
      this.body.velocity.x = this.body.velocity.x / 2
      if(airborne){
        this.animations.play('fall')
      } else {
        this.animations.play('idle')
      }
  }

  _capMovement () {
      if(this.body.velocity.y > this.velCap.y){
          this.body.velocity.y = this.velCap.y
      } else if (this.body.velocity.y < -this.velCap.y) {
          this.body.velocity.y = -this.velCap.y
      }
      if(this.body.velocity.x > this.velCap.x){
          this.body.velocity.x = this.velCap.x
      } else if (this.body.velocity.x < -this.velCap.x) {
          this.body.velocity.x = -this.velCap.x
      }
  }
}

Thanks in advance!

phaser_wtf.zip

Share this post


Link to post
Share on other sites
1 hour ago, samme said:

My first guess is you should put the player in the World, not the Stage:


// add the player
this.game.world.add(this);

Also add game.debug.cameraInfo(…).

Good call - that was exactly it, thanks!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.