csanyk.com

video games, programming, the internet, and stuff

GameMaker Position and Motion Tutorial

Motion is critical to just about any video game. Nearly every game has moving things in it, and how they move is a vital part of the game. Learning how to program motion and control it effectively is one of the most important parts of a successful game. There are a number of possible approaches to handling position and movement. Learning how these work will help you make better games.

This isn’t absolutely everything there is to know about motion, but it’s a great overview to start with, and covers everything I’ve learned with respect to motion in GameMaker Studio. 

Position Variables:

x, y

The current x and y coordinates of the instance.

These are in reference to the absolute x and y coordinates in the room. The left-top corner of the room is (0,0), and the right-bottom corner of the room is (room_width, room_height).

Game Maker Room

Coordinates outside the room are valid locations — objects don’t automatically cease to exist or stop functioning just because they are outside the room. Indeed, they continue to exist and function, unless you code them otherwise, and of course they can come back inside the room.

Think of the room as the “stage” of a theater that the player views; the area outside the room is “off-stage”. Stuff still exists when it goes off-stage, and things can happen to that stuff. However, positions that lie outside the room will never be displayed in the game. The game will stop scrolling at the edge of a room, showing no space outside of it.

xprevious, yprevious

The former x and y coordinate of the instance, from the previous step. Comes in handy when you need to compare where an object is to where it was, or if you’re trying to handle collisions with high-speed objects (see below), or to “undo” a position change to the old position in the previous step.

xstart, ystart

The x and y coordinates of the instance when it was created. Useful if you need to return an object back to where it started from. You can also change the values to update the starting point (such as if the player reaches a new save point.)

GML move_ functions

There are a number of GML functions built in to GameMaker, whose names start with “move_” that are good to be aware of.

move_snap(hsnap, vsnap)

Moves the instance to the nearest position in the room, aligned to an invisible grid of “cells” of a size defined by the hsnap and vsnap arguments.

This is pretty easy to understand. Let’s say you want to base your game off of a grid of 32×32 pixel cells. You want your instances to align to this grid. If an instance is at a position such that instance.x and instance.y are both mulitples of 32, then it is aligned to the grid. If the instance is not aligned to the grid, this function will move the instance to the nearest location that is a grid intersection.

A common problem you may encounter with this function is if you have a grid within the room but that does not take up the entire room. Say you have a chess board in the room, but there is a border around the chess board. You want cells in the invisible grid to align with the squares of the chess board, but because of the border, there’s an offset because the border is not a width that aligns to the grid. To adjust, you simply need to offset the positions of the instance after calling move_snap:

move_snap(32, 32);
x += border_x; //border_x and border_y must be defined by the programmer.
y += border_y;

Keep in mind, too, that the instance’s sprite origin can confuse the location where the instance is being drawn. If the origin is centered, the instance will appear centered on the grid intersection (like a stone in Go) rather than centered inside the grid cell (like a chess piece).

move_snap with centered sprite, sprite with origin at (0,0)

The results from move_snap are based on the instance’s x and y, so how it ends up looking will depend on the instance’s sprite’s origin.

move_random(hsnap, vsnap)

The move_random() function moves the instance to a random location in the room that is an intersection on the invisible grid defined by the hsnap and vsnap arguments.

As with move_snap(), move_random() has the same issues to be aware of with grid offset and sprite offset.

move_towards_point(x, y, speed)

This function sets the speed and direction of the instance so that it will move towards the point (x, y) as defined by the first two arguments. It’s convenient in that the function figures out the direction to travel for you, based on the spatial relationship between the calling instance and the destination point.

move_contact_all(dir, maxdist), move_contact_solid(dir, maxdist)

Moves the calling instance in direction dir, up to maxdist pixels, or until the instance contacts another instance. This does not set a speed, so it is using the position assignment method to move the instance. If the moving instance does encounter an obstacle, it stops just short of triggering a collision. The move_contact_solid function works by checking for contact with objects objects that have the solid property set to true.

This is a very useful function for platformer games, where moving instances need to stop when they encounter a solid wall or platform object, without triggering a collision with it.

move_bounce_all(advanced), move_bounce_solid(advanced)

Calling this function causes the object to bounce when it encounters another instance (or solid instance) in the game. The instance will only bounce off of other instances if they have a sprite or mask index. The bounce function calculates the new direction and applies it to the instance.

If the advanced parameter is set to true, precise collision detection is used. This requires greater processing and can slow the game down if too many instances are using it. It does not make the angle of the bounce any more precise. You generally will not need to use precise collision checking.

Realistic bouncing is probably better done using the new Box2D physics. This move_bounce() is an older GML function that has been around in GameMaker since long before the new physics engine was added. You can still use this function if you don’t want to bother with the full physics system.

move_outside_all(direction, maxdist), move_outside_solid(direction, maxdist)

These two functions are the inverse of move_contact_all() and move_contact_solid(). They are intended for when two objects are in collision, and can be useful to resolve a collision event so that the collision event does not happen automatically again during the next step. They work by displacing the calling instance up to maxdist pixels away, in direction, until a collision condition is no longer present. If no value is provided for maxdist, GameMaker will assume a value of 1000 pixels.

The catch here is that the instance will move until it is outside of any collision condition, not just a collision with the other instance it is currently involved with. So if the move_outside function is triggered by a collision with one instance, and moving just outside of collision with the first instance would result in a collision with another instance, the calling object will continue to move until it is positioned outside of any and all collisions, or maxdist is reached. This is possibly not desirable, since you may need the collision with the next instance to be triggered as well. In this case, the best thing to do is set maxdist to a small value, such as the calling instance’s sprite offset.

The move_outside_solid() function can be useful to get an object out of collision condition with solid objects. One of the annoying things about solid objects is that when they overlap (collide) with an object, they prevent the object from moving, causing it to become stuck or embedded inside the solid object. An object with a speed and direction will not move through a Solid object, and if it is in collision with a Solid object, it cannot move by its speed and direction vector any longer. The only way to move it is by position assignment. The move_outside_solid() function does the necessary calculations to place the calling instance just outside of the collision mask of the Solid object.

