. . .WELCOME TO THE SAT COLLISION TUTORIAL BY ME, ENDESGA!
Let's kick this all off with a little backstory...
I've been programming for most of my life - ever since I was about 8-9, when my father showed me his ZX Spectrum. Now, I'm not your typical 24+ y.o. Professional Freelance programmer, but thanks to my father being a computer engineer who can program in HDL and C, in 10 years I went from "How do you make games?" to "I'm going to make this game, and I'm going to try to teach people as much as I know.", despite my father wanting me to program in Hardware (which is cool and all, but Game Development is a lot more fun for me!)
OKAY! So, now you know a wee bit about me, I'm going to explain as well as I can a system often not looked at. The Separating Axis Theorem, which can be used in collision - if done right, it can be incredibly efficient.
HOW DO WE USE SUCH A SYSTEM?!Well, before I begin, I'm going point out that I use GameMaker. I've programmed in C, Python, Unity, Java, and GML has got to be my absolute favourite. It's powerful, fast, and super efficient. 'nuf said.
Let's begin.We're first going to cover what would happen if the object (or player) is going in the RIGHT direction. This is because, this system is the same in all directions, it just checks different sides.
MAY I ALSO POINT OUT, THIS SYSTEM WORKS FOR PLATFORM GAMES AND TOP-DOWN!THE THEORY: For this explanation to work, you need to understand that the yellow markers at the top left of the player (red box) and solid object (blue filled box), along with the corresponding purple markers; are the coordinates for the rectangle.
KEY: A RECTANGLE CAN BE DRAWN FROM ONLY 2 POINTS
Notice the green arrow and marker? This is where the player WILL BE ONCE IT MOVES IN THE NEXT STEP. Think "It's position, plus it's speed.", so in both cases, the player has not collided with the solid. BUT, in the right diagram, they will. This is because originally the player is closer to the solid (Think of the left diagram as the step before the right diagram!)
So, now you know what is going to happen in the next step, let's throw in some code!
/// CREATE EVENT
// let's get the width of the sprite, we can be precise with functions,
// but you can also just use "sprite_width*image_xscale" etc.
bw = sprite_get_width(sprite_index)*image_xscale;
bh = sprite_get_height(sprite_index)*image_yscale;
hbw = (bw/2); // half of the sizes, saves math later on
hbh = (bh/2);
//top left
x1 = (xstart-hbw);
y1 = (ystart-hbh);
//bottom right
x2 = (xstart+hbw);
y2 = (ystart+hbh);
// Those 2 sets of coordinates are VERY important!
// and make sure your Player's sprite's origin is centred!
co = noone; // collide object
co_d = 0; //collide distance
hsp = 0; // horizontal speed
vsp = 0; // vertical speed
As you can see, we now have x1 (left), y1 (top), x2 (right), and y2 (bottom). And using these variables, we can calculate a collision INDEPENDANT from pixels. Which is pretty neat
Now you need similar code for the solid objects:
///CREATE EVENT
// The origin is the top left this time, so co-ords are different!
x1 = xstart;
y1 = ystart;
x2 = xstart+(sprite_get_width(sprite_index)*image_xscale);
y2 = ystart+(sprite_get_height(sprite_index)*image_yscale);
AND NOW THE MAGIC BEGINS!
In the "Begin Step" event, we need to check if the player has pressed a key. Then we check for collisions, and adjust accordingly!
THINGS TO NOTE:- There are a lot of comments, so try to read over them!
- This is only for the RIGHT direction, so make sure you understand this, and I encourage you to try to do the rest!
///BEGIN STEP EVENT
//Make sure to read over this code, I'll try to explain as well as I can without getting in the way!
vsp += gravity_speed; // this is a hint as to how you would have gravity.
// If you're pressing right, accelerate. NOTE THAT THE MAX SPEED IS A DECIMAL NUMBER!
// The crazy decimal is just a proof of concept. You can have ANY speed you want.
if keyboard_check(vk_right) hsp = min(hsp+0.1, 2.4984705112)
else hsp = max(hsp-0.2, 0);
if hsp!=0 { // if we're moving
co = noone; // reset collide object
co_d = abs(hsp); // the max distance we can collide IS our speed
// IN THIS NEXT CODE, WE ARE FINDING THE CLOSEST COLLIDING OBJECT!
with(parSolid) { // rename "parSolid" to the Parent Solid object
if ((y1<other.y2) && (y2>other.y1) && ((x1<=(other.x2+other.hsp)) && (x1>=other.x2)) {
// Check if it's in colliding height, and check if it's left edge is less than or equal to the PLAYER'S RIGHT SIDE + it's speed.
// Also check if the solid's left edge is more than the player's right edge, to eliminate solids BEHIND the player having problems.
var _d = (x1-other.x2); // distance between the left edge and the player's right
if _d<other.co_d {other.co=id; other.co_d=_d;};
// If the distance is LESS THAN the collision distance, make the COLLIDE OBJECT = this Solid's ID
}
}
// if it has gone through ALL the possible solids, and there is one...
if co!=noone {
x2 = co.x1;
x1 = x2-bw;
hsp = 0;
// Make the player's RIGHT edge EQUAL to the collision object's LEFT edge.
// Then make the player's left edge equal the right edge - the width
// Make hsp = 0, stopping future movements and checks ("if hsp!=0" at the start)
}
else
{ // if we didn't collide with anything, move the player by it's speed.
x1 += hsp;
x2 += hsp;
};
};
THERE YOU HAVE IT! This may seem pretty intense for some, but the purpose of this is the fact that it is VERY reliable, and in fact a lot easier for the computer to handle rather than pixel-perfect collisions!
"What do I do next with this?"Use what I explained, to do the left, up, and down directions. Remember that if it's going left, the colliding edges will be flipped. Try to imagine what it will look like, and imagine how rectangles' axis overlap.
With this, you can do Jumpthrough platforms EASILY! Just make the JT Platforms only count as a solid if the player is going down (and not count all together for horizontal) - it's as easy as that.
You can also do slopes with some simple trigonometry, and using the "Y = mX + c" formula!
To do gravity and jumping, the code I use is similar to this:
//Before everything I add a gravity speed to vsp
vsp += 0.31;
//For jumping, all we gotta do is:
if keyboard_check(vk_space) vsp -= 6; // 6 can be whatever speed
//Simple stuff, but super useful!
I hope this helped, and if you have any questions, feel free to
message me on Twitter-ENDESGA