Thursday, August 7, 2014

Lesson Learned: A Bit More On Collision

DISCLAIMER:  While I work on developing my first games, I will be making "Lesson Learned" posts.  All this post will be is me explaining code, so that I myself will better understand what I am learning.

As I mentioned in my previous post, I'm going to spend some time on GameMaker's tutorial of "Break Out", which focuses on teaching the coding language of GameMaker as opposed to their Drag and Drop system.  Since applying code will be so much more useful in making my platformer,  this tutorial will be pretty important in this learning experience.

Right off the bat, the tutorial asks you to make some changes to the "Collision Mask" of the sprite we will use for the ball in the game.  Opening up the collision mask properties gives us a few interesting options, but what we are primarily focusing on is the shape of the mask.  All this really does is give you an area of pixels that define at what points the sprite will be considered "colliding" with other sprites.  You are given 4 options, "precise", "rectangle", "ellipse", and "diamond".  Since we are supposed to be using an actual ball for this sprite, the tutorial recommends using the ellipse.  However, for whatever reason I opted to use a sprite of Bowser, I'll apply the "precise" collision mask and see what happens.

Next, we're building the collision for the "bat" object, the bar that the player controls to deflect the ball against the bricks.  There is some familiar code that is applied to he object when the left arrow key is pressed:

if place_meeting(x-5,y,obj_wall) == false
{
x -= 5;
}

Here we see the "place_meeting" applied again.  Now, by my understanding,  what is going on here is that the code observes the x coordinate of the bat.  The actual speed of the bat will probably end up being 5, so it observes the pixel space to the left of the object 5 pixels at a time.  As long as there is no wall object within those 5 pixels (indicated by the "== false" condition), then the object will move to the left another 5 pixels (where x -=5 comes into play, remember that the plane starts in the top left corner in the room at (0,0), so moving to the left of the plane means a negative x value).

The exact same thing is done with the right arrow key, only some values are flipped to imply traveling in a positive direction of the plane:

if place_meeting(x+5,y,obj_wall) == false
{
x += 5;

}

Now I've sort of skipped ahead at this point, as this post is mainly focusing on collision.  After applying some code concerning the creation and movement of the ball object itself, we get into creating collision events for the ball.

First is collision with our invisible wall, which only uses a single line of code:

move_bounce_all(true);

It seems that GameMaker has its own built in physics for bouncing.  Apparently, this sets the ball to have "precise" collision with the wall.  I'm not sure how this works, how to change it, or what it truly does, but if I figure it out, I will update this little blurb of the post.

Next we create a collision instance for the ball with the brick object:

move_bounce_all(true);

with (other) instance_destroy();

This does the same exact thing as the collision with the wall, however now the ball will destroy whatever it collides with(excluding the wall, yet INCLUDING THE BAT, we'll fix that in a moment).

Now for the collision with our player controlled bat:

move_bounce_all(true);
var dir;
dir = point_direction(other.x, other.y, x, y);
motion_add(dir, 5);

speed = 5;

Now... this is going to take some going over.  We have our familiar first line of code, however, everything that follows focuses on the position of the bat and how it will affect the direction of the bounce of the ball.  What the function "point_direction" does is establish a vector (if you recall from Physics 101) relative between the direction the ball is currently travelling (with its x and y coordinates) and the position of the bat (the bat represented by other.x and other.y).  Once that angle is set, the direction and speed is actually set, as in the ball will start moving at the angle we established, with the "motion_add" function at a speed of 5.  Since motion_add affects the speed of the ball relatively, we had to set the speed again in the last line to ensure the ball bounces back at the same speed.

I guess this technically doesn't concern the concept of collision itself, but it is still a necessary thing to understand for future projects.

Now, at this point, I have a functioning game of breakout.  The next couple of bits of tutorial cover primarily general game play elements, such as lives and score keeping.  While I'll code these into the game, I wont go over those concepts too much in this post.

The final bit I will cover, however, fine tunes and smooths out the physics we have already established.  As the game plays now, it's functional, but it has bugs.  For example, the ball (or in my case Bowser), can get stuck in the bat if it is hit at just the right angle:



With this in mind, GameMaker introduces Scripts.   First, we are creating the following function, which we will apply to all other objects in case Bowser manages to get stuck elsewhere:
while (place_meeting(x, y, argument0))
{
x += lengthdir_x(1, argument1);
y += lengthdir_y(1, argument1);

}

This script observes the object that is being collided into and will scoot the instance along 1 pixel at a time until it senses that there is no collision with the object.  The direction the object will then travel is passed in as an argument (into argument 1).  We apply this to the ball object in every collision event.

To further understand exactly what is going on in this script, I had to dive further into the manual.  The biggest tool in this script is the "lengthdir" function.  The first argument is "len" or the length away from the origin of your object, and argument1 is the direction in which you are pointing that pixel.  Added onto the current x and y coordinates, the object moves relatively.

First we apply this in collision with obj_wall:

var dir;
dir = direction - 180;
scr_move_out(other, dir);

move_bounce_all(true);

As you can see, the direction (argument1 in our new script) is set to be our current angle -180.  In the next line, we pass argument0 as other (indicating the object being collided into) and argument1 as the direction.  The length is already set to 1 pixel, so in case Bowser gets trapped in the wall object, the script will now search 1 pixel at a time until it finds an escape.  This is all added above our already set "precise bounce" function.

Similar code is added to the other collision objects as well, like the Bat:

var dir;
dir = point_direction(other.x, other.y, x, y);
scr_move_out(other, dir);
move_bounce_all(true);
motion_add(dir, 5);

speed = 5;

And the Bricks:

var dir;
dir = point_direction(other.x, other.y, x, y);
scr_move_out(other, dir);
move_bounce_all(true);
with (other) instance_destroy();

Upon pressing F5, I find that Bowser no longer becomes ensnared in that skinny prison of Goldenrod.

With that, I must get ready for my day job.  Next post I'll take a break from learning and apply what I've learned so far into a platformer.

-JWest

No comments:

Post a Comment