A lot of GameMaker developers avoid using the Solid property entirely, opting instead to handle objects which they wish to behave as though they were solid using their own code.

move_wrap()

Mentioned here for completeness, but discussed in depth in the section Wrapping the Room.

Shameless plug: I’ve developed an extension called iMprOVE_WRAP which is more powerful and flexible than move_wrap(). It can be downloaded from the GameMaker Marketplace and Itch.io.

Vector variables:

In GameMaker, a motion vector is a speed (in pixels per step), combined with a direction. In mathematics, a vector can be added to a position to get a new position.  Two vectors can be added together to combine their motion. An understanding of Vectors can give you a very useful way to move instances around in GameMaker.

direction

The current direction of motion of the instance, in degrees. 0 = right/”east”. 90 = up/”north”. 180 = left/”west”. 270 = down/”south”.

direction model for GameMaker objects

Direction model for GameMaker objects

Note that if you’re using Box2D physics, your physics fixtures use a different model for direction, starting with 0° pointing up/”north”, as shown below:

Box2D physics direction model

Direction model for Box2D Physics

You’ll need to compensate for this if you ever need to convert a GM Object’s direction to a Box2D fixture’s direction:

GameMaker direction = Box2D direction + 90

Box2D direction = GameMaker direction – 90

Related to direction is image_angle, which is a built-in GML variable that is used to rotate the sprite belonging to an instance. If you only change an instance’s direction, it will move in that direction but the sprite will continue to be drawn with its orientation unchanged. If you want the sprite to rotate, you can set image_angle = direction. This is why it is usually best to draw your sprites so that they face right, so that image_angle = 0 looks correct for direction = 0.

speed

The speed in pixels per step in the current direction. Negative = moving “backward”; positive = moving “forward” with respect to the current direction.

hspeed

The horizontal component of the instance’s speed, in pixels/step. Negative = moving left; positive = moving right.

vspeed

The vertical component of the instance’s speed, in pixels/step. negative = moving up, positive = moving down.

Up being negative often seems counter-intuitive to new programmers, since “up” should be “higher”. So shouldn’t a high y correspond with a higher vertical position?  You might think so, but no. The reason for this is that computer displays start drawing every new frame at the top of the screen, and work their way down.  So they start at the 0th row of pixels, and count upward toward the bottom row of pixels. If this isn’t a natural concept, you’ll just have to get used to it. Just remember that the room coordinate system starts with its origin at top left, at (0,0), and counts upward to positions that are vertically down from the origin.

Tip: speed, hspeed, and vspeed

The values of speed/directionhspeed, and vspeed are interdependent, and are updated automatically by the game each step when one of their values changes.

For example, if you set and speed = 8 direction = 0, your instance will be moving to the right at a speed of 8px/step. If you look at the value of hspeed, it will also be 8, even though you never directly set hspeed.

This means that hspeed and vspeed and speed are always accurate to the actual motion of the object in the game. This is very useful.

For example, if your instance is not moving at an orthogonal angle (eg, 0, 90, 180, 270), and you need to know the horizontal or vertical component of motion for some reason, without hspeed and vspeed, in order to know its precise horizontal or vertical speed, you would need to perform trigonometry functions in order to isolate the horizontal and vertical components of the object’s motion vector. But, thanks to hspeed and vspeed, this is already done for you, “for free.”

One thing to note about this is that changes to hspeed or vspeed can change direction, but changes to absolute speed do not change direction.

To illustrate this, take an instance in your game moving at speed = 8, direction = 0. It is moving 8px/step to the right. To reverse its motion, you could use either speed = -speed, or direction += 180. In the first case, you’ve reversed speed but in the other you’ve reversed direction. Visually, the result is identical (unless the sprite’s image_angle is also updated, or you swap sprite_facing_right with sprite_facing_left).

If we watch the variables, we see that it’s possible to have negative speed, so that it’s possible to move “backward” at a negative speed, but that when hspeed or vspeed reverses, direction also reverses. An object with a negative hspeed will always have a direction between 90 and 270; positive hspeed, a direction between 0-90 or 270-360. And object with a negative vspeed will have a direction between 0-180; positive vspeed, between 180-360.

This can be easier to understand with the following demo:

Use the left/right arrow keys to increase/decrease hspeed. Up/Down arrows to increase/decrease vspeed. W/S to increase/decrease absolute speed, A/D to increase/decrease direction. Watch how the values relate to each other as they change.

gravity

In GameMaker, gravity works by creating acceleration in a given direction (270 == down) at a given rate. The gravity variable is the amount of speed that is added to the instance each step, while gravity_direction is the direction in which this speed is added.

Gravity is very useful as a component of jumping mechanics, or for ballistic trajectories of flying objects.

You can also use gravity for more novel purposes, such as to simulate the force of a strong wind pushing against objects, blowing them away.

Gravity can be a little tricky to get right. We think of gravity in the real world as a force which attracts objects with mass towards one another. In GameMaker, it is just a vector, a force which adds speed in a given direction. This vector is added to the instance each step, accelerating it, so even a little gravity can add up quickly and come to dominate the instance’s motion. The official documentation recommends using a small value, such as 0.2. Over time, the acceleration due to gravity will add up, leading to very high speeds, which can lead to problems with collision detection (see below). Often it’s desirable/necessary to cap an instance’s speed with a “terminal velocity” so speed doesn’t get too crazy. You can do this using the GML functions min(), max(), or clamp().

In GameMaker, gravity does not have a location that the instance is pulled towards — it does not attract an instance to a particular point in the room; it just adds speed in the direction set by the instance’s gravity_direction. (You can fake a gravity toward some object or point in the room, if you update gravity_direction each step, and use point_direction(x, y, attractor_x, attractor_y)

In the real world, we think of gravity as a force which affects everything equally, but in GameMaker, gravity is set on a per-instance basis. This means that you can set different forces and/or direction for each instance. This could be used, for example, to create MC Escher spaces in 2D where gravity works differently for each object, depending which surface they are standing on. Or for creating some objects that are affected by gravity while others are not.

You can set gravity to a negative value, which is equivalent to setting gravity in the opposite direction (gravity_direction += 180).

You cannot have multiple gravity forces acting on a single instance through the built-in GML gravity variable. You can, however, write your own “gravity” functions that work in the same way as gravity, by using motion_add() each step to add a little bit of speed and direction.

friction

Deceleration, slowing the instance, until speed reaches 0. Friction is useful for simulating slippery surfaces like ice, where an object shouldn’t stop immediately, or viscous surfaces, like mud, where constant effort is necessary in order to keep moving. Friction has no direction; by its nature it works in opposition to the present direction of the instance it is working on, by reducing its speed until it reaches 0.

You can set negative friction, which is essentially the same as setting gravity in a direction which constantly matches the instance’s direction.

Techniques

You can change the values of these functions in any mathematical way you can imagine. Understanding the math is important to getting the behavior you expected.

Position assignment:

x = n;
y = n;

Instance position “teleports” to (n, n). Note that despite moving from point A to point B, the position change has no effect on the instance’s speed.

Position shift

x += n;
y += n;

Instance is displaced by (n, n) pixels relative to current position. Again, this only affects the instance’s position, not its speed.

Direction assignment

direction = n; //sets direction used by motion
image_angle = n; //sets rotation angle of instance's sprite.

Instance instantaneously faces new direction.

You can, of course, also add/subtract degrees to direction, as well:

direction += n; // turns the direction of motion by n degrees
image_angle += n; //rotates the sprite by n degrees

Rotate toward

For automatic rotation:

Step event:

(If you want “chunky” turning, like a clicking mechanism, use this code in an alarm event instead of the Step event.)

if (abs(direction-new_direction) < turn) {
 direction = new_direction; } 
else { 
 if direction < new_direction { 
 direction += turn; } 
 else {
 direction -= turn; 
 }
}

Instance rotates by n degrees toward the new direction each step until it faces the new direction.

For controlled rotation, simply add or subtract from direction when the control event occurs. For example:

Key | <Left> event:

direction += turn;

Key | <Right> event:

direction -= turn;

Note: turn and new_direction are not built-in variables in GameMaker; you must create and assign values to them. The variable turn is the number of degrees per step that the instance is able to turn. new_direction is the goal direction that you want the instance to face when it is done rotating.

assign speed

speed = n;
hspeed = n;
vspeed = n;

speed(or hspeed, or vspeed) immediately changes to n.

shift speed

speed += n;
hspeed += n;
vspeed += n;

Addsn to the current value of speed (or hspeed, or vspeed).

Set speed and direction

speed = n;
direction = n;

or

motion_set(dir, spd);

This replaces whatever the former speed and direction was with the new values.

Add vector

motion_add(dir, spd);

Adds a vector to the existing motion, preserving the original speed and motion and combining their values with the new vector. Useful for simulating inertia.

Linear Acceleration with Terminal Velocity

In the Step event, or an input Event:

speed = min(max_speed, speed + accel);

Speed increases each step by accel pixels/step until max_speed is reached. Note max_speed and accel are not built-in variables, and must be created and assigned values in the program.

As you can see, most normal motion can be expressed through simple value assignment (the equals sign) or through addition or subtraction.

Other math is possible, of course. These numbers are just numbers, and you can perform any math on numbers. You can get some interesting effects using multiply, divide, exponents, square root, logarithms, trig functions, etc. in many different ways to modify position, speed, or direction.Oftentimes it’s useful to multiply a base speed by another number to get a range of relative speeds for walking, running, or whatever.

Linear interpolation and extrapolation with the lerp() function

One gml function that is very useful for movement is lerp(), the linear interpolation function. lerp(a, b, amt) returns a value between a and b that is interpolated by the amount (when amt is between 0 and 1). Lerp can be used in many ways, usually it’s best to lerp iteratively each step, so in the Step event, or in an Alarm loop:

lerp the position (x,y) coordinates of an instance, to smoothly approach a position:

x = lerp(x, destination_x, percent);

y = lerp(y, destination_y, percent);

lerp the speed to smoothly accelerate or decelerate to a target speed.

speed = lerp(speed, desired_speed, accl);

lerp direction, to smoothly turn towards a desired direction.

direction = lerp(direction, desired_direction, turn_rate);

You can even use lerp to extrapolate rather than interpolate, by using a value greater than 1 for the amt argument. The help file provides a cool example that you can use to predict the position of an instance one second in the future based on its current hspeed and vspeed:

future_x = lerp(x, x+hspeed, number_of_steps_into_the_future);

future_y = lerp(y, y+vspeed, number_of_steps_into_the_future);

Assuming the instance doesn’t change course, we can extrapolate into future by as many steps as we want.

This can be used to target where an object will be, assuming its speed and direction don’t change.

Other techniques

Find the distance or direction

GML provides a few functions that are useful for determining where things are, which can help you know where you want to go:

  • instance_nearest(x,y,obj): returns the id of the nearest instance of the named object class to the point named at (x,y) . If you call a parent object, the function will return the nearest object that is of the parent class, or any of its descendants.
  • instance_furthest(x,y,obj): returns the id of the most distant instance of the named object to the point named at (x,y).
  • distance_to_point(x,y): returns the distance from the bounding box of the current instance to (x,y). Note that there is a subtle, crucial difference between the bounding box and the x,y position of the calling object (normally a point somewhere within the bounding box of the object’s sprite).
  • distance_to_object(obj): returns the distance in pixels from the calling object to the nearest edge of the bounding box of the instance specified in argument0, or the nearest object specified if an object name is used instead of an instance. Again, there is a difference between the bounding box edge and the x,y position of the target instance.
  • point_direction(x1,y1,x2,y2): returns the direction (in degrees) from point (x1,y1) to (x2,y2).
  • point_distance(x1,y1,x2,y2): returns the distance (in pixels) from point (x1,y1) to (x2,y2).

Relative Positioning

An instance’s x and y variables always refer to the absolute coordinates within (or sometimes outside of) the room.

Oftentimes, though, it’s easier to think of a position as being relative to the position of some other instance. We don’t care where in the room something happens, we care that it happens exactly M pixels to the left and N pixels up from some object in the room, which might be anywhere.

It’s easy to use the object of interest’s position as a reference point, by calling instance.x and instance.y, and then using simple math to offset an amount of room pixels relative to the instance’s position.

Rather than using the instance’s x and y position, it’s also possible to use the bounding box of the instance’s sprite:

  • instance.bbox_top
  • instance.bbox_bottom
  • instance.bbox_left
  • instance.bbox_right

You can also use the sprite’s height and width:

  • instance.sprite_width
  • instance.sprite_height

Finally, you may want a position that is some distance from a point, in an arbitrary direction. To do this you can use trig functions;in GML, the trig functions all take the angle argument in radians, and direction is in degrees, so you have to convert direction to radians using degtorad(). [Update: this is changing with GM:S 1.3, which will introduce degrees-based trig functions.] As well, the image_angle variable is also degrees.

  • x - distance * sin(degtorad(direction))
  • y + distance * cos(degtorad(direction))

GM:S 1.3 on:

  • x - distance * dsin(direction);
  • y + distance * dcos(direction);

Actually, in GameMaker, there is a pair of functions that do this math for you. I included the trig functions method to expose the underlying math, and to make the example more relevant to other languages and frameworks, but if you’re using GameMaker, you can use these built-in functions instead:

  • lengthdir_x(length, direction)
  • lengthdir_y(length, direction)

When using the lengthdir functions, it expects the direction argument in degrees, so you don’t need to convert it to radians.

Sprite direction and Motion direction are independent

If you change an instance’s direction, it will move at speed pixels per step in the new direction. Note however that the instance’s sprite still points in the same orientation as before. Unless your sprite is completely symmetrical, this probably doesn’t look right.  To change the angle of the sprite, use the image_angle variable:

image_angle = direction;

This works fine if all you need is for your sprite to rotate to point the direction of the instance’s motion. It’s a great approach to take for top-down spaceship or car games.

Download: Sprite rotation with inertia demo.

This approach is not right for all games, though. For example, the original Legend of Zelda had separate views of each side of each character: front, back, left, right. These were not simple rotations of the same image, but rather were different images depicting the character from the front, side, side, or back.

There are a lot of ways to implement a 4-sprite system. You could use a single object, and just change which sprite is drawn based on the direction the object is facing. Or you could create a group of objects, each one representing the character facing a given direction. The four-object approach sounds like more work and more complexity, but it actually allows you to keep each object much simpler.

To start out, it’s optional, but handy to create constants to make your code more legible:

NORTH = 90
SOUTH = 270
EAST = 0
WEST = 180

Next, create all your sprites. You’ll need a North, South, East, and West version for each type of action or animation. So, this can be a lot more work to create all the graphics. Rotating a single image is a lot less work, but this approach can pay off due to the richer variety of graphics that you’ll end up with.

For a single-object solution, in the Draw event:

if direction == NORTH {draw_sprite(x,y,sprite_facing_north);}
if direction == SOUTH {draw_sprite(x,y,sprite_facing_south);}
if direction == EAST {draw_sprite(x,y,sprite_facing_east);}
if direction == WEST {draw_sprite(x,y,sprite_facing_west);}

Or, in the Step event:

if direction == NORTH {sprite_index = sprite_facing_north;}
if direction == SOUTH {sprite_index = sprite_facing_south;}
if direction == EAST {sprite_index = sprite_facing_east;}
if direction == WEST {sprite_index = sprite_facing_west;}

The above example is easy to understand, but not optimized. If you want to avoid running unnecessary code, you can nest the if statements, or use a switch statement.

Note that the above approach will check and re-assign the sprite every single step. Most likely, that’s way more often than is necessary. If you have a lot of instances to draw this way, it could hurt performance.

Really, you only need to switch out the sprite when the object’s direction changes. If we can know when the direction will change, then we can know when to switch the sprite. And knowing this is quite possible. A better approach is to note any points in your code where the instance’s direction changes, and perform the sprite change there.

For example, if the player moves NORTH, you can just put a line of code on the event that handles the control for moving NORTH:

sprite_index = sprite_facing_north;

If this is a player-controlled object, you can change the sprite in the Key Press event for keyboard-driven controls; this way the sprite assignment only happens in the first step when the key is pressed. You can set speed and direction here as well.

Or, if you’re using a position shift approach, you can use the regular Key event to handle the position-shift each step the key is held down. This is an easy way to get the object to stop when you release the key. Otherwise, you can use the Key Release event, to set speed = 0, or to set friction for a more gradual stop. Try each approach to gain an appreciation for how each one feels.

The “system of objects” approach is very similar to the “changing the sprite only when needed” approach, except that, instead of changing just the sprite, you change the entire instance, using the instance_change() function.

Why go through all that trouble? Because: it makes managing the rest of the object’s code much easier.

For example, say we have a character who is invulnerable on one side, like the Darknut enemy in Legend of Zelda, who is invulnerable to frontal attacks.Writing the collision event would be complicated if you had to first detect what the object’s facing is to know which side is the invulnerable side at the moment.

If we represent the character using four separate objects, one for each direction it can face, then you already know which is the invulnerable side, because that’s an immutable property of that object. object_Character_facing_left is always invulnerable from attacks coming from the left, and so on.

So with this approach, you only need to determine which side the attack came from, not which side the attack came from AND whether that side is the invulnerable side. This reduces the amount of work you needed to do to resolve the collision, roughly by half. That work reduction goes right into your game’s performance — collision handling is now twice as fast! The code is simpler and easier to understand and troubleshoot, as well.

Another example, if your controls are dependent upon your facing direction, it makes implementing this much simpler as well.

Say you have a simple 4-direction D-pad set up, the most basic movement scheme is simply: “up = up, down = down, left = left, right = right”. It doesn’t matter which direction the character is facing, it will always move the same direction as the D-pad input.

But for a more sophisticated game, you might have something like this going on: forward = attack stance, backward = defensive stance. Forward and Backward are not absolute directions — they are relative to the facing of the character. If the object is capable of facing in four directions, then depending on the present direction, any D-pad input could be “forward” or “backward” — or neither! If you try to create your character out of just one object, you’ll need complicated code for every controller input to determine which direction the character is facing, and what direction the input is in relative to that facing.

On the other hand, if you use different objects to represent the character in each of the four facing directions, the character’s facing is an immutable* property of the instance. This means you no longer need to check which direction it is facing before you can know whether the controller input is “forward” or “backward”. Again, if you don’t need to check this, it makes the control code faster.

Not only do you gain performance, you also gain ease of troubleshooting. It’s much harder to look at a complicated nested if statement and figure out why it’s not behaving the way you want it to, than it is to have to only code one branch of that statement, that is the only branch you really needed, and get that working right. It’s a code smell: if you see a complicated if statement with many branches to it, start thinking about whether you’re better off managing a group of objects instead, each object representing a different state in your complex if structure.

*”Immutable” literally means “cannot be changed.” By “immutable” I’m technically not being accurate. In GameMaker, there isn’t anything that prevents the value of direction from changing if you code it to do so. GameMaker doesn’t have a concept of private or readonly variables (at least, not user-defined private or readonly variables — there are some readonly variables built-in to the framework). But you can make pseudo-immutable variables if you assign the value once and only once in your code, e.g. in the Create event. Set direction = NORTH (or SOUTH, etc.) in the Create Event for each of your object_character_facing_[direction] and leave it alone. As long as you never change it, it’s as good as immutable.

Tip: Create a parent object for your group of facing objects. When you use functions that reference the object class, for example instance_nearest(object), for targeting purposes, check for the parent object — that way you don’t need to write complex code that checks for each of the child objects individually.

You don’t necessarily need to even put any code into the parent object itself. When I use parents in this way, I call that an “identity parent” since all I’m using the parent to do is to group the child objects and refer to any of them through its parent. The children inherit the parent object’s “identity”, even though the parent doesn’t pass on any other attributes or behaviors.

If need be, you can use the same approach for a 2D horizontal game, like a platformer or beat-em-up, only you just have to worry about facing LEFT and RIGHT. Or if you want to, you can create even more sprites and allow 8-directions or even 16 directions. It all depends on what the game design calls for, and how much work you’re willing to put in.

Independent facing direction and movement direction.

Let’s go back to the sprite rotation approach for a moment. As we saw, the facing of the sprite is not automatically the same as the direction of motion — you have to set image_angle that in order for it to happen. Say you want a more realistic spaceship game, where there is inertial motion. Your spaceship has forward firing thrusters which accelerate you when they are on, but you continue to move at your forward speed in the direction of thrust even after you turn them off. Furthermore, you have directional thrusters which can rotate your ship, but all they should do is make you spin — not change your main thrust vector. How do you make that?

Rather than setting image_angle = direction, you create a new variable, which we can call facing. When you hit the rotational thrusters, you don’t change direction, depending on your implementation, you can either change image_angle directly, or change facing, and update image_angle to match facing. And, when you fire the thrusters, you need to add a motion vector to the instance, in the direction of facing, rather than in the direction of motion:

motion_add(thrust, facing);

This preserves the inertia of the existing motion and adds the new thrust in the direction you’re currently facing.

Paths

A path is a pre-defined “track” of motion. This is useful for scripted motion, where an object needs to follow a specific, known route through the room. They’re easy to learn how to use, and quite useful, if limited in the sense that the path must be pre-determined prior to runtime. Note that just because the shape of a path is pre-determined, doesn’t mean that you can’t set the location of the path itself at a position that is defined during runtime. Paths can also be scaled. You can use them in a lot of ways.

Motion Planning

Motion planning is a more advanced way of allowing an instance to determine its course at runtime, without predefined paths. This is more complicated to implement, and deserves its own article, so it’s a bit beyond the scope of this one to discuss how to do it. I mention it for sake of completeness. There are tutorials available to learn motion planning if you want to look for one. I may end up writing one of my own someday.

Physics

New in GameMaker Studio is Box2D Physics. If you are using physics, those objects which use physics, should not use the “normal” direction and speed variables. Beyond that, covering how to use the new Physics systems is a large enough topic that it deserves its own post, and I will not attempt to cover it here.

Faking motion through sprite animation

I don’t recommend this technique if you can possibly avoid it, but sometimes it is useful. If you are having a hard time describing an object’s motion using math and programming, you can always fake it by creating a special sprite animation, in which the motion is depicted. When the special motion is called for, you can switch in the motion sprite, and when the animation is over, you can switch it back. Or, alternately, it may be easier to change the instance into a specialized object that does this special motion, and then change back to the regular object type. In some cases, it may be useful to simply loop the animation.

Using an oversized sprite with a generous amount of invisible whitespace, the object can “move around” inside of its bounding box through whatever animation techniques you can come up with, while its x and y and speed and direction all remain unchanged.

At the end of the animation sequence, most likely you’ll need to update the x and y of the instance to put it in its new position, then switch back to the regular sprite.

The major downside to this is that the motion is prescribed through several frames of animation, while all of the code you normally are used to writing executes in a single step. When you get used to thinking about your game in slices of time, and doing just a part of a job to get you a little closer to a desired end, it can be hard to figure out what to do during the “down time” while the instance is going through its animation.

Another downside is that the animation is “canned”, or pre-rendered motion, and cannot be modified once it is drawn. This makes it inflexible. These animations are also time-consuming to produce, and if you decide later to replace your sprites with better looking graphics, you’ll be stuck throwing out all that work and re-doing it.

Finally, it can be difficult to position the origins of the before, animation, and after sprites so that everything lines up to create a convincing illusion. It’s not that big of a deal to figure it out, but it usually takes a little trial and error before you get it looking just right.

Still, there are times when the technique can be quite handy.

Wrapping the room

Classic arcade games such as Asteroids and Pac Man were notable because they featured screens that wrapped around at the edges. When you reached the edge of the screen, you would disappear on one side, and reappear on the other.

In GameMaker, there’s a built-in action for doing this, but it’s somewhat tricky.

Intuitively, you might think that wrapping is simply a property that you could assign to a room. It isn’t the room that wraps, though — it’s the object. Rather, wrapping is applied to a object as its’ action in response to an event, usually the Outside Room event or Intersect Boundary event. This allows you to program some objects to wrap while others don’t, which may be useful if you don’t want everything to be able to wrap, but it also means you need to do a little extra work for every object that you do want to wrap.

One important thing to understand about the built-in GameMaker commands for handling the wrap functionality is that they only work if an instance has a non-zero speed. In other words, if you’re moving a the player by position shifting only, and it has no defined speed, the built-in functions that handle wrapping will not work. If you need to wrap a no-speed instance, you’ll need to write your own code to handle it, like this:

if x < 0 {x = room_width;}
if x > room_width {x = 0;}
if y < 0 {y = room_height;}
if y > room_height {y = 0;}

For instances that are truly moving — that is, which have a non-zero speed, it’s a bit easier to use the built-in functions.

The Drag and Drop action is called “Wrap Screen”. It lets you control whether the instance wraps horizontally, vertically, or both. Its GML equivalent, is a function called move_wrap(), adds a third argument, margin, which defines how much of the instance must be out of the room before the wrap is triggered. The default is that the instance wraps when it is entirely outside the room. The sprite does not wrap as soon as a pixel of it goes off the edge of the room. Instead, the edge of the room cuts off the drawing. If the object continues to move toward the outside of the room, once the object’s position is outside the room by margin pixels, the object suddenly wraps and appears on the other end of the room. This is jarring and sudden — you have nothing poking out on the other side to foreshadow the impending wrap, and the object started to go outside the room at one edge, disappears suddenly, and then suddenly appears partially outside the opposite edge of the room.

There is a way to improve this. First, in the object’s Draw event, you can add a little code to draw four extra copies of the object’s sprite. Draw them at the following coordinates:

<

p style=”padding-left: 30px;”>x, y //original
x - room_width, y //left
x + room_width, y //right
x, y - room_height //up
x, y + room_height //down

These extra images come into play when the object is overlapping the edge of the room. The “extra” image will overlap the border of the opposite side of the room, thus giving the appearance of the object visually overlapping at both sides of the room.

Another problem with objects that wrap is collision checking. You need to decide whether you need to perform collision detection at all five positions. This is not done automatically for you just because you’re drawing an extra four sprites. If you don’t check for collisions in the four extra locations manually, then only the “true” instance location will trigger collisions. This means that on the wrap side of the room, the instance won’t collide with any objects that overlap its sprite’s collision mask, until enough of the object comes into the room to trigger the wrap action. Thus, you could have a rather large sprite wrapping by a substantial amount onto the other side of the screen that is not triggering collisions.

If you do check for collisions, however, it means that your object has effectively five times the collision area. It takes 5x the processing time to perform collision checking. And, if there is a non-wrapping instance of some type sitting outside the room, your extra sprites can collide with it, which isn’t really fair. If all your collide-able objects wrap when they go outside of the room, this isn’t a problem, but if some do and others don’t, then it’s possible for the non-wrapping instances to collide with one of the extra images belonging to a wrapping instance at a false position. To guard against this you can perform additional checking to determine whether the collision is happening inside or outside of the room, and exit the collision event early if the non-wrapping instance is outside of the room. It’s a lot of extra work, and can harm performance, so unless it’s very important or your wrapping instance has a very large sprite, it’s perhaps best to leave it alone and not bother checking for collisions at the four false locations.

Finally, wrapping around the room will cause odd behavior with functions like point_direction, point_distance, distance_to_point, distance_to_object, and instance_nearest. This is because the game will only consider the straight-line distance between the instance’s actual location (x, y) and the other location — it doesn’t check across the boundary of the wrap to see if there’s a shortcut available by utilizing wrapping.

To fix this issue, you simply perform four extra checks, one for each of the positions of the “extra” sprites that you draw. Check the distance from each position, then use the min() function to pick whichever point is truly the shortest distance away. It’s easy when you see the solution, but not very intuitive to figure out if you’ve never done it before. Again, performing all these extra checks can mean a lot of additional overhead, and can affect game performance.

Fast Moving Instances and Collision Detection

If an instance moves very fast, it can be hard to get it to detect collisions with other objects correctly. This is because motion is an illusion. Like frames of a movie film reel, a video game consists of a series of discrete instants, called steps. Each step takes a fraction of a second. There’s nothing “between” these steps; an instance is in one position in Step 1, and a new position in Step 2, and does not move through intermediary positions. Rather, it’s new position is calculated and updated for each step. So it’s more like teleporting a short distance each fraction of a second than it is like continuous motion.

If the object’s speed is greater than the length of its collision mask, there can be problems, because collision checking will only detect a collision if it happens in the collision mask in its location in Step 1 and Step 2; it does not “connect the dots” between those two locations. If speed is very high, there will be a gap between these masks, and if something collidable exists in this gap, and does not touch the collision mask in step 1 or 2, no collision happens, and the object seems to “jump over” (or through) the object instead of colliding, even though the object should have had to pass through the object that it jumped over if it happened in real life.

With fast motion, a different style of collision detection is needed. You need to detect collisions in the region swept through by the collision mask of the object between each step. This is where xprevious and yprevious can be useful. GML functions collision_line() and collision_rectangle() can be used to approximate the swath of space that the instance would have swept through had its motion been continuous. Use this to check for more robust collision detection for fast moving objects.

What can you do when checking for collisions with TWO fast-moving objects, or, worse, MULTIPLE objects? This becomes increasingly difficult and tricky.

The best thing to do is reduce object speeds while increasing room_speed. This gives greater time-fidelity, and results in greater accuracy of the basic collision detection, and reduces the need for the more complicated approaches. An object moving at 50px/s in a room running at 30 fps appears to the player to be moving just as fast as an object moving 25px/s in a room running at 60fps.

Of course, if the computer is trying to run the game 60fps, it has has half as much time to calculate everything going on in each Step. So the performance of your code becomes all the more critical. You’ll need to be more vigilant about inefficient code and writing things to be more optimized. Recent versions of Game Maker have brought performance improvements with the runner, making it easier to achieve decent frame rates without sacrificing quality. But it’s still very important to write efficient code that doesn’t waste CPU time.

Multiple collisions are even harder to implement. The GML collision functions return only a single instance ID, which means that if there’s more than one object in that collision rectangle, the function will only return one of them. Which one? You can’t predict or control it. We can get all of them, though, but it takes a little creativity. A technique of temporarily moving each object out of the way, and then re-checking the collision, until all objects in the zone have been detected, and then moving everything back, is possible:

while !(collision_function() == noone) {
 //get id of the instance in collision
 //add id to array or data structure we use to track our objects involved in the collision
 //remember current location of id (store it in an array or data structure)
 //move id to temp location using the "jump to" technique
}

This code will check for collisions with the object, and as long as it keeps detecting collisions, it will move the object detected by the collision function to a temporary location, thereby removing it from the collision. Then, it will re-check the collision function to see if there are still any other instances returned.

Then, once you have identified all of the instances in collision for this Step, you’ll need to return them to their old location, back from the temporary location.

for each id in array or data structure {
 //move back to previous location using the "jump to" technique
 //do all the collision stuff
}

Because all of the temporary movement happens between steps, and the movement is undone after all the collisions are detected but before the main loop updates the on-screen positions, the temporary movement is never visible to the player. Since each instance’s speed and direction are never touched, the objects are not interrupted from their existing motion.

Because of the way the collision functions work, the instance with the lowest id is returned when a collision is detected. This means that you can’t rely on the function to tell you which collision should have happened “first” based on the direction of motion and the position of the objects. To determine this, you’ll need yet additional GML code, to compare the distances from each object in the collision zone to the “before” position of the fast-moving instance.

Obviously, if you are using a large collision zone, or have many active instances in the game, these methods can be “expensive” and negatively impact performance. Try to keep your collision handling loop as small as possible, or keep the number of simultaneous collisions to a minimum.

There are perhaps other techniques that you can come up with as well.

Moving the room around a stationary object

Sometimes, it’s better to create an illusion of motion by keeping one “reference” object (usually the player) still, and moving everything else around it. To do this, you need to use position shifts rather than setting speed/direction for the rest of the objects. The rest of the objects may need to have their own motion too, of course, so you can’t mess with their speed and direction as you set up your “reference” motion.

This is a clever technique used to simulate large, even infinite rooms. Stick one “reference” instance in the center of the room. When this reference object “moves”, it actually remains where it is, while everything else in the room shifts position. You’ll need to move the room’s background(s), as well as all visible objects in the room (and any invisible objects that have a meaningful location in the room).

//determine the pixel shift
shiftx = lengthdir_x(shift_amount, direction);
shifty = lengthdir_y(shift_amount, direction);
//shift the background image
background_x -= shiftx;
background_y -= shifty;

To make moving every other object in the game easier, we’ll group all the other Objects in the game as children of an identity parent, which we’ll call oOther:

//using parent object oOther, shift all the visible objects in the room
with(oOther) {
 x -= Player.shiftx;
 y -= Player.shifty;
}

It’s also possible to rotate the entire room around a reference point, using Views and the view_angle variable.

Normalizing speed with fps

Update: There is a similar, better technique for this called delta time that I recommend using instead.

If your game suffers from intermittent frame rate problems, this may come in handy for you. An instance’s speed is defined in pixels per step. Room speed is defined in steps per second. If your game is not able to keep up with your room speed, you can end up with jerky motion.

Very likely, your performance problems are being caused by you not programming something efficiently. Maybe you put a big loop in the Step event, or maybe you have too many objects active in the room. There are a lot of ways to optimize performance, and there has been a lot written on this subject already if you want to search for it. You should attempt to find and fix these problems first. But the fps normalization hack is specific to smoothing out motion, so I’ll mention it here.

GameMaker has a variable called room_speed, which is the number of steps per second that the runner tries to execute the main program loop. If the hardware is not fast enough to execute that many steps, the game’s frame rate will drop. There’s also a global readonly variable, fps, which is the actual steps per second that the runner is achieving, extrapolated from how long it took to compute the previous step. If the hardware can crank out more fps than the room_speed, the game automatically throttles itself down so that it doesn’t run too fast, but if the game can’t execute all the instructions in every step fast enough to keep up with the desired room_speed, it has no choice but to slow down. When the game doesn’t hit the desired room_speed, the apparent motion of all moving instances becomes jerky and stuttery.

The fps variable is updated moment to moment automatically by the game, and we may use the ratio of the actual fps to the room_speed to create a ratio, which we then re-calculate every step, and which we can use to adjust the speed of our objects. Simply multiply the speed of every moving object by this ratio every step, and it will smooth out your apparent speed by adjusting the actual speed of the object from one step to the next by just the right amount.

The math works out like this:

[
\frac{10 pixels}{step} \times \frac{30 steps}{second} \times \frac{second}{FPS steps} = \frac{300 pixels}{FPS steps}
]

So, if you take your desired speed, 10 in our example, and in each step of the game, multiply it by room_speed (3o in our exmaple) and divide it by fps (which varies from step to step) we get a nicely smoothed out apparent motion, even if fps fluctuates greatly.

Remember, fps is normally going to be close to room_speed, so we’re essentially multiplying the instance’s speed by a fraction (room_speed/fps) that will normally be very close to 1, unless fps drops. If this happens, speed will be boosted exactly as much as the fps dip slowed it down. This means the apparent speed will be very close to the defined speed value of the instance. The only reason it’s not absolutely perfect is because the fps calculation is actually the fps score achieved by the runner for the previous step, so it is always 1 frame behind.

This does work very well to smooth out motion when frame rates are consistently low, but not when they are exceedingly erratic. It’s not a great technique to use, because it complicates matters.For one, if framerates absolutely choke, speeds can jump momentarily to where you start having fast object collision detection problems. For example, an object moving 20px/step in a 30fps room gets its speed boosted to 100px/step if fps momentarily dips to 5fps. If that sprite isn’t >100px, now you’re in fast object territory and may experience collision skips. Finally, we’re doing a lot of extra calculations to achieve this apparent speed smoothing, and we’re doing it precisely because we’re already asking the game to perform too many calculations. You have to apply the adjustment to the speed of every instance in the game that is in motion — if you don’t adjust the speed of everything in the game, every step, the technique won’t work. And in Step 1, fps == 0, which means you need to avoid dividing by 0. You can accomplish this by adding a tiny value to the divisor so that even if fps == 0, there won’t be a divide by 0, or by checking whether fps == 0 before calculating the smoothing_factor, and set smoothing_factor = 1 if fps == 0.

Probably the best way to implement this is to set up an Object that manages the smoothing for all moving objects in the game.

objSmoother

Create Event:

smooth_factor = 1; //initialize smooth factor
smoothing_enabled = true; //use this if you want to be able to turn smoothing on or off.

Or, alternately, to skip the if, create/destroy this object (or activate/deactivate) when you want smoothing.

Step(Begin Step) Event:

if smoothing_enabled{
 smooth_factor = room_speed/(fps+0.01);
 
 with(all) {speed *= smooth_factor;}
}

or

if smoothing_enabled{
 if fps {
 smooth_factor = room_speed/fps
 } else {
 smooth_factor = 1;
 }
 
 with(all) {speed *= smooth_factor;}

If you want to, and you probably should, you can create an identity parent object (I suggest naming it objMoving) to replace all, which you can use to filter out invisible objects or other objects that don’t move. The brilliant thing with this approach is that you don’t have to code the smoothing adjustment every object’s step event, which saves a lot of coding and maybe makes it a bit faster to process.

However, you’ll still need to apply smoothing anywhere in code where you are actually changing speed — that is, if changing an instance’s speed other than for smoothing reasons, you’ll still need to apply the smoothing factor to the adjustment, like this:

speed += 8; //no smoothing going on here; adding 8 will behave oddly if smoothing is "on".
speed += 8 * objSmoother.smooth_factor; //smoothing applied to speed change

This way you handle all the speed adjustments due to things like actual acceleration or deceleration in a way that takes the momentary smoothing factor into account.

Wave Motion

Wave motion is a bit complicated to explain in a couple paragraphs, so I’ve published it as a separate article.

18 Comments

Add a Comment
  1. Really detailed, thanks, needed that bit to explain collsion boxes.

      

  2. This is awesome can’t wait for your motion with physics article.

      

  3. Thanks, Nick.

    I’ve only played around with the new 2D Physics a little bit, so far. Not enough to write a full-blown tutorial on it. I’ll probably get around to doing it at some point. But one of the books I helped out with, HTML5 Game Development with GameMaker, has a chapter devoted to implementing physics. Hope this helps:)

      

  4. Hi,i am a newby in the programming world,would you please tell me the code i have to write ,to allow the player to make a line,in wich a ball can bounce?

      

    1. If your goal is simply to make a ball that will bounce, that’s not too hard. There are many ways to do it, but move_bounce_all() is probably the easiest. In general, the GameMaker documentation available at http://docs.yoyogames.com is the best place to start when you want to learn how to do something.

      There’s also good tutorials built into GM:S, if you look at the projects in the Tutorials tab you can see clearly how many different types of games are built. If you’re new to GameMaker, they’re the best place to start.

      If you actually want to draw a line that traces the path the ball takes when it bounces, that’s a different matter. Your best bet would be to ask your question on the GameMaker Community Forums, in the Questions & Answers forum.

        

  5. i have 3 object move in the same line . and i want when the first one stop movin the other to and same the third . withe same space between them .

    Any idea ??

    Thanks.

      

    1. You could do this in a number of ways…

      If these three instances are all of the same object type, and are the only instances of that object in the game, you can use a with statement to stop them all at the same time.

      with object { speed = 0;}

      If they are not the same type of object, then you'd need to know the id of each instance. If you know the id, you can set the variables for each instance, one at a time:

      id.speed = 0;

      In order to get the spacing right, for example if the objects are moving out of formation, and need to come to order, then you'll need to do more work than just telling them all to stop. Try asking on the GameMaker Community forums for more help.

        

      1. so i have much work lol.

        thak u for ur help =D .

          

  6. Thank you for this, I’ve been trying things for weeks and this looks promising. Thank you, thank you, thank you.

      

  7. Hi,

    I have to make a projectile move from a point to another with a curve path. I know the coordinates of the start and end of the path and I also know the equation of the parabol I want to follow…. but I’m unable to convert it into motion code for my projectile….
    Thank you for any help

      

    1. If you haven’t yet, try asking this on the GameMaker Community Programming Q&A forum. Lots of people will help.

      This could be tricky if you need to calculate the motion with each step, but could be very easy to do if you use Paths, which I didn’t cover in this article.

      The easiest non-paths way to do it would be to provide an initial motion to the projectile, by setting speed and direction, and give the projectile some gravity. A linear velocity plus downward acceleration due to gravity will result in a parabolic arc emerging naturally, although to get it to be positioned exactly where you want it, at the speed you want it, could take some trial and error tweaking.

        

  8. I discovered function lengthdir through this article. I needed that function so much. Big thanks to the author!

      

  9. How can i make an object face/move toward another moving object?

      

    1. Use the point_direction() function.

      point_direction(x, y, target.x, target.y)

      assign the value returned by point_direction to image_angle or direction.

        

  10. Beyond just refreshing the basics, it’s the way you explain the how and why that makes this document essential reading. Great stuff.

      

    1. Thanks so much for the kind words.

      When I was learning the basics in GameMaker, I found that as I learned things, I'd forget them again if I didn't use them all the time. After a while, I realized that taking notes and explaining to myself how stuff worked helped a lot — as much as reading the Help file, if not more so. And I realized that this had value beyond simply writing it for myself, so I put it up on my website to share with people, hoping it could benefit many.

      To this day, this is the most popular thing I've written, and it's always good to hear from people who found it useful.

        

  11. I’m about to start working with GameMaker. One of the pre-acquisition questions I have about GameMaker is are there room size limits within GameMaker. I am making the presumption that game Maker rooms cam be scrolled left and right/up and down. As my goal is to produce a quasi retro game where characters move one square per move, and thus I kinda would like to know where room limits are.

      

    1. Rooms can be any size you need. You can set up a room to scroll, through the use of Views, or in GMS2, Views and Cameras.

      If you haven't purchased GMS yet, it's probably a good idea to wait until GMS2 is released. It's currently in public beta, but is not yet available for purchase. There's little sense in learning the old version now that the new one is so close to coming out.

        

Leave a Reply

csanyk.com © 2016
%d bloggers like this: