Making a player grow as they eat stuff in ImpactJS

Posted on June 17, 2012 | 5 minute read

I’ve been running a few experiments and trials with Impact, just to get some stuff working that I wasn’t sure about before. One of these tests that I think will come in useful is having the player grow as it eats or picks up objects. I actually saw a couple of people getting to my blog through search terms related to Impact entity scaling and growth, so thought I’d outline the method I’ve been using here. This is probably not the neatest or best way to do this, but it works for me. Note that the actual resizing aspect of this whole thing has been outlined in an awesome tutorial on Point of Impact. This post more outlines how I am using that resizing mechanism to make the player grow continuously as they progress through the game.

So let’s say that our playable character starts off as a baby triangle and eats delicious squares to grow. What we need is:

  1. A player entity that physically scales (not just the animation, but the collision area as well) when touching a delicious square entity

  2. A player entity whose other attributes scale along with its physical dimensions as it grows (so that it can jump higher, move faster, etc as it eats more squares

Three files for this one:

  1. player.js

  2. player-controller.js

  3. square.js

The code is below. The indentation will most likely be way off in places as my code pasting Wordpress plugin is still pasting spaces weirdly and misaligning things, so sorry about any messiness. Let me know if you notice any errors as I’m actually modifying this from the original as I go because I have other growth stage stuff on top of this that isn’t really relevant.

player.js

This is where the resizing actually takes place. (note: I am cutting out pretty much all of the player code, including the update function, except for the relevant scaling stuff and some other attributes that we reference in the grow function)

ig.module( 
    'game.entities.player'
)
.requires(
    'impact.entity'
)
.defines(function(){
EntityPlayer = ig.Entity.extend({
    size: {x: 60, y:35},
    offset: {x: 0, y: 0},
    flip: false,
    maxVel: {x: 200, y: 200},
    friction: {x: 0, y: 0},
    accelGround: 300,
    accelAir: 500,
    health: 3,
    name:'player',
    gravityFactor:1,
    scale: 0.5,
    jump: 400,
    stage: 'newborn',    

    type: ig.Entity.TYPE.A, // Player friendly group
    checkAgainst: ig.Entity.TYPE.NONE,
    collides: ig.Entity.COLLIDES.PASSIVE,

    animSheet: new ig.AnimationSheet( 'media/player.png', 120, 70 ),

    init: function( x, y, settings ) {
        this.parent( x, y, settings );
        // Add animations
        this.addAnim( 'idle', 0.2, [0,1,2,1] );
        this.addAnim( 'move', 0.2, [0,1,2,1] );

            ig.game.player = this;

    },

    draw: function() {
        var ctx = ig.system.context;
        ctx.save();
        ctx.translate( ig.system.getDrawPos( this.pos.x - this.offset.x - ig.game.screen.x ),
                 ig.system.getDrawPos( this.pos.y - this.offset.y - ig.game.screen.y ) );
        ctx.scale( this.scale, this.scale );
        this.currentAnim.draw( 0, 0 );
        ctx.restore();
    }

});
});

Ok so the most important parts are the scale attribute and the stuff happening in the draw function. And again, the actual scaling part is taken from the relevant Point of Impact tutorial. The tutorial explains exactly what each part of code in the draw function does. I don’t want to copy and paste their awesome explanations or butcher them with my own mediocre versions, so just go there if you’re curious.

Disregard the “stage” attribute; I might make a separate post about this later. In reality I’m also adding manually set growth “stages” that increment with each level, but this is separate from this particular thing.

So our player starts off at a scale of 0.5 as a newborn in the beginning of the level. You can see that the animation dimensions specified above are actually double the size of the x and y size specified in the entity attributes, so if our baby player was the same size as the original animation frames it would have a scale of 1 (and would be fully grown).

player-controller.js

As a default for all of my Impact projects I create a custom Controller class. This is where I place all the things like the overall game score calculation, number of lives, etc. This is where the function that tells the player to grow goes. Note that at the moment nothing is calling this function (this will be called when the player touches the square, in square.js):

ig.module(
    'game.director.player-controller'
)
.requires(
    'impact.impact'
)
.defines(function(){

ig.PlayerController = ig.Class.extend({

    init: function(){

    },

    grow: function(player,growthpoints) {
        player.scale = player.scale + (player.scale * growthpoints);
        player.size.x = player.size.x + (player.size.x * growthpoints);
        player.size.y = player.size.y + (player.size.y * growthpoints);
        player.accelGround = player.accelGround + (player.accelGround * growthpoints);
        player.maxVel.x = player.maxVel.x + (player.maxVel.x * growthpoints);
        player.maxVel.y = player.maxVel.y + (player.maxVel.y * growthpoints);
        player.jump = player.jump + (player.jump * growthpoints);
        console.log('width: ' + player.size.x);
        console.log('height: ' + player.size.y);
    }

});

});

 square.js

The square.js file calls the grow function in the player-controller.js file above. Among other attributes it contains one called “growthpoints”, which specifies the amount of growth that the player entity will have when colliding with the square (which allows us to add different kinds of shapes for the player to eat down the line if we want, and have them increase the player’s size by varying amounts).

ig.module( 
    'game.entities.square'
)
.requires(
    'impact.entity'
)
.defines(function(){
EntitySquare = ig.Entity.extend({
    size: {x: 35, y:35},
    gravityFactor: 0,
    growthpoints: 0.05,

    type: ig.Entity.TYPE.B, 
    checkAgainst: ig.Entity.TYPE.A,
    collides: ig.Entity.COLLIDES.NONE,

    animSheet: new ig.AnimationSheet( 'media/square.png', 35, 35 ),

    init: function( x, y, settings ) {
        this.parent( x, y, settings );

        // Animations
        this.addAnim( 'idle', 1, [0] );
    },

    check: function( other ) {
            ig.game.playerController.grow(other,this.growthpoints);
            this.kill(); 	
    },

    update: function() {
        this.parent();
    },
});
});

The check function calls the “grow” function in our controller class when the square comes into contact with anything that has a type of A assigned to it (in this case just our player). It passes the entity that touched the square and the “growthpoints” attribute that will be used in the grow function to resize the entity. After calling the grow function the square is killed (eaten).

And that should be it. I may have missed stuff (as I mentioned I butchered the code quite a bit from the original for this, as I’m doing a whole bunch of other growth stage related stuff that’s not relevant to this), so let me know if you notice anything looking weird or off.




Categories:game dev dev games
comments powered by Disqus