tagwolf
Member
GM Version: Studio 2
Target Platform: Windows
Download: see code below
Links: NA
Summary:
I've noticed pretty much no platformer tutorials account for tweaking control feel, input buffering, air control, etc. So I'm tossing it here hoping it will be of help to someone.
Features:
* Move input cancellation
* Pixel perfect collision avoidance and diagonal evaluation
* Ground braking and acceleration
* Air braking and acceleration
* Ledge buffering
* Jump input buffering
* Adjustable everything (speed, gravity, controls, etc etc.)
* Basic debug console info to help tweaking
* No gamemaker physics (all basic math, unless hspeed/vspeed count as gm physics. I might switch to x/y operators someday but it seems like a pain in the ass as you'd need to track current speed ongoing anyways so why not use the built-ins and kill 2 birds with one stone)
* Alt controls
Todo: gamepad inputs (this should work fine for digital assignments now. Might need to tweak analog left/right to account for deadzone. But should basically just need to be like 'if absolute (abs) value > 0 + deadzone then move total is 1 or -1')
Some refactoring can probably be done.
Potential Bug: See nothing is perfect . While the ledge buffer exception prevents most types of "wall jumping" is does not prevent all unless you add logic to differentiate a floor vs a wall. Basically you need to make the step event aware that the player is indeed over valid ground before allowing them to jump (while accounting for ledge and input buffers.. For many peoples games this probably wont matter. But if you have more intricate level design I'm just making you aware to account for it. I am still working on a "one size fits all" solution to this. I know I could just use different block types for floor vs walls. But I like a challenge. Definitely open to input on this.
Create 2 objects. obj_Player and obj_Block.
obj_Block is used for floor and walls, etc.
obj_Player
Create event
Step event
Target Platform: Windows
Download: see code below
Links: NA
Summary:
I've noticed pretty much no platformer tutorials account for tweaking control feel, input buffering, air control, etc. So I'm tossing it here hoping it will be of help to someone.
Features:
* Move input cancellation
* Pixel perfect collision avoidance and diagonal evaluation
* Ground braking and acceleration
* Air braking and acceleration
* Ledge buffering
* Jump input buffering
* Adjustable everything (speed, gravity, controls, etc etc.)
* Basic debug console info to help tweaking
* No gamemaker physics (all basic math, unless hspeed/vspeed count as gm physics. I might switch to x/y operators someday but it seems like a pain in the ass as you'd need to track current speed ongoing anyways so why not use the built-ins and kill 2 birds with one stone)
* Alt controls
Todo: gamepad inputs (this should work fine for digital assignments now. Might need to tweak analog left/right to account for deadzone. But should basically just need to be like 'if absolute (abs) value > 0 + deadzone then move total is 1 or -1')
Some refactoring can probably be done.
Potential Bug: See nothing is perfect . While the ledge buffer exception prevents most types of "wall jumping" is does not prevent all unless you add logic to differentiate a floor vs a wall. Basically you need to make the step event aware that the player is indeed over valid ground before allowing them to jump (while accounting for ledge and input buffers.. For many peoples games this probably wont matter. But if you have more intricate level design I'm just making you aware to account for it. I am still working on a "one size fits all" solution to this. I know I could just use different block types for floor vs walls. But I like a challenge. Definitely open to input on this.
Create 2 objects. obj_Player and obj_Block.
obj_Block is used for floor and walls, etc.
obj_Player
Create event
Code:
/// @description Init Vars
// Player Vars
move_rate = 6;
jump_rate = 4;
jump_buffer_count = 0;
jump_buffer = 10;
jump_ledge_buffer = 10;
accel_rate_ground = 0.3;
accel_rate_air = 0.2;
brake_rate_ground = 0.4;
brake_rate_air = 0.2;
gravity_vspeed = 8;
gravity_rate = 0.2;
// Controls
control_left = ord("A");
control_right = ord("D");
control_jump = vk_space;
// Alternate Controls
control_left_alt = vk_left;
control_right_alt = vk_right;
control_jump_alt = vk_up;
Code:
/// @description Control Player
// Get input
move_input_total = 0;
if keyboard_check(control_left) || keyboard_check(control_left_alt) { move_input_total -= 1; }
if keyboard_check(control_right) || keyboard_check(control_right_alt) { move_input_total += 1; }
// Jump input buffer
if keyboard_check_pressed(control_jump) || keyboard_check_pressed(control_jump_alt)
{
jump_buffer_count = 0;
}
// Check / increment jump buffer
if jump_buffer_count < jump_buffer
{
jump_buffer_count++;
}
// No move input, brake
// On ground
if place_meeting(x, y + 1, obj_Block)
{
if move_input_total == 0 || (hspeed * move_input_total < 0)
{
hspeed -= hspeed * brake_rate_ground;
}
}
// In air
if !place_meeting(x, y + 1, obj_Block)
{
if move_input_total == 0 || (hspeed * move_input_total < 0)
{
hspeed -= hspeed * brake_rate_air;
}
}
// Move player and clamp value to max
// On ground
if place_meeting(x, y + 1, obj_Block)
{
hspeed += move_input_total * accel_rate_ground;
}
// In air
if !place_meeting(x, y + 1, obj_Block)
{
hspeed += move_input_total * accel_rate_air;
}
// Limit speed to move_rate
hspeed = clamp(hspeed, -move_rate, move_rate);
// Gravity
if (vspeed < gravity_vspeed) || !place_meeting(x, y + 1, obj_Block)
{
vspeed += gravity_rate;
}
// Jump if on / close to ground
// Account for ledge buffer but prevent wall jumping
if (place_meeting(x + jump_ledge_buffer, y + 1, obj_Block) && jump_buffer_count < jump_buffer && !place_meeting(x + 1, y, obj_Block)) ||
(place_meeting(x - jump_ledge_buffer, y + 1, obj_Block) && jump_buffer_count < jump_buffer && !place_meeting(x - 1, y, obj_Block))
{
vspeed = 1 * -jump_rate;
}
// Collisions and stuck/overlap prevention
if (place_meeting(x + hspeed, y, obj_Block)) {
while (!place_meeting(x + sign(hspeed), y, obj_Block)) {
x += sign(hspeed);
}
hspeed = 0;
}
if (place_meeting(x, y + vspeed, obj_Block)) {
while (!place_meeting(x, y + sign(vspeed), obj_Block)) {
y += sign(vspeed);
}
vspeed = 0;
}
// Diagonal
if (place_meeting(x + hspeed, y + vspeed, obj_Block)) {
while (!place_meeting(x + sign(hspeed), y + sign(vspeed), obj_Block)) {
x += sign(hspeed);
y += sign(vspeed);
}
hspeed = 0;
vspeed = 0;
}
// Speed debug
show_debug_message("HInput: " + string(move_input_total));
show_debug_message("Jump Buffer Count: " + string(jump_buffer_count));
show_debug_message("HSpeed: " + string(hspeed));
show_debug_message("VSpeed: " + string(hspeed));
Last edited: