Steering Behaviours in GML

This is my current version of steering behaviours ported to Game Maker Studio 1.4.

Download, 29KB – .gmz only
Download, 4.3MB – ZIP containing .gmz, .exe compiled with standard Game Maker compiler, .exe compiled with YYC, exported scripts in folder

I will try to keep this self contained so there is no need to go elsewhere to understand anything. That said, if you are curious, here is the original thread by u/PixelatedPope that I based my initial work off. My first example includes a download that can get you started, although this current one is far better. My second example has no download, just a (poor quality) video, but shows a proof of concept using ds_grids to manage flocking behaviours.

Enough history lessons.

So “what are steering behaviours?” you might be asking. Steering behaviours let an object move based on certain criteria. I will detail the ones I have ported below, but the interesting thing about them is that they can be mixed together in (pretty much) any combination with each one weighted. For example you could have a bumble bee that wanders around, while also being drawn towards flowers or fish that form groups and swim together but then avoid sharks. The ones that interest me most are the 3 flocking behaviours (alignment, cohesion and separation), these behaviours take other instances into account and let them form groups much like birds, fish, crowds, etc. see Craig Reynolds page about boids that ultimately all of this is based on for more info.

The steering behaviours are all scripts which an instance calls each frame (you could stagger them if desired). Each script has comments to help explain how to use them. All steering behaviours use scripts from the Vector folder (included) so if you add these to another project, be sure to copy those too. A few of them also use a couple of scripts from GMLscripts.com (also included).

Steering behaviour scripts:

sb_seek – moves towards a given point.
sb_seek_arrive – moves towards a point and slows down when near it.
sb_flee – moves away from specific point.
sb_pursuit – chases specific entity (with awareness of it’s velocity).
sb_evade – avoids specific entity (with awareness of it’s velocity).
sb_wander – move in random direction (has parameters to control randomness).
sb_avoid_collision – tries to avoid colliding with given object, doesn’t actually prevent collisions.
sb_path_loop – follows a specified path and loops.
sb_path_tofro – follows a path and changes direction upon reaching either end.
sb_alignment, sb_cohesion, sb_separation – these all deal with flocking, usually used together.
sb_alignment_grid, sb_cohesion_grid, sb_cohesion_grid2, sb_separation_grid, sb_separation_grid2 – also for dealing with flocking, detailed below.

ds_grid based flocking:
The scripts with “grid” and “grid2” in their names use a much faster method for flocking than those without. However they require a bit more set up. I have tried to make it as easy as possible to understand. Basically use the cont_steering object if you want to use them and it will deal with it. In the standard flocking behaviours, each instance is checking every other instance and reading variables from them… all… every frame. The CPU cost quickly scales to ridiculous amounts if you use too many instances. My solution is to have a controller object (cont_steering) which maintains ds_grids for XY locations, velocity and number of instances in a given cell. Using this data you can achieve similar results with much less CPU overhead.

GMZ layout
Sprites are whatever. Scripts are divided into folders, all steering behaviours start “sb_…”. Objects are also divided into folders:
“Default Drones” contains the “base” objects. Each one has slightly different capabilities (for example to support grid based flocking). These do not actually appear anywhere in the project file, think of them as templates.
“Obstacles and Solids” is what it sounds like. Just basic shapes. Obstacles can be avoided but there is nothing to actually stop entities overlapping them if other steering behaviours are weighted high enough. Solids can’t be overlapped, although sometimes entities get caught on the edges/corners (you will have to see your needs and modify accordingly to prevent this).
“Controllers” only has two objects. cont_debug is just used for controls to reset and skip rooms and write the room info to the screen, you don’t need this at all. cont_steering is only needed for grid based flocking to work. You only need one instance of cont_steering assuming all your entities are referencing each other. If you wanted two or more sets of entities that only interact within their set, but not with each other, you would need an instance of cont_steering per set.
“Demo Drones” has all the objects used in the demo file. Each one is based on an object in “Default Drones”, just with various settings and behaviours set for demo purposes.

Rooms
Generally if you want to know something about a specific demo room, look at the creation code, all object spawning is done there. rm_initialise spawns the cont_debug object (which is persistent) so it should run first.

I haven’t done any scientific testing, but there is a considerable speed difference between using the Yoyo Compiler and not. With few instances or only basic steering behaviours (sb_seek, sb_flee, etc.) the difference might be negligible. Once you start using lots of instances or flocking behaviours (especially non-grid based ones), the difference is massive. This is what compelled me to make the grid based system in the first place as I hadn’t bothered to set up the YYC when I first started working on this (also I just love me some ds_grids). Even the grid system can cost a lot of CPU, you can test this by pressing UP and DOWN on the keyboard in rooms that use the grid system to change the grid resolution on the fly. I have also included two .exes in the download for those without access to the YYC so you can see the difference it makes.

Known Issues/Stuff for the Future:
-The solid obstacle avoidance code isn’t perfect and things will sometimes get stuck. I’m sure it’s possible to code around it fairly easily on a specific usage case basis, but ideally it’d be nice to have a plug’n’play version that just works.
-Optimisation. The grid based flocking system is much faster than the one without, but it could be even faster.
-MOAR BEHAVIURZ! Yeah, there are others I haven’t ported such as queuing.
-“This doesn’t do path finding”. Nope, no it doesn’t. I might integrate Game Makers motion planning into this at some point, no promises.

So, I hope some of you find this useful. Please post any feedback or questions. Let me know what you make with this, I’d love to see!

(original reddit thread this post is based on)

Music: ‘Not That Deep’ by Stormzy
Original Video: https://www.youtube.com/watch?v=HuEoiIQo0WE
Fucking fire!!

Leave a Reply

Your email address will not be published. Required fields are marked *