Tag: platformer

  • Retro Platformer tutorial part 4 – Sliding

    Now that Mega Woman can jump, run, shoot and die, we’ll continue adding to her arsenal of moves by giving her the ability to slide!

    Here’s the steps we’ll go through to do that:

    • Modify the level to have a low ceiling to slide under
    • Create a sprite with a different collision setting to be applied as a mask
    • Add controls in the NORMAL state to enter a SLIDE state
    • Add the new SLIDE state

    Modify the level

    Open the room “rm_level_template”. Add some blue wall blocks to create a small gap we want our hero to be able to slide under.

    Something like this:


    Create the sprite with the low stance collision mask

    Here’s one way to do it:

    • Right-click on “spritesheet_megawoman” and select “Duplicate” (or select the sprite and press Ctrl+D
    • Rename the new sprite to “spr_megawoman_slide
    • Open the new sprite
    • Find the sliding frame (frame 7). Delete all other frames.
    • Open the Collision Mask section of the sprite and change the Top value to 14

    The new sprite should look like that:

    Note: You don’t really need the sliding frame. The mask could be an empty 32px by 32px sprite with the Collision Mask value defined above but I find it useful to have the sliding sprite to set up the mask.

    Add the controls in the NORMAL state

    In Mega Woman’s NORMAL state block, find this code:

    // CONTROLS JUMP
    if((blocked_down || coyote > 0) && input.A_pressed)
    	vsp = -jump_spd;

    We want our hero to slide when we press the jumping button while holding the down button. To allow for both jumping and sliding, we’ll change the code above to this:

    // CONTROLS JUMP / SLIDE
    if((blocked_down || coyote > 0) && input.A_pressed)
    {
    	if(input.down)
    	{
    		mask_index = spr_megawoman_slide;
    		set_bounds();
    		become(STATES.SLIDE);
    	}
    	else
    	{
    		vsp = -jump_spd;
    	}
    }

    The first condition stays the same (the hero has to either be on ground or have remaining coyote time to be able to jump or slide). But then we add a condition to check if the down button is held or not.

    If it is held down, we do three things:

    • Change the mask_index to our new spr_megawoman_slide sprite
    • Call “set_bounds()” (a State object function) to update the bounding box of our hero object (This is important or tile collisions won’t consider the new mask)
    • Instantly change the state to STATES.SLIDE

    If the down button is NOT held down, the hero jumps normally.

    Now all we have to do is build the new SLIDE state.

    Creating the SLIDE state

    In Mega Woman’s Step event, under the STATES.DIE block we created previously, add this:

    case STATES.SLIDE:
    			
    	if(blocked_left || blocked_right)
    		become(STATES.NORMAL);
    		
    	state_play("slide");
    	state_pause(24);
    	state_pause_until(!is_under_ceiling());
    	state_set_mask(spritesheet_megawoman);
    	state_become(STATES.NORMAL);
    			
    	if(input.left_pressed)	hdir = -1;
    	if(input.right_pressed)	hdir = 1;
    	hsp = spd * 2 * hdir;
    			
    	if(!blocked_down)
    	{
    		mask_index = spritesheet_megawoman;
    		set_bounds();
    		become(STATES.NORMAL);
    	}
    	
    	break;

    Step by step explanations

    Let’s look at this state’s code step by step:

    if(blocked_left || blocked_right)
    	become(STATES.NORMAL);

    The part above checks if the hero is blocked on either side (meaning he is touching a wall) in which case he instantly turns back to NORMAL state. This way if the player slides into a wall the slide will quickly be interrupted.

    state_play("slide");
    state_pause(24);
    state_pause_until(!is_under_ceiling());
    state_set_mask(sprite_index);
    state_become(STATES.NORMAL);

    The state functions above start by having Mega Woman play her slide animation. Then it introduces a 24 steps pause (Again, all state_* functions are played in sequence, meaning it finishes dealing with one function before moving to the next one). This defines how long the slide move lasts. Customize this to your liking.

    Next we have another pause (state_pause_until()) that checks for a condition. The condition checked is a handy Super State Function that checks if any solid tile is close above the actor. This part makes sure our hero doesn’t stop her slide until the space above her head is clear, preventing her from standing up and getting stuck inside a wall.

    Once the slide is finished and the space above is clear, we set Mega Woman’s mask back to her normal mask and then send her back to her NORMAL state. Note that you don’t have to use “set_bounds()” when using “state_set_mask()” as this function takes care of calling “set_bounds()” itself.

    if(input.left_pressed)	hdir = -1;
    if(input.right_pressed)	hdir = 1;
    hsp = spd * 2 * hdir;

    Above we check if the left or right buttons have been pressed and change the horizontal direction (hdir) accordingly. Then we set the speed to be twice the normal walking speed and multiply it by the direction she’s facing.

    This part sets the sliding speed but also allows the player to change direction mid slide. This is not strictly necessary depending on what you want to achieve.

    if(!blocked_down)
    {
    	mask_index = spritesheet_megawoman;
    	set_bounds();
    	become(STATES.NORMAL);
    }

    Lastly, this part checks if our hero has a floor under her feet. If at any point she finds herself above empty space she interrupts the slide and revert back to NORMAL state, causing her to fall.

    Conclusion

    So here’s another classic Mega Man move added to our game! I hope you enjoy this series. Next up we’ll look at how to create our own levels and eventually add a charge shot and other weapons.

    Stay tuned!

  • Retro Platformer tutorial Part 3 – Getting hurt and dying

    In Part 2 we went through:

    • creating a bullet by extending the o_projectile object
    • adding input to shoot said bullets
    • adding animations related to shooting

    Now that our hero can shoot, let’s make her receive damage!

    Adding a new HURT behavior

    Mega Woman only has one behavior so far (STATES.NORMAL).

    She can already receive damage as it is handled out of the box by projectile and enemy objects in Super State Engine. However, we need to manage what happens when she receives damage and to do this we need to create a new state.

    Actor objects change state when they are damaged. The state they go in is defined by hurt_state in their Variable Definitions and is set by default to be STATES.HURT.

    So we need to create that state:

    • Open o_megawoman’s Step event
    • Under the case STATES.NORMAL block (after the break), add the new HURT state block:
    case STATES.HURT:
    
        break;
    

    In this state, we’ll be checking her hit points to see if she should die or not and then script a simple behavior showing her being hurt.

    Scripting the hurt animation

    Add the parts in bold to the STATES.HURT block:

    case STATES.HURT:
    
        state_play("hurt");
        state_set_speed(1*damage_dir_h,0);
        state_pause(20);
        state_become(STATES.NORMAL);
    
        break;

    Explanations

    In Super State Engine, state_* functions are played in the order they are written. This is handy to script animations, cutscenes or complex behaviors. Let’s see what happens in this sequence:

    • The first line plays the “hurt” animation (defined in Mega Woman’s Create event in part 1).
    • The second line sets her speed to 1 in the direction from which she’s taken damage (1 * damage_dir_h). The value of damage_dir_h (1 or -1) has been set by the enemy or projectile that damaged the actor.
    • The third line simply adds a 20 steps pause. These three lines together mean that our hero will play the hurt animation while moving at a constant speed of 1 in the direction of the damage she received for 20 steps.
    • Once this pause / delay has passed, she moves to the fourth line which sends her back to her STATES.NORMAL state.

    Checking hit points

    Add this in the STATES.HURT block:

    case STATES.HURT:
    
        if(hp <= 0)
            become(STATES.DIE);
    
        state_play("hurt");
        state_set_speed(1*damage_dir_h,0);
        state_pause(20);
        state_become(STATES.NORMAL);
    
        break;

    Explanations

    What this does is when our hero receives damage (and thus enters her STATES.HURT state), she checks how many hit points (hp) she has left and if it’s equal to or less than 0 she instantly goes to her STATES.DIE state instead (we’ll add that soon).

    All o_actor objects (which our o_hero object is a child of) have a hp variable. In the Create event of the object, hp is set to hp_max so it starts with full health. By default hp_max is set to 1 but can be customized in the object’s Variable Definitions.


    Adding a dying state

    Upon the hero dying we’ll show a small explosion animation, wait for a short time then restart the level. Let’s start by creating the explosion effect.

    • Import and set up the sprite
      • From the Mega Woman asset pack, drag “spr_fx_explosion_quick_strip6.png” into your project
      • Open the new sprite
      • Remove “_strip6” from its name
      • Set the origin to “middle center”
      • Set the animation speed (Fps) to 24
    • Create the effect object
      • Create a new object named “fx_explosion
      • Set its sprite to “spr_fx_explosion_quick
      • Set its parent to “o_fx

    Once the FX object is created, add this code to Mega Woman’s Step event after the STATES.HURT block we created previously:

    case STATES.DIE:
    
        state_set_var("visible", false);
        state_create_instance(fx_explosion,x,y,depth);
        state_pause(60);
        state_create_instance(o_transition,0,0,0,{t_room:rm_level_restart});
    
        break;

    Explanations

    This creates the die state we need when the hero’s hit points reaches 0 or below. What this does is:

    • Set the hero object to be invisible
    • Create the fx_explosion at the hero’s position
    • Wait for 60 steps (one second)
    • Create a transition object with “rm_level_restart” as the target room

    The o_fx object (parent of the fx_explosion object we created) is a very simple object that simply waits for its animation to reach the last frame before destroying itself. It offers a quick way to add FX animations to your game to juice it up.

    The o_transition object is also very handy. For now it only has a single animation but I might add more in the future. I invite you to modify it to your liking! It has a “progress” variable that gets up from 0 to 100. When it reaches 100, it send the game to the target room then the “progress” value gets back down from 100 to 0. You can get creative and use this value to make your own animations!

    The room “rm_level_restart” is a very simple room. In its Creation code it launches the level_restart() function which essentially clears the level before sending the game back to “rm_level_launch“, starting the selected level again from the start (or saved checkpoint).

    Conclusion

    So now Mega Woman can run, jump, shoot, get hurt and die, in which case the level is restarted.

    If you have not changed the demo level that comes with importing Super State Engine into your project, you can test what happens when Mega Woman touches the little enemy (that I like to call a “gamboo”). You should also be able to kill the gamboo by shooting at it!

    Now would be a good time to try to change things. Change Mega Woman’s hit points, speed, jump speed. Maybe try to change her hurt behavior or create new animations effects. Have fun!

    In the next part, we’ll add a sliding move.

  • Retro Platformer tutorial part 2 – Shooting bullets!

    In part 1 of this series, we went through:

    • Setting up a new project
    • Creating a hero character
    • Coding her running and jumping behavior
    • Adding some animations

    In part 2, we will create a bullet object, write the code to shoot bullets on a button press and add some shooting animations for the hero character.

    Creating a bullet object

    • Take the “spr_bullet_buster.png” file from Mega Woman’s asset pack and drop it into you Game Maker project
    • Set the origin to “middle center”
    • Create a new Object and call it “o_bullet_blaster”
    • Set its sprite to “spr_bullet_buster”
    • Set its Parent to “o_projectile”
    • Open the Object’s Variable Definitions and turn off “flag_hurt_hero” (see below)

    While you’re there, you can also customize the bullet’s speed by changing the spd value. Make it 3 for now. You can change it to your liking later.


    Shooting bullets

    Open the Mega Woman object’s Step event and add this code under the jumping part:

    if(input.B_pressed && instance_number(o_bullet_blaster) < 3)
    {
        instance_create_depth(x+6*hdir,y,depth,o_bullet_blaster,{hdir:hdir});
    }

    What this does is when the “B” key is pressed (the gamepad’s B key is linked to the “X” key on the keyboard ) and there’s less than three blaster bullets on screen, it will create a new blaster bullet. Note: the “o_projectile” objects destroys themselves when they get out of the current View. Check the Outside View 0 event for details.

    The {hdir:hdir} part at the end of the instance_create_depth passes the horizontal direction of the hero object to the bullet so that the bullet moves in the same direction the hero is facing.

    Setting a minimum delay between bullet shots

    Let’s say we want to implement a minimum delay between each shot to prevent the player from spamming the shoot button too quickly.

    Add these variables to the Create event:

    shoot = 0;
    shoot_delay = 10;

    Next go the the Step event and add the bold parts to the shooting code:

    if(input.B_pressed && shoot == 0 && instance_number(o_bullet_blaster) < 3)
    {
        shoot = shoot_delay;
        instance_create_depth(x+6*hdir,y,depth,o_bullet_blaster,{hdir:hdir});
    }
    
    if(shoot > 0)		shoot--;

    This adds a condition to shooting the bullet (shoot == 0) and set the shoot variable to the shoot_delay value when the button is pressed. This value is decreased by 1 with each step until it reaches 0.

    This essentially create a 10 steps minimum delay between each fired shot. You can customize this by changing the value of shoot_delay.

    Shooting animations

    Open Mega Woman’s Create event and add these lines to set up the animations we need:

    create_anim("idle_shoot",6);
    create_anim("walk_shoot",16,21);
    create_anim("jump_shoot",15);

    We also need a few variables to manage how long the animations are used. Add this too to the Create event:

    shoot_posture = 0;
    shoot_posture_delay = 30;

    Now let’s implement these variables into the shooting code by adding the parts in bold:

    if(input.B_pressed && shoot == 0 && instance_number(o_bullet_blaster) < 3)
    {
        shoot = shoot_delay;
        shoot_posture = shoot_posture_delay;
        instance_create_depth(x+6*hdir,y,depth,o_bullet_blaster,{hdir:hdir});
    }
    
    if(shoot > 0)		shoot--;
    if(shoot_posture > 0)	shoot_posture--;

    Just like with shoot and shoot_delay, the new code above set shoot_posture to the shoot_posture_delay when the fire button is pressed and the value of shoot_posture is then decreased by 1 every step until it reaches 0.

    But how do we use this to manage our animations? Let’s change the animation management part in the Step event (inside the STATES.NORMAL state):

    // MANAGE ANIMATIONS
    if(blocked_down)
    {
        if(shoot_posture > 0)
        {
            if(hsp == 0) play("idle_shoot");
            else         play("walk_shoot");
        }
        else
        {
            if(hsp == 0) play("idle");
            else         play("walk");
        }
    }
    else
    {
        if(shoot_posture > 0)    play("jump_shoot");
        else                     play("jump");
    }

    Again the animations are divided between “walk” and “idle” when the hero is on the ground (blocked_down) and “jump” when he is not.

    On top of that we check if the shoot_posture is above 0 or not to decide if we should use the shooting animations or the regular ones. This will cause our hero to display her shooting animations (with her arm outstretched) for 30 steps after pressing the shoot button. You can customize the delay by changing the value of shoot_posture_delay in the Create event.

    Summary: Mega Woman’s code so far

    Create event

    event_inherited();
    
    create_anim("idle",0,5);
    create_anim("slide",7);
    create_anim("walk",8,13);
    create_anim("jump",14);
    create_anim("hurt",44);
    
    create_anim("idle_shoot",6);
    create_anim("walk_shoot",16,21);
    create_anim("jump_shoot",15);
    
    shoot = 0;
    shoot_delay = 10;
    
    shoot_posture = 0;
    shoot_posture_delay = 30;

    Step event

    event_inherited();
    
    if(do_step)
    {
        switch state
        {
    	case STATES.NORMAL:
    	    hsp = (input.right - input.left) * spd;
    	    if(hsp != 0)
    	        hdir = sign(hsp);
    
    	    // CONTROLS JUMP
    	    if((blocked_down || coyote > 0) && input.A_pressed)
    		vsp = -jump_spd;
    
    	    // INTERUPT JUMP
    	    if(vsp < 0 && !input.A)
    		vsp = 0;
    
    	    if(input.B_pressed && shoot == 0 && instance_number(o_bullet_blaster) < 3)
    	    {
    		shoot = shoot_delay;
    		shoot_posture = shoot_posture_delay;
    	        instance_create_depth(x+6*hdir, y, depth, o_bullet_blaster, {hdir:hdir});
    	    }
    
    	    if(shoot > 0)		shoot--;
    	    if(shoot_posture > 0)	shoot_posture--;
    
    
    	    // MANAGE ANIMATIONS
    	    if(blocked_down)
    	    {
    		if(shoot_posture > 0)
    		{
    	    	   if(hsp == 0)	play("idle_shoot");
    		   else		play("walk_shoot");
    		}
    		else
    		{
    		   if(hsp == 0)	play("idle");
    		   else		play("walk");
    		}
    	    }
    	    else
    	    {
    		if(shoot_posture > 0)	play("jump_shoot");
    		else			play("jump");
    	    }
    			
    	    break;
        }
    }

    Conclusion

    So now Mega Woman can run, jump and shoot bullets! In this part we’ve built a very basic projectile. You can see that the o_projectile object took care of everything without us having to code any behavior.

    The o_projectile object has many customizable variables to change the way it behaves including its speed, damage, piercing through enemies, making it persistent (not destroyed by colliding with a target), adding an effect when it hits or is destroyed and making it affected by gravity. All of that can be done just by changing the settings in the Variable Definitions of your child objects. Check the Super State Engine documentation to learn more.

    We can also code different or more complex behaviors for our projectiles by writing in the their Step events. There are not limits to the possibilities! This will be covered in a future post.

    Next up! Getting hurt and dying

    In part 3 of this series, we’ll make our hero object get hurt by enemies and handle dying.

  • Making a retro platformer with Game Maker and Super State Engine part 1‌

    Disclaimer: This tutorial series is using my Super State Engine Framework which is sold on my itch io page. This Framework has tons of tools to help build platformer games specifically but can also be used for other kind of games. The State object in particular is very useful to manage animations and writing sequence of actions to make, for example, writing cutscenes easier.

    Creating a new project

    • Create a new blank project in Game Maker.
    • Click Tools > Import Local Package
    • Select the latest SuperStateEngine package, click “Add All” then “Import”

    You now have a new project with all files from Super State Engine. In some case you might not need the whole framework but for the purpose of this project, we’ll import it all as most of it will be necessary. Next we start setting things up.

    • Go in the “Rooms” folder and delete the default “Room1” file
    • In the “Room Order“, make sure the “launcher” room is at the very top
    • Open the “Game Options” panel (the little gear icon)
    • Go to “Main Options > General” and activate Collision Compatibility Mode

    Create the hero

    If you test the project now, you get a working level with the default “Jack” character. Now let’s change things up a bit.

    • Take the image file “spritesheet_megawoman_strip58.png” and drop it into your project
    • Open the created sprite and remove “_strip58” at the end of the name or rename it to your liking
    • Set the origin to “middle center”
    • Set the Fps to 12
    • Set the collision mask to these values
      • Left : 8
      • Right : 23
      • Top : 3
      • Bottom : 29

    This should look like the image below. Since the file has “_strip58” at the end of it’s name, Game Maker automatically spliced the image into animation frames, which is a very handy trick!

    Now that the sprite is ready, we will create a new “hero” object.

    • Create a new Object (either click the (+) at the top of the Asset Browser or do a Right-Click > Create > Object
    • Open the new created object
    • Change its name to “o_megawoman”
    • Set the sprite to be “spritesheet_megawoman”
    • Open the “Parent” panel and set the Parent to “o_hero”
    • Make this object “Persistent”

    We now have a new “hero” object. We’ll add it to our game.

    • Open the Room “rm_level_launch”
    • Delete the object “o_jack” (the default hero object)
    • Drop your new “o_megawoman” object in the room

    The new hero object “o_megawoman” is now correctly placed in the game but it does not have any set behavior so it can’t be controlled. We’ll get to that right away!

    Coding Mega Woman’s behavior

    That’s when we really get into Super State Engine’s features! We’ll start by setting up some animations in the Create event and then code the hero’s behavior in the Step event.

    Setting up some animations

    • Open the “o_megawoman” object
    • Right-click the Create event and select “Inherit Event”
    • Under the “event_inherited();” call, write these lines:
    create_anim("idle",0,5);
    create_anim("slide",7);
    create_anim("walk",8,13);
    create_anim("jump",14);
    create_anim("hurt",44);

    create_anim() function explained

    The create_anim function (which can be used with any child of the o_state_object) creates a new anim, gives it a name that is used to reference it and defines which frames of the set sprite it uses.

    Let’s look at the “walk” animation we set up above:

    create_anim("walk",8,13);

    To use the walk animation, you would call it with either

    play("walk"); or state_play("walk");

    The animation will run from frame 8 to frame 13. You can go into the object’s sprite sheet to look those frames to get an idea of how it works.

    If you only set one frame value, the animation will be static (single frame). If you set two frame values, it will run animation out of all the frames between and including those values.

    There are more values you can pass to the create_anim function. The fourth value is “loop” and is expecting a Boolean. By default it is set to true, which makes the animation run in a loop indefinitely. If you set it to false, the animation will stop at the last frame.

    The fifth passed value is the playing speed. By default it is set to 1. Increase this if you want the animation to play faster than the set framerate of the sprite, or decrease it to have it play slower. Play around with it to see how it works.

    Mega Woman’s behavior

    • In the object “o_mega_woman”, right-click the Step Event and select “Inherit Event
    • Open the Step event and after the event_inherited() call, write this:
    if(do_step)
    {
        switch state
        {
    
        }
    }

    The “do_step” value is managed in every State object’s Begin Step event and is used for pausing / unpausing the game. We don’t have to worry about that for now.

    State objects have a “state” value keeping track of which state they are in. This value accepts any number but is typically used with the enum STATES.

    Let’s write our hero’s first state by adding the code below in the switch block. We’ll start with basic movement:

    case STATES.NORMAL:
        hsp = (input.right - input.left) * spd;
        if(hsp != 0)
            hdir = sign(hsp);
        break;

    Explanations

    • hsp = horizontal speed. We check left and right input with the input object and multiply it by the object’s speed
    • hdir = horizontal direction. If an input is given (hsp != 0), we set the hdir to either -1 (left) or 1 (right)
    • break. Marks the end of a case block in a switch statement.

    Mega Woman can now go left and right when pressing the arrow keys and she turns to face the direction she’s going in.

    Adding jumping

    Add the part in bold below into your STATES.NORMAL block:

    case STATES.NORMAL:
        hsp = (input.right - input.left) * spd;
        if(hsp != 0)
            hdir = sign(hsp);
    
        // CONTROLS JUMP
        if((blocked_down || coyote > 0) && input.A_pressed)
            vsp = -jump_spd;
    
        // INTERRUPT JUMP
        if(vsp < 0 && !input.A)
            vsp = 0;
    
        break;

    Explanations

    The first part checks if the hero is on the ground (blocked_down) or if the coyote value is above 0 (This basically implements coyote time, which means the hero can still jump a short time after he’s fallen off a cliff).

    If either of those are true, pressing the A key will set the vsp (vertical speed) to the negative value of the jump_spd.

    Values like spd, jump_spd and coyote time can be customized in the Variable Definitions panel of your object.

    There are many values than can be customized in this panel. Look at the “Super State Engine Documentation.pdf” document for more details.

    The second part interrupts the jump mid-air. If the vsp is negative (meaning the object is going up), releasing the A key will instantly set the vsp to 0, essentially stopping it from going higher and letting it fall back with gravity.

    Linking this to our animations

    So now our hero can move and jump but doesn’t play the animations we’ve set up previously. Let’s fix that by adding this code under the jumping parts:

    // MANAGE ANIMATIONS
    if(blocked_down)
    {
        if(hsp == 0) play("idle");
        else         play("walk");
    }
    else
    {
        play("jump");
    }

    Explanations

    If the hero is standing on something (blocked_down), she will either play her “idle” animation or her “walk” animation depending on her horizontal speed. Otherwise she will use her “jump” animation.

    Conclusion

    Now Mega Woman can run and jump, look in the direction she’s going and play the appropriate animations for these actions. Her Step event should look like this:

    event_inherited();
    
    if(do_step)
    {
        switch state
        {
            case STATES.NORMAL:
    	    hsp = (input.right - input.left) * spd;
    	    if(hsp != 0)
    	    hdir = sign(hsp);
    			
     	    // CONTROLS JUMP
    	    if((blocked_down || coyote > 0) && input.A_pressed)
    	        vsp = -jump_spd;
    			
    	    // INTERUPT JUMP
    	    if(vsp < 0 && !input.A)
    	        vsp = 0;
    			
    			
    	    // MANAGE ANIMATIONS
    	    if(blocked_down)
    	    {
    	        if(hsp == 0)	play("idle");
    		else		play("walk");
    	    }
    	    else
    	    {
    		play("jump");
    	    }
    			
    	    break;
        }
    }

    In part 2 of this series, we’ll add shooting bullets!