Impact

This forum is read only and just serves as an archive. If you have any questions, please post them on github.com/phoboslab/impact

1 decade ago by Jon123

I have been trying to create a ladder entity but have had a few problems. Anyone know any good examples I can use? I am basing everything on the JumpNRun game so I am looking for something to fit in with that.

Thanks in advance

1 decade ago by Joncom

Maybe you could find these useful. They were used in my game Digga. Sorry if it's a bit chaotic. I'd be happy to answer any questions if you have them.

/* ladder.js */
ig.module('game.entities.ladder')

.requires('impact.entity')

.defines(function() {

    EntityLadder = ig.Entity.extend({

        size: {
            x: 16,
            y: 16
        },

        // Width of ladders in game.
        real_width: 10,

        _wmDrawBox: true,
        _wmScalable: true,
        zIndex: 256,
        nature: 'ladder',
        gravityFactor: 0,
        animSheet: new ig.AnimationSheet('media/palette.png', 16, 16),

        init: function(x, y, settings) {
            this.parent(x, y, settings);
            this.addAnim('idle', 1, [5]);
            if(!ig.global.wm) {
                var offset = (this.size.x - this.real_width)/2;
                this.offset.x = offset;
                this.size.x = this.real_width;
                this.pos.x += offset;
            }
        },

        update: function() {},

        draw: function() {
            if(!ig.global.wm) {

                // Draw tiles except the first one.
                if( this.currentAnim ) {
                    var tilesize = ig.game.collisionMap.tilesize;
                    var tile_height = this.size.y / tilesize;
                    for(var i=1; i<tile_height; i++) {
                        this.currentAnim.draw(
                            this.pos.x - this.offset.x - ig.game._rscreen.x,
                            this.pos.y - this.offset.y - ig.game._rscreen.y + (i * tilesize)
                        );
                    }
                }

                // Draw first tile, and debug info.
                this.parent();
            }
        }

    });

});

/* humanoid.js */
ig.module('game.entities.humanoid')

.requires('impact.entity')

