DevLog: Creating a Platformer Pt.2

DAY 1 (Cont.)

I’ve had quite a bit of positive feedback in response to this devlog! I’m super happy about that and would love to post updates ‘somewhat’ daily. I say ‘somewhat’ because I am bound to miss a day here and there, BUT I’d love to make this a regular thing!

I greatly appreciate the shares, retweets, likes, etc!

Helpful Scripts

I’d like to continue by covering a few auxiliary scripts that I use often and highly recommend. The first comes from Matt Thorsen (Untitled Story, Ogmo, Towerfall, and more). He implemented it in a simple platformer engine that he created for Game Maker called, The Grandma Engine. The script simply shifts one value, towards another, by a specific amount.

/// Approach(start, end, shift);

if (argument0 < argument1)
    return min(argument0 + argument2, argument1); 
else
    return max(argument0 - argument2, argument1);

This script is often used to cut down on repetitive code. For example, it can be used for acceleration and friction without having separate cases for each movement direction.

Other useful scripts include code that is used many, many times within single code blocks. A common one being a check to see if the player is standing on solid ground.

/// OnGround();

return place_meeting(x, y + 1, oParSolid);

Note: The three backslashes are used to create information that is compatible with Game Maker’s intellisense. When you start to type the name of a function, you can use this feature to auto-complete the statement or remind yourself of the parameters.

oParEntity

This object is used as a parent for all moving objects that will need to collide with solid objects or react to other moving objects. I will gradually be adding to this throughout this to development, but we can start with the basics.

For example, we know that these objects move. We will save their velocity values as well as check whether or not they are grounded.

oParEntity 'create event'
// Velocity
vx = 0;
vy = 0;

onGround = OnGround();

At the beginning of each frame, we’ll reset the grounded state.

oParEntity 'begin step event'
onGround = OnGround();

At the end of each frame, we’ll check for collision with static blocks and simple slopes. I won’t go into this here, but you can read about the collision in another article, HERE.

Get Things Moving

My number one goal at this point is to get things playable! The player can now collide with objects, but the sprite isn’t moving around yet. Let’s start to tackle this next!

oPlayer 'create event'
// Inherit oParEntity variables
event_inherited();


// Movement ///////////////////////////////////////////////////////////////////

// Multiplier
m = 1.0;

groundAccel = 1.0  * m;
groundFric  = 1.9  * m;
airAccel    = 0.75 * m;
airFric     = 0.1  * m;
vxMax       = 6.5  * m;
vyMax       = 10.0 * m;
jumpHeight  = 8.0  * m;
gravNorm    = 0.5  * m;
gravSlide   = 0.25 * m; 

clingTime   = 4.0 * m;

// Misc ///////////////////////////////////////////////////////////////////////

// Relative collision checks
cLeft  = place_meeting(x - 1, y, oParSolid);
cRight = place_meeting(x + 1, y, oParSolid);

// Common calculation
sqrt2 = sqrt(2);

I tried to add comments here so that I don’t have to go into it too much, but I’ll mention a few things. When getting the initial player physics down, you may feel that the ratios from value to value are good (for example, the jump arc may look nice), but overall, it feels too slow… In this case you could adjust the value ‘m’ to adjust the overall speed of the object. Also, you may want to save the value of a heavy operation like common square-roots or other expensive math functions.

For now, I’d like to stick to the basics. Horizontal movement, variable jumping (holding jump key to jump higher), and wall jumping. Below is the ‘step event’ that I came up with.

oPlayer 'step event'
// Input //////////////////////////////////////////////////////////////////////

var kLeft, kRight, kUp, kDown, kJump, kJumpRelease, tempAccel, tempFric;

kLeft        = keyboard_check(vk_left);
kRight       = keyboard_check(vk_right);
kUp          = keyboard_check(vk_up);
kDown        = keyboard_check(vk_down);

kJump        = keyboard_check_pressed(ord('Z'));
kJumpRelease = keyboard_check_released(ord('Z'));

// Movement ///////////////////////////////////////////////////////////////////

// Apply the correct form of acceleration and friction
if (onGround) {
    tempAccel = groundAccel;
    tempFric  = groundFric;
} else {
    tempAccel = airAccel;
    tempFric  = airFric;
}

// Reset wall cling
if ((!cRight && !cLeft) || onGround) {
    canStick = true;
    sticking = false;
}   

