GML Useful Math For Game Development

Xor

@XorDev
GM Version: GMS 2
Target Platform
: ALL
Download: N/A
Links: https://www.reddit.com/r/gamemaker/comments/eyg2kf/useful_math_for_game_development/

Summary:
I was recently asked about what math/logic knowledge is needed for game development, so I've decided to outline it here and maybe you can use it too! I'll start simple and go from there. Stop at any point if you need a break!

Tutorial:
[1] Basic operators/arithmetic:
Make sure you're comfortable with the basic math operators (addition, subtraction, multiplication, division). You can do a lot with just these operators. Here are some examples:

Horizontal movement (with sprinting):
Code:
//keyboard_check returns 0 when a key is not pressed and 1 when it is. This can be used for a lot!
var _direction = keyboard_check(vk_right) - keyboard_check(vk_left); //-1 = left, 0 = stop, 1 = right.
var _speed = 1+keyboard_check(vk_shift); //speed is 1 or 2 when you hold shift.
x += _direction*_speed; //Move by adding the speed times the direction to x.
Smooth Approach:
Code:
var difference = mouse_x-x; //Difference from the current position to the mouse.
x += difference/10; //Move one-tenth of the way the mouse.

[2] More operators and simple functions:
Now you're ready for "mod" (%) and "div". div is used to divide something into a whole number so 5/2 = 2.5 while 5 div 2 = 2. The decimal/fraction part is ignored leaving us just with a whole number. modulo aka mod does the opposite of div. It returns the part that can't be divided into a whole number (remainder) so 5 mod 2 = 1 and 8 mod 3 = 2. Another way to think of mod is in a case of a mod b, no matter what "a" equals you subtract "b" enough times to make "a" less than "b". This is particularly useful when you want something to cycle. Here are some more examples:

Calculating minutes:
Code:
minutes = seconds div 60; //Calculate the number of minutes from "seconds".
Cycling through 24 hours:
Code:
//++a  will add 1 (aka increment) to a before any other operations happen.
hour = ++hour mod 24;//Increment the hour and reset it to 0 if it pass 24.
Also here are some useful functions to know.

  • sign(x) - returns the sign of the number (positive, negative, zero). Examples: sign(-5) == -1; sign(0) == 0; sign(0.0001) == 1;

  • abs(x) - returns the positive value of the number. Examples: abs(-5) == 5; abs(20) == 20;

  • round(x) - returns nearest whole number to x. Examples: round(0.5) == 1; round(10) == 10; round(-0.7) == -1;

  • floor(x) - returns a whole number less than or equal to x. A bit similar to x div 1. Examples: floor(0.5) == 0; floor(10) == 10; floor(-0.7) == -1;

  • ceil(x) - returns a whole number greater than or equal to x, opposite of floor. Examples: ceil(0.5) = 1; ceil(10) == 10; ceil(-0.7) == 0;

  • frac(x) - returns the fraction part of a number, a bit like the inverse of floor (for positive numbers: frac(x) = x-floor(x)). Examples: frac(0.5) == 0.5; frac(10) == 0; frac(-0.7) == -0.7;
Here's a quick example to try.
Grid snapping:
Code:
var grid_size = 64; // Size of grid to snap position to.
x = round(mouse_x/grid_size)*grid_size; //Set x to the snapped mouse position.
y = round(mouse_y/grid_size)*grid_size; //Set y to the snapped mouse position.
Now try replacing the rounds with floors and then try ceils. round will snap the position centered while floor would favor the top left and ceil will favor the bottom right.

[3] Useful functions:
If you have made it this far I suspect you've learned enough that we can spend less time on the rest and skim over them. Here's a list:

  • max(x,y..), min(x,y..) - max returns the largest of a set of numbers while min returns the smallest. Examples: max(-5,0) == 0; max(4,3,2) == 4; min(-1,2,3) == -1;

  • clamp(x,min,max) - returns x as long as it's between the min and max. Examples: clamp(1,0,2) == 1; clamp(5,0,2) == 2;

  • mean(x,y..) - returns average all numbers. Examples: mean(1,2) == 3/2; mean(5,5,5) == 15/3; mean(-2,2,4,6) == 10/4;

  • lerp(x,y,amount) - returns a mix of x and y depend on amount you choose (0 = 0% of x and 100% y, 0.5 = 50% of x and 50% y, etc). Examples: lerp(1,2,0) == 1; lerp(1,2,0.5) == 1.5; lerp(1,2,2) == 3;