.defines(function() {

    EntityHumanoid = ig.Entity.extend({

        size: {
            x: 16,
            y: 16
        },

        _wmIgnore: true,
        fallSpeed: 30,
        speed: 60,
        defaultGravityFactor: 3,
        animSheet: new ig.AnimationSheet('media/player.png', 16, 16),

        input: null, // left, right, down, up

        climbing: false,
        over_ladder: false,
        ladder: null,
        over_bridge: false,
        bridge_that_entity_is_over: null,
        possible_land_on_entity: false,

        init: function(x, y, settings) {
            this.parent(x, y, settings);
            this.maxVel.x = this.speed;
            this.maxVel.y = Math.max(this.speed, this.fallSpeed);
            this.addAnim('idle', 0.1, [19]);
            this.addAnim('falling', 0.1, [12,13,14,15,16,17]);
            this.addAnim('climbing', 0.1, [6,7,8,9,10,11]);
            this.addAnim('running', 0.1, [0,1,2,3,4,5]);
        },

        update: function() {

            // Handle input. ie 'left', 'up', etc.
            if(this.input === null) this.stop();
            else this.handle_movement_input(this.input);

            // Disable gravity when over a ladder or on a bridge.
            if(this.over_ladder || this.over_bridge) this.gravityFactor = 0;
            else this.gravityFactor = this.defaultGravityFactor;

            // Do not update climbing animation while still.
            if(this.climbing && this.input !== 'up' && this.input !== 'down')
                this.currentAnim.timer.pause();
            if(this.climbing && (this.input === 'up' || this.input === 'down'))
                this.currentAnim.timer.unpause();

            this.parent();

            this.after_movement_and_collision();

            // Must receive input again for it to count.
            this.input = null;

            // Handle animations.
            if(!this.standing && this.anims.falling) this.currentAnim = this.anims.falling;
            else if(this.climbing && this.anims.climbing) this.currentAnim = this.anims.climbing;
            else if(this.vel.x !== 0 && this.anims.running) {
                this.currentAnim = this.anims.running;
                this.currentAnim.flip.x = (this.vel.x > 0 ? true : false);
            } else if(this.anims.idle) {
                this.currentAnim = this.anims.idle;
                this.currentAnim.flip.x = !this.is_facing_left();
            }
        },

        handleMovementTrace: function( res ) {
            this.parent(res);
            if(this.over_ladder || this.over_bridge) this.standing = true;
        },

        after_movement_and_collision: function() {
            // Handle smoothly getting off ladder.
            if(this.climbing && this.input === 'down' && this.at_bottom_of_ladder()) {
                this.climbing = false;
            } else if(this.climbing && this.input === 'up' && this.at_top_of_ladder()) {
                this.pos.y = this.ladder.pos.y - this.size.y;
                this.vel.y = 0;
                this.climbing = false;
            }

            // If position has changed, recalculate if over ladder.
            if(this.last.x !== this.pos.x || this.last.y !== this.pos.y) {
                this.check_for_ladder();
                this.check_for_bridge();
            }

            // Handle landing on top of a ladder, and stopping immediately!
            // Just landed on a ladder.
            if(this.possible_land_on_entity && this.standing && this.over_ladder) this.pos.y = this.ladder.pos.y - this.size.y;
            // Just landed on a bridge occupied by an enemy.
            else if(this.possible_land_on_entity && this.standing && this.over_bridge && this.bridge_that_entity_is_over.enemy) this.pos.y = this.bridge_that_entity_is_over.pos.y - this.size.y;
            // Flag possible ladder landing situation.
            this.flag_possible_land_on_entity();
        },

        flag_possible_land_on_entity: function() {
            this.possible_land_on_entity = !this.standing;
        },

        check_for_ladder: function() {
            this.ladder = null;
            this.over_ladder = false;
            this.size.y += 1; // because 1px under still counts
            var ladders = ig.game.getEntitiesByType(EntityLadder);
            for(var i=0; i<ladders.length; i++) {
                if(this.touches(ladders[i])) {
                    this.ladder = ladders[i];
                    this.over_ladder = true;
                    break;
                }
            }
            this.size.y -= 1;
        },

        check_for_bridge: function() {
            this.over_bridge = false;
            this.size.y += 1; // because 1px under still counts
            var bridges = ig.game.getEntitiesByType(EntityBridge);
            for(var i=0; i<bridges.length; i++) {
                // Touching bridge and an enemy is there and it's not this entity.
                if(this.touches(bridges[i]) && bridges[i].enemy && bridges[i].enemy !== this) {
                    this.over_bridge = true;
                    this.bridge_that_entity_is_over = bridges[i];
                    break;
                }
            }
            this.size.y -= 1;
        },

        at_top_of_ladder: function() {
            return this.pos.y + this.size.y - 2 <= this.ladder.pos.y;
        },

        at_bottom_of_ladder: function() {
            var velocity = { x: 0, y: 1 };
            var result = ig.game.collisionMap.trace(this.pos.x, this.pos.y, velocity.x, velocity.y, this.size.x, this.size.y);
            var stopped_by_floor = result.collision.y;
            var out_of_ladder = this.pos.y >= this.ladder.pos.y + this.ladder.size.y;
            return stopped_by_floor || out_of_ladder;
        },

        mount_ladder: function() {
            this.pos.x = this.ladder.pos.x + this.ladder.size.x/2 - this.size.x/2; // align player over ladder
            this.vel.x = 0; // prevents possible case of not being centered
            this.climbing = true;
        },

        jump_off_ladder: function(side) {
            this.stop();
            if(!this.can_move_in_direction(side)) return;
            this.pos.x += ig.game.collisionMap.tilesize * (side === 'left' ? -1 : 1);
            this.climbing = false;
            // Need to know there is no ladder to prevent walking on air.
            this.check_for_ladder();
        },

        set_velocity_by_direction: function(direction) {
            this.vel.x = 0;
            this.vel.y = 0;
            if(direction === 'left' || direction === 'right')
                this.vel.x = ( direction === 'left' ? -1 : 1 ) * this.speed;
            if(direction === 'up' || direction === 'down')
                this.vel.y = ( direction === 'up' ? -1 : 1 ) * this.speed;
        },

        handle_movement_input: function(direction) {
            // Getting off ladders.
            if ((direction === 'left' || direction === 'right') && this.climbing) this.jump_off_ladder(direction);
            // Running while atop ladders and on the ground.
            else if ((direction === 'left' || direction === 'right') && (this.standing || this.over_ladder)) this.set_velocity_by_direction(direction);
            // Prevent moving horizontally by walking over a ledge while holding key.
            else if ((direction === 'left' || direction === 'right') && !this.standing) this.stop();
            // Getting off at the top and the bottom of ladders.
            else if (this.climbing && ((direction === 'up' && this.at_top_of_ladder()) || (direction === 'down' && this.at_bottom_of_ladder()))) this.climbing = false;
            // Moving up and down ladders.
            else if ((direction === 'up' || direction === 'down') && this.climbing) this.set_velocity_by_direction(direction);
            // Getting on to ladders.
            else if ((direction === 'up' || direction === 'down') && this.over_ladder) this.mount_ladder();
            // Prevent moving horizontally by tapping left or right and then holding up or down.
            else if ((direction === 'up' || direction === 'down') && this.standing) this.stop();
        },

        stop: function() {
            this.vel.x = 0; // never move horizontally without input
            if(this.standing) this.vel.y = 0; // prevent climbing but allow falling
        },

        is_facing_left: function() {
            if(this.anims.running) return this.anims.running.flip.x === false;
            else return false;
        },

        can_move_in_direction: function(direction) {
            var velocity = { x: 0, y: 0 };
            // Left and Right check a few pixels further than immediately beside,
            // because when on a ladder, entity is a few pixels from edge of tile.
            if     (direction === 'left')  velocity.x = -1 * (this.offset.x + 1);
            else if(direction === 'right') velocity.x =  1 * (this.offset.x + 1);
            else if(direction === 'up')    velocity.y = -1;
            else if(direction === 'down')  velocity.y =  1;
            var result = ig.game.collisionMap.trace(this.pos.x, this.pos.y, velocity.x, velocity.y, this.size.x, this.size.y);
            if(direction === 'left' || direction === 'right') return !result.collision.x;
            else return !result.collision.y;
        }

    });

});

1 decade ago by Jon123

Thanks a lot Joncom, I will let you know if I run into any problems!

1 decade ago by stahlmanDesign

I created a ladder plugin that is pretty easy to implement without changing a lot of your code.

here is a working demo
source

You have to add your own climbing animation and bind up and down keys
Page 1 of 1
« first « previous next › last »