// Cling to wall
if (((kRight && cLeft) || (kLeft && cRight)) && canStick && !onGround) {
    alarm[0] = clingTime;
    sticking = true; 
    canStick = false;       
}

// Handle gravity
if (!onGround) {
    if ((cLeft || cRight) && vy >= 0) {
        // Wall slide
        vy = Approach(vy, vyMax, gravSlide);
    } else {
        // Fall normally
        vy = Approach(vy, vyMax, gravNorm);
    }
}

// Left 
if (kLeft && !kRight && !sticking) {
    // Apply acceleration left
    if (vx > 0)
        vx = Approach(vx, 0, tempFric);   
    vx = Approach(vx, -vxMax, tempAccel);
}

// Right 
if (kRight && !kLeft && !sticking) {
    // Apply acceleration right
    if (vx < 0)
        vx = Approach(vx, 0, tempFric);   
    vx = Approach(vx, vxMax, tempAccel);
}

// Friction
if (!kRight && !kLeft)
    vx = Approach(vx, 0, tempFric); 
        
// Wall jump
if (kJump && cLeft && !onGround) {
    if (kLeft) {
        vy = -jumpHeight * 1.1;
        vx =  jumpHeight * .75;
    } else {
        vy = -jumpHeight * 1.1;
        vx =  vxMax;
    }  
}

if (kJump && cRight && !onGround) {
    if (kRight) {
        vy = -jumpHeight * 1.1;
        vx = -jumpHeight * .75;
    } else {
        vy = -jumpHeight * 1.1;
        vx = -vxMax;
    }  
}
 
// Jump 
if (kJump) { 
    if (onGround)
        vy = -jumpHeight;
    // Variable jumping
} else if (kJumpRelease) { 
    if (vy < 0)
        vy *= 0.25;
}

Note: This code block is a work in progress and is a first iteration of something that will change several times throughout development.

To find out more about why I chose to do things the way I did, you can check out my article on ‘feel’ in fast-paced platformer games, HERE.

More to come!

-Z

DevLog: Creating a Platformer Pt.2

9 thoughts on “DevLog: Creating a Platformer Pt.2

  1. Platy says:

    Hey Zack,

    I just wanted to point something out. You never update cLeft and cRight in your Step Event which results in wall jumping and sticking not working. As you said this is work in progress and you might have left it out intentionally but since wall jumping can be seen in the gif at the end I’m guessing that it might be a mistake.

    Keep up the good work, SUPER III is looking amazing btw 😉

    Like

    1. Ah, you are correct. After finishing up the step event, the begin step event was edited to update cLeft and cRight after updating onGround. Thanks for pointing that out! 🙂

      Like

  2. Victor says:

    Hey, Zack! I’m in love with these tutorials! Nice work. 🙂 I just had one problem: I’m using the latest GMStudio, and for some weird reason when I type the first line of the Approach script (if (argument0 < argument1) I keep getting an error message which tells me that I'm missing a ")" at that exact line. Do you know what that could be?

    Like

    1. Thanks for the compliments 😀 I have no idea why that would cause an error. I’d have to see a screenshot of the complete script with error.

      Haven’t heard of any issues from anyone else, yet.

      Like

  3. Jon Björn says:

    Hi Zack! I have been tinkering with Game Maker for a while and as my ambitions grew, I got recommended to improve my basic movement codes by taking your example. I start a new project and try to replicate your code for collision and from creating a platformer p1 and p2.

    I follow what you write step by step, arm myself with an Approach script, OnGround and make the entity parent to my player object. At this point the code is identical 100% to this blog, even down to object names. I run the game and I’m able to go left and right(slippery though), up and down slopes but for some reason I’m unable to jump.
    I am stumped, have looked into everything multiple times and can’t spot where I go wrong. I figure the OnGround script might not be read properly so I just replace all “onGround” commands in the player step with place_meeting(x,y+1,oParSolid). This allows me to jump, but the slipperyness is gone and I can’t walljump.

    I wouldn’t post here to ask unless I had spent hours trying to figure it out and now my curiosity is greater than ever 🙂

    Like

      1. Myturtleoreo says:

        Never mind I got it to work, but I can’t wall jump? That is the thing I wanted to try the most, everything else works fine though.

        Liked by 1 person

Leave a comment