I'm omitting functions that you likely won't use (ln,cos,sin,tan,etc), but you can always read up on them later. Finally here are some advanced functions that I still use often:

  • pow(x,y) - x to the power of y or x (aka base) multiplied by itself y times (aka exponent). Examples: power(2,3) == 2*2*2; power(4,2) == 4*4; power(16,0.5) == 4;

  • sqr(x), sqrt(x) - sqr = power(x,2) while sqrt = power(x,1/2). Examples: sqr(4) == 16; sqrt(25) == 5;

  • logn(x,y) - returns the exponent of "y" at base "x" or in other words the inverse of power. Examples: logn(2,16) = 4; logn(3,9) = 2;

  • log2(x), log10(x) - same as logn(2,x) and logn(10,x) respectively. Examples: log2(16) = 4; log10(1000) = 3;

  • lengthdir_x(len,dir), lengthdir_y(len,dir) - respectively returns the x and y of a point on a circle at radius "len" and the angle "dir". Examples: lengthdir_x(4,0) == 4; lengthdir_y(2,270) == 2;

  • point_distance(x1,y1,x2,y2) - returns the distance between 2 points. Examples: point_distance(0,0,4,0) == 4; point_distance(0,0,4,4) == sqrt(32);

  • angle_difference(dir1,dir2) - returns the signed difference between two angles. Examples: angle_difference(90,0) == 90; angle_difference(0,450) == -90;


Phew! These functions are hard to summarize. If you need more info you can find it in the manual. Now you're ready to see why this matters with some advanced examples.

[4] Advanced applications:
Here some examples to show how this may be used:

Computing the max AA:
Code:
var exponent = floor(log2(display_aa));//Floor the highest exponent.
var max_aa = pow(2,exponent);//Return the exponent to a base 2.

display_reset(max_aa,1);
//Note: this can be simplified with bit-wise operations.
point_distance_3d deconstructed:
Code:
//Calculate the difference between points.
var diff_x = x1-x2;
var diff_y = y1-y2;
var diff_z = z1-z2;

//Pythagorean theorem
distance = sqrt(sqr(diff_x)+sqr(diff_y)+sqr(diff_z));
point_direction_3d:
Code:
theta = point_direction(x1,y1,x2,y2); //First angle for xy plane

var length = point_distance(x1,y1,x2,y2);//Reduce xy to 1-dimension.
var diff_z = z1-z2;//Calculate the z difference.

phi = point_direction(0,0,length,diff_z);//Compute second/vertical angle.
//Note: this can be simplified with other trigonometry functions.
Jointed Arm:
Code:
//Initialize angles for each joint (make these dynamic for more fun).
joint_ang = [15,30,45];
joint_len = [48,32,16];

//Set the starting joint position.
joint_x[0] = x;
joint_y[0] = y;
//Compute the second joint position.
joint_x[1] = joint_x[0]+lengthdir_x(joint_len[0],joint_ang[0]);
joint_y[1] = joint_y[0]+lengthdir_y(joint_len[0],joint_ang[0]);
//Compute the third joint position.
joint_x[2] = joint_x[1]+lengthdir_x(joint_len[1],joint_ang[1]);
joint_y[2] = joint_y[1]+lengthdir_y(joint_len[1],joint_ang[1]);
//Compute the fourth joint position.
joint_x[3] = joint_x[2]+lengthdir_x(joint_len[2],joint_ang[2]);
joint_y[3] = joint_y[2]+lengthdir_y(joint_len[2],joint_ang[2]);
//Now sprites or lines connecting these points.
[5] Additional topics:
This is all I have the time for today, but it's also useful to learn more about the bitwise operations, trigonometry functions, and the other exponential functions. Here's a hint: dcos(ang)*len == lengthdir_x(len,ang) and dsin(ang)*len == -lengthdir_y(len,ang).

Anyway, I hope this answered some questions and gave you some insight into more advanced math!
 
Last edited:

NightFrost

Member
Very nice. Though I'm guessing in the jointed arm code
Code:
joint_y[0] = x;
you're wanting for it to receive y instead.
 
  • Like
Reactions: Xor

JeffJ

Member
As someone who's greatest pitfall in development is math (pretty big pitfall in game development, I know), this is excellent. Will definitely be coming back to this on a regular basis. Thank you!
 
Top