Jump to content

Arcade physics - checking body.touching.down in custom sprite


ralphinator
 Share

Recommended Posts

I'm using a custom sprite for my main player. Based on the demo's I've seen I'm using `this.body.touching.down` to detect when the sprite is touching my bottom border. The problem is that when I use a custom sprite like this Phaser is first calling the `Phaser.Sprite.prototype.preUpdate` method which clears the touching state. Using `this.body.wasTouching.down` seems to work but seem semantically wrong since I'm trying to check the current state. Does anyone have ideas or suggestions on the appropriate way to do this?

Here's the custom player sprite I have so far. The relevant piece is the last part of the update method. Also just to note, I do have a main Game object that has the typical update method and if I call `this.player.body.touching.down` it does resolve to true as expected but that's not encapsulating the behavior of the player which is my purpose for having a custom player object.

BasicGame.Player = function(game, x, y) {    // new Sprite(game, x, y, key, frame)    Phaser.Sprite.call(this, game, x, y, 'dude');    game.physics.arcade.enable(this);     //  Player physics properties. Give the little guy a slight bounce.    // this.body.bounce.y = 0.2;    // this.body.gravity.y = 6;    this.body.bounce.y = 0.4;    this.body.gravity.y = 375;    this.body.collideWorldBounds = true;     // //  Our two animations, walking left and right.    this.animations.add('left', [0, 1, 2, 3], 10, true);    this.animations.add('right', [5, 6, 7, 8], 10, true);};BasicGame.Player.prototype = Object.create(Phaser.Sprite.prototype);BasicGame.Player.constructor = BasicGame.Player;BasicGame.Player.prototype.update = function() {    var cursors = BasicGame.input;    //  Reset the players velocity (movement)    this.body.velocity.x = 0;    if (cursors.left.isDown) {    // if (this.game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) {        //  Move to the left        this.body.velocity.x = -150;        this.animations.play('left');    } else if (cursors.right.isDown) {    // } else if (this.game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) {        //  Move to the right        this.body.velocity.x = 150;        this.animations.play('right');    } else {        //  Stand still        this.animations.stop();        this.frame = 4;    }     //  Allow the player to jump if they are touching the ground.    if (cursors.up.isDown && this.body.wasTouching.down) {    // if (this.game.input.keyboard.isDown(Phaser.Keyboard.UP) && this.body.touching.down) {        this.body.velocity.y = -350;    }};
Link to comment
Share on other sites

Since I can't tell from your description, let me ask the obvious: are you doing game.physics.arcade.collide() or other functions first in your game update? And, if you are using a tilemap, doing collision detection against it too?

 

I do something like the following in my own very similar code.

if(cursors.up.isDown && (this.body.onFloor() || this.body.touching.down)) {    this.body.velocity.y = -350;}
Link to comment
Share on other sites

Thanks for responding @Videlais. I am using the `collide` method and I did try using `this.body.onFloor() || this.body.touching.down`. The problem is an order of operation when creating a custom sprite. Notice that I'm extending the base `Phaser.Sprite` object and overriding the `update` method. The problem is that the default sprite object clears the `touching` state actually assigning it to `wasTouching` object in the `Phaser.Sprite.prototype.preUpdate` method which is called before the update method. This is only a problem when extending `Phaser.Sprite`.

 

Just for more context below is my Game object. Basically this is a copy of one of getting started demos that I've started to break into separate files. Instead of creating a base Player object that 'has a' sprite I'm extending the base sprite object so the the player 'is a' sprite. Below in the Game object if I check `this.player.body.touching.down` then it will be true because `Phaser.Sprite.preUpdate` hasn't been called yet. Again I'm trying to encapsulate the sprite behavior in the custom sprite object.

Aha! While posting this I figured out that the `Phaser.Sprite.update` is being called before the `Game.update` which is why the touching object which is sent in the `Game.update` when `this.game.physics.arcade.collide(this.player, this.platforms);` is called. Thanks for mentioning that @videlais. I was calling the `collide` method but I wasn't looking at it from that point of view.

 

So if I call `this.player.update` from `Game.update` after `collide` is called it will work but then `Player.update` is getting called twice. Once from the phaser default machinery because it's a sprite and once from my manual call. Any better ideas?

 

Just to summarize the behavior I'm trying to encapsulate. If the sprites body is touching down and the up key is pressed it will it will jump. The body state is set when `collide` is called which occurs outside of the the sprite object.

BasicGame.Game = function(game) {    this.player = null;    this.platforms = null;    this.stars = null;    this.score = BasicGame.score;    this.scoreText = null;};BasicGame.Game.prototype = {	create: function() {        // Use ARCADE physics        game.physics.startSystem(Phaser.Physics.ARCADE);        //  A simple background for our game        this.game.add.sprite(0, 0, 'sky');        this.scoreText = this.game.add.text(16, 16, 'Score: 0', { font: '32px arial', fill: '#000' });             //  The platforms group contains the ground and the 2 ledges we can jump on        this.platforms = this.game.add.group();             // Here we create the ground.        var ground = this.platforms.create(0, this.game.world.height - 64, 'ground');        game.physics.arcade.enable(ground);             //  Scale it to fit the width of the game (the original sprite is 400x32 in size)        ground.scale.setTo(2, 2);             //  This stops it from falling away when you jump on it        ground.body.immovable = true;             //  Now let's create two ledges        var ledge = this.platforms.create(400, 400, 'ground');        game.physics.arcade.enable(ledge);        ledge.body.immovable = true;             ledge = this.platforms.create(-150, 250, 'ground');        game.physics.arcade.enable(ledge);        ledge.body.immovable = true;        this.player = new BasicGame.Player(this.game, 32, this.game.world.height - 150);        this.game.add.existing(this.player);        // Inputs        BasicGame.input = this.game.input.keyboard.createCursorKeys();        this.stars = this.game.add.group();        //  Here we'll create 12 of them evenly spaced apart        for (var i = 0; i < 12; i++) {            //  Create a star inside of the 'stars' group            var star = this.stars.create(i * 70, 0, 'star');            game.physics.arcade.enable(star);                 //  Let gravity do its thing            star.body.gravity.y = 350;                 //  This just gives each star a slightly random bounce value            star.body.bounce.y = 0.7 + Math.random() * 0.2;        }	},	update: function() {        //  Collide the player and the stars with the platforms        this.game.physics.arcade.collide(this.player, this.platforms);        this.game.physics.arcade.collide(this.stars, this.platforms);        this.game.physics.arcade.overlap(this.player, this.stars, this.collectStar, null, this);	},    collectStar: function(player, star) {        // Removes the star from the screen        star.kill();        //  Add and update the score        this.score += 10;        this.scoreText.content = 'Score: ' + this.score;    },	quitGame: function(pointer) {		//	Then let's go back to the main menu.		this.game.state.start('MainMenu');	}};
Link to comment
Share on other sites

So if I call `this.player.update` from `Game.update` after `collide` is called it will work but then `Player.update` is getting called twice. Once from the phaser default machinery because it's a sprite and once from my manual call. Any better ideas?

 

Yeah, that will happen. It's exactly as you wrote: it will be called twice if you also make your own call to player.update() too.

 

A couple thoughts:

  • How about just changing the name of the update function on player? Maybe something like "updatePhysics?" That way, your player object would update its internal Phaser.Sprite bits automatically, you could run the collision checks, and then do the movement/key checks after by calling something like "updatePhysics" from player at the end of game's own update().
  • Without using player.update(), you could encapsulate some of the movement as separate functions within player and just do the key checking from game. Call things like moveLeft() or moveRight(). It isn't a full encapsulation answer, but it would allow you to do additional things for types of movement if you wanted.
Link to comment
Share on other sites

@Videlais thanks for the additional thoughts. Your first point is definitely a viable workaround for this, but it makes me feel dirty cuz I'm doing something the framework should be handling already.  :) 

After a bit more digging and playing I've found that changing the order that `Phaser.Game.upadate` updates all of its objects actually fixes this and another issue I struggled with initially. The initial problem I had was getting input from the game object via `this.game.input.keyboard.isDown(Phaser.Keyboard.UP)`. The input has the same problem as the body in that it's cleared before `Sprite.update` is called. Below are the ordering changes I made which just involved moving the `state.update` above `stage.update`.

 

Actually when the `preUpdate` methods are being called `state.update` is being called before `stage.update` as shown here. This makes me curious if this was just overlooked initially but I haven't dug through the git history yet to see if that's the case. Also I don't see an entry point for running unit tests so who knows if changing the order here breaks anything else. All I can say is it works fine for me so far.

 

I'm going to submit a pull request for this as it seems to be quite relevant to anyone that wants to create custom `Phaser.Sprite` objects.

 

Original:

this.stage.update();this.tweens.update();this.sound.update();this.input.update();this.state.update();this.physics.update();this.particles.update();this.plugins.update();

Updated:

this.state.update();this.stage.update();this.tweens.update();this.sound.update();this.input.update();// this.state.update();this.physics.update();this.particles.update();this.plugins.update();
Link to comment
Share on other sites

@adamyall thanks for the reply. Could you provide a small code snippet? I think I'm pickin up what you're puttin down, but how are you checking the collision with other sprites without passing those sprites into the update method or giving the player sprite a handle to the sprites it checks for collisions against?
 
For example the collision code to check for a player colliding with a platform from my Game object looks like so:

this.game.physics.arcade.collide(this.player, this.platforms);

If I move that to `Sprite.update` then it becomes:

this.game.physics.arcade.collide(this, this.platforms);

But the sprite doesn't have a handle to `this.platforms` and it shouldn't so how does that work? Is there another collision method I haven't found yet that should be used?

 

Along these lines I could have a `Sprite.collide` method that is the callback to pass into `arcade.collide` but that's going to get messy if there are multiple `arcade.collide` calls with `this.player`.

Link to comment
Share on other sites

Just found this post. Towards the bottom a couple of people ran into the exact same issue as me and it looks like there's a global `collsionLayer` (that I can't seem to find) that I could use to accomplish what @adamyall suggested.  Btw I submitted an issue and fix for this. Based on @rich's last comment on the post I linked 

 

 

it probably needs doing as a bigger internal change.

 

It sound's like it does need a bit of fixing. As I mentioned above without unit tests it's hard to know if my simple fix will break other things but here's hoping for a merge on that fix.

Link to comment
Share on other sites

Looks like the `collisionLayer` is a property of the Tilemap. From this post:

Tile map has a property called collisionLayer, which can be set to any of your layers, visible or not.

There is no way to inject sprites between layers right now, but it's on the list.

 

So that's not going to work as I originally thought. Also the docs don't show that property so it might not be available anymore anyway.

 

@adamyall that is a good idea! However, you would have to create all sprites that could potentially collide at the beginning or update the array/map for dynamic sprites. For example if you had an asteroid style game where an asteroid could drop a power up when it was shot all those power ups would have to be created up front. You could do things like reusing power ups so you didn't have to create multiple copies which would be a good idea anyway.

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

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