This is a dev log about creating a game called Newtius in GameMaker Studio 2 (Indie edition). Starting with entry one might make this easier to follow.
In entry 11 I lamented about control systems, my unsuccessful attempt to make a background using GML and did some brainstorming. I’m sure this entry will be much better.
In this post I’m going to explain a minor setback, speculate on a cause and go into a side thing I’ve started in the form of a Udemy course.
As always, the Official newtius project page on itch.io is up, featuring the last playable version. Which may or may not have sound since it’s HTML5.
Just a step
I decided to take a pause in the Udemy course I’ve been following and implement the control system from that into Newtius. Originally I wasn’t going to bother with this since the built-in event system seemed a lot more readable and “self documenting” (my prior dev log post has a screenshot if you would like an example). But since I liked how the control turned out from the course I put it over – copy/pasted – into Newtius.
I didn’t expect a straight copy/paste to work straight away but it apparently has. There may still be a few things I don’t understand about the code but at least 95 percent of it I think I do.
Okay let just go through section-by-section.
First is the ship object/step event.
Object Ship/player
Event: Step
// these are just the four directions
// the "ord" function just converts a string keyboard stroke
// to an ascii value the keyboard check direction function can
// read. Which seems obvious.
if keyboard_check_direct(ord("W")) {
goUp = -1; // zero is at the top of the screen so
} else { // -1 is "moving towards 0" or "the top"
goUp = 0;
}
if keyboard_check_direct(ord("S")) {
goDown = 1; // s is down
} else {
goDown = 0;
}
if keyboard_check_direct(ord("D")) {
goRight = 1; // d is right
} else {
goRight = 0;
}
if keyboard_check_direct(ord("A")) {
goLeft = -1; // zero is on the left. so -1 is moving towards 0
} else {
goLeft = 0;
}
I didn’t like this style because it’s running the big series of if/else sixty times a second which seems unnecessary. But since the course does it and later extends things further to create both an acceleration/deceleration affect as well as a “bounce” affect against the different walls I decided to use it anyway.
The next section of the same step event is making some calculations and setting some variable values.
Object: Ship/player
Event: Step – the rest of it
// this establishes some variables, goal being to detect when the
// player ship is in motion
difV = goUp + goDown;
difH = goRight + goLeft;
// use above variables to get an angle
// point direction documentation
// https://manual-en.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FMaths_And_Numbers%2FAngles_And_Distance%2Fpoint_direction.htm&rhsearch=point_direction&rhhlterm=point_direction
// start x and y coords and end x and y coords
// 0,0 being the current x/y of the this ship object (right?) and difH/difV being the angle of the
// ship
dir = point_direction( 0, 0, difH, difV );
// more informaiton on lengthdir
// https://manual-en.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FMaths_And_Numbers%2FAngles_And_Distance%2Flengthdir_x.htm&rhsearch=lengthdir_&rhhlterm=lengthdir_
if ( difV == 0 && difH == 0) {
isMoving = false;
} else {
isMoving = true
}
// short boolean. could easily if (isMoving == TRUE)
// but since we're cool we'll shorthand it
if isMoving {
// the various variables below are declared in the create event
// abs is to find a whole/int value
if ( abs(vX + lengthdir_x(acceleration, dir )) <= maxVelocity) {
vX += lengthdir_x(acceleration, dir);
}
if ( abs(vY + lengthdir_y(acceleration, dir)) <= maxVelocity ) {
vY += lengthdir_y(acceleration, dir);
}
} else {
// when "isMoving" is false
// since drag is defined as less than 1 (0.9) this will "slowdown"
// the ship x and y speed will slowly count down to 0 the longer
// no keys are being pressed
vX *= drag;
vY *= drag;
}
// all this variable declaring is happening 60 times a second...
// ship movement
prevX = x;
prevY = y;
x += vX;
y += vY;
The above may or may not be that easy to follow. I think some shorthand is being used that the instructor of the course didn’t go into. Well he did go into it technically. Just not really the shorthand part, more the “remember this from high school physics” part.
The line
dir = point_direction( 0, 0, difH, difV );
for instance. There’s only one direction to point in. The ship doesn’t point any other direction. Well as the diffH and diffV variables values approach 0 – since they’re set and re-set 60 times a second – point_direction will get closer and closer to (0,0,0,0). So ya, I do kind of understand it. Not necessarily why it’s done that way other than it’s a clever use of that function. But I probably understand what it’s doing.
Okay re-looking at the code just now I think I do understand all of it: it’s just a clever way to give some relative values and have them decrement themselves back to down zero in something of an indirect way. It’s just confusing.
For the variables, lets put in the create event.
Object: Ship/player
Event: Create
spd = 6;
goUp = 0;
goDown = 0;
goLeft = 0;
goRight = 0;
isMoving = false;
acceleration = 0.95;
vX = 0;
vY = 0;
maxVelocity = 10;
drag = 0.9;
Hopefully the Step event code makes more sense after seeing these variables. The “drag” for instance clarifies that the variables in the Step event are multiplying themselves by slightly less than one which “drags” that value back down to 0. That’s not the intended use of “drag” in this context, but it works just as well I think. The similar with the acceleration variable.
Next is something I had never really done before: create an external script. I mean literally right click in the right hand side column in GMS2 and “create new script”. Use that to create a function then call said function from an event.
There was a change to GML sometime in 2021 that changed how these scripts worked. When I first put put in the code the function didn’t seem to be calling. So I was trying to read this blog post about this new of implementing functions in script files. And then it mysteriously started working when I ran the project even though I’m fairly certain I didn’t do anything.
I’ll just move on and not worry about it. Both the script file and function are called screenBounce. And it’s called from the End Step event.
Object: Ship/player (external script)
Event: End Step.
// Script assets have changed for v2.3.0 see
// https://help.yoyogames.com/hc/en-us/articles/360005277377 for more information
// section 3/room boundries video
/// @function screenBounce ( damper );
/// @param { real }
// called if the "after step" event
function screenBounce(argument0) {
// sprite origin is at middle/center
var halfWidth = sprite_get_width(sprite_index) / 2;
var halfHeight = sprite_get_height(sprite_index) / 2;
var damper = argument0;
if (x > room_width - halfWidth || x < 0 + halfWidth ) {
// offsetting half of spritewidth
x = prevX;
// the "bounce of the edge" section
// try 1.5 instead of damper for hilarity to ensue
vX = -vX * damper ;
//show_message("made it to x = prevx.");
}
if (y > room_height - halfHeight || y < 0 + halfHeight ) {
// offsetting half of spritewidth
y = prevY;
vY = -vY * damper;
}
}
Seems unnecessary, but here is End Step directly:
Object: Ship/Player
Event End Step
// This is it. This is it for End Step.
screenBounce(0.3);
This screenBounce function is run in the end step event so it that it will always run after the step event.
This screenBounce function is supposed to be checking for the edges of the screen and resetting the x and/or y coordinate accordingly.
And it works for the Udemy course project but not entirely right for Newitus: on only the left edge half the ship goes off screen. The other three edges it works fine. Feels like I’m back on one of the first dev log entries again.
You might have noticed the line with y = prevY;. That line is setting that y coordinate, set at the bottom of the main step event, to the prevY value (and the same for prevX). In other words detect when out of bounds and set the the coordinate back to inside bounds. Also, take into account the width of the sprite involved.
(later)
Actually I just realized why it’s doing what it is: I had my ship for Newtius with portions of it including transparencies. The bullet comes out of the “origin point” which means I could seethe bullet going through the ship before being fired outwards. So I changed the origin point to the “right center” instead of “middle center”. This way I can’t see the bullet before it fires. Simple solution on account of I’m a genius. Except in this case the code assume the origin is middle/center.
So I could either a) change the origin point to “middle center” or b) change the statement
sprite_get_width(sprite_index)
to be something like sprite_get_width(sprite_index + 17) since it’s a 16×16 sprite. I think that would achieve the same effect. But I’m going to re-set the origin point. Not like I’m going to use a sprite with included transparencies forever, right? ….RIGHT??
I made the change to the point-of-origin in GMS2 and indeed the ship does step on the left side now.
Reference links:
- GML Manual: Paths
- GML Manual: generically all-about-layers
- GML Manual: About background layers specifically
- The Udemy course I kept mentioning
The latest version/progress of Newtius can always be found at my GitHub repo: https://github.com/tildesarecool/newtius
One thought on “Dev Log Entry 12: Control System Stepping”