Sponsored By

Featured Blog | This community-written post highlights the best of what the game industry has to offer. Read more like it on the Game Developer Blogs.

Object Distance with GameMaker: Studio

Join us as we review different methods of measuring distances between objects in GameMaker Studio and discuss their efficacy for specific use-case scenarios.

Nicholas Doane, Blogger

July 29, 2015

6 Min Read

 

For our first game development project, we are creating a casual game about sea turtles for the iPad, called Turtle Sprint -- it happens to be coming out on August 12th via the Apple App Store! The programming for the game is fairly straightforward; the majority of objects in our game move in pre-determined patterns… simple, right? It got tricky when I created a new object that has a “seek and destroy within x aggro radius” behavior.

Note: Working with a tool like GameMaker Studio really simplifies development so it’s tempting to stumble through development challenges rather than planning and researching the programming needed for a game (e.g. pseudo code). Instead of choosing the smart path and conducting a Google search for solutions, I chose to just "figure it out".

New Object Desired object behavior: Object_monster will remain stationary until a viable target enters its aggro area. Object_monster will immediately move towards the target object until 1) it collides with the target object, or 2) the target object leaves the aggro area. Following one of these events, Object_monster will check for other valid targets in the aggro area and if found, pursue them. If no valid target objects are detected in the aggro area, Object_monster will return to the center of the aggro area and become stationary again, until the next trespasser.

First version:

In GameMaker Studio, every object has a “Step” event that you can place code in, and it’s executed every time the game refreshes the frame. We set this to occur approximately 30 times per second. My first plan was to implement the aggro radius for the seek and destroy object (“Object_monster”). This involved checking 30 coordinate locations in a circle around the object every second, to see if a viable target object is present.

If a target object was detected then Object_monster would move to the target’s location. If not then it would return to its home location. This approach worked well, except that in our game there are multiple viable targets on the screen at the same time. Sometimes, more than one target can be within the aggro radius of Object_monster and Object_monster would go after the first target and dispose of it.

But instead of going after the second target, Object_monster returned to its home location. After it returned to the home location, it would check for other trespassers but sometimes it’d be too late and one of the target objects had already exited the aggro radius. So that didn’t work.

Second version:

After I took a break, I worked with a concept that included an invisible “aggro radius” object that was created by Object_monster. Instead of having Object_monster check 30 different locations within 30 seconds, I planned to have the target objects turn into an alternate object when they collided with the aggro radius object. My reasoning was that it’d be easy for Object_monster to identify its targets.

Additionally, the target object would periodically see if they were on top of Object_aggro_radius. If they weren’t then they’d revert back into their original version. Finally, Object_monster would check for the existence of the alternate target objects. If found then it’d move toward the target. If Object_monster collided with a target, it would destroy it – then check for other alternate target objects. Rinse, wash, repeat.

This approach has worked relatively well. After taking out an initial target, Object_monster would move toward a second target if present in the aggro radius. This has brought us much closer to the object’s desired behavior but isn’t perfect. I’m struggling with one bug in particular — after Object_monster takes out a target then Object_monster should move back to its original location if there aren’t any other targets within the aggro radius. But it never returns to the exact original location.


 

 

 

 

 

 

 

Third and final version:

In an attempt to solve this, I changed the code to detect the distance to the exact coordinates of the middle of the aggro radius. This is problematic since the object is moving and the coordinate test must happen at exactly the right 1/30th of a second to satisfy the “stop” condition. Currently when Object_monster takes the return trip home, it “misses” stopping at its home location, realizes it’s gone too far, reverses direction, misses it again, and gets stuck in an infinite loop of moving back and forth near the home location (like a shivering movement).

At this point I had run into a brick wall while learning on my own, and I had to take the battle to Google. Luckily the mighty search gods lead me to the YoYo Games forums -- probably where I should have started to begin with!

After reading a thread or two, I quickly realized what the cause our difficulties was. In the version we tested for Part 1 of this blog post series, we were using the GameMaker Studio function distance_to_Object() to measure the distance to coordinate waypoints for our moving object. The description of the distance_to_object() function in the GameMaker Studio help docs (found within GMS) reads as follows:

This function calculates the distance from the edge of the bounding box of the calling instance to the nearest edge of the nearest instance of the object specified. The object can be an object index or a specific instance id as well as the keyword other, and the distance is returned in pixels. Note that if either of the objects have no sprite or no mask defined, the results will be incorrect.

During my research on the YoYo Games forums, I came across mention of another distance measurement function, point_distance(). The description of the point_distance() function in the help docs is as follows:

This function returns the length of a vector formed by the specified components [x1,y1] and [x2,y2].

We used this function and set the [x1, y1] values to [x, y] to insert the current coordinates of the object. For [x2, y2] and values we used the center coordinates of our aggro radius object — which is exactly where we want the object to stop. By measuring the distance to these coordinates every step and knowing the speed of the object, we can affirm the object slows down as it approaches the home coordinates and then stops when exactly aligned with the coordinates we specified, using the point_distance function.

Here is our current code snippet related to this object behavior (in STEP):

if point_distance(x, y, obj_aggro_radius.x, obj_aggro_radius.y) > 3 {
image_speed = 0.25;
move_towards_point(obj_aggro_radius.x,obj_aggro_radius.y,3);
}
if point_distance(x, y, obj_aggro_radius.x, obj_aggro_radius.y) < 4 {
image_speed = 0;
speed = 0;
Object_monster.image_index = 0;
}

There is still some fine-tuning to be done with this code to make it work perfectly, particularly in terms of matching the measurement size to the speed of the object, and also tweaks for future animation changes. But overall the object works as intended, and we're quite satisfied with the outcome when using the point_distance() function! Onto the next challenge...

Thanks for joining us and as always please participate in the comments if you want to share any insight!

---

With a professional background in computer science and information technology, Nick uses this knowledge to help improve player experiences at NDXP Games.

Read more about:

Featured Blogs
Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like