3D Collisions in GameMaker
A collision system that works in native GML is probably one of the most frequently requested items for 3D GameMaker games of all time. So I made one!
This requires a reasonably recent version of GameMaker. A number of optimizations rely on new(ish) additions to the language.
I also made a 20-hour tutorial series on how the code works for some reason.
Works on Windows, Linux, OperaGX, HTML5, and probably everything else.
What's Included
- 3D primitives:
- Points
- Spheres
- Axis-aligned bounding boxes (AABBs)
- Oriented bounding boxes (OBBs)
- Planes
- Capsules
- Triangles
- Triangle meshes
- Line segments
- Raycasts, including contact location, contact normal, and contact distance
- Collision world data structures:
- Octrees
- Quadtrees
- Spatial hashes
- Camera frustums for frustum culling (works with quadtrees and octrees)
- Sphere displacement vs most of the above shapes
- Optimizations out the nose
How to use the system
I'll write some proper documentation at some point. If you watched any of the videos, you probably already know the general design idea.
There are three and a half things you'll generally be doing with this thing:
Creating a collision world
That's as simple as calling
world = new ColWorld(new ColWorldOctree(bound_min, bounds_max, recursive_depth))
in the relevant Create or Room Start or whatever event.
Creating collision objects
Shapes are bits of primitive geometry. Objects are a somewhat higher-level abstraction that contain shape data, as well as a collision mask (this kind, not the GameMaker kind) and a reference to the actual GameMaker instance, etc that they represent.
player_shape = new ColSphere(new Vector3(0, 0, 10), 10) player_object = new ColSphere(player_shape, obj_player)
Unless you want to check shapes against each other individually (which you probably don't), objects must be added to a collision world.
world.Add(player_object)
Checking for collisions
You can check an object against the collision world to figure out if there's an overlap between it and anything in the world. Checking objects against the world will return a reference to the first ColObject found to be in contact, or undefined.
overlap_result = world.CheckObject(player_object)
You can also raycast against the world, such as from the player's position in the direction that they're looking.
ray = new ColRay(new Vector3(obj_player.x, obj_player.y, obj_player.z), new Vector3(obj_player.look_x, obj_player.look_y, obj_player.look_z)) raycast_result = world.CheckRay(ray)
The raycast will return a struct containing information about the nearest object hit, or undefined.
Displacing spheres
Instead of simply checking for a collision, you can do a sphere displacement check against the collision world.
displacement_location = world.DisplaceSphere(player_object)
DisplaceSphere will return an { x, y, z } representing the nearest location where the sphere may be moved in order to stop being in contact with things in the world. If no objects are overlapped (or if no displacement is possible) it'll return undefined instead.
Suggestions and best practices
Use a collision world type that suits your game and subdivide it appropriately. Optimal partitioning of the collision world can make a big difference.
Ask YYG (nicely) to improve the performance of array_push.
Avoid triangle meshes whenever possible; approximate the shape of objects with primitives such as spheres and AABBs wherever you can get away with it.
If a triangle mesh is required, it should have the bare minimum number of triangles necessary. Collision detection does not require nearly as much mesh detail as a visible model, and collision meshes with a large amount of detail can actually make collision detection more janky instead of less.
Repeatedly instantiating (and later garbage collecting) structs and arrays is expensive. If you can get away with it, you should cache shapes (especially rays) to save time. The vector and matrix abstractions are nice but they're also pretty expensive.
Performance
The system performs okay, all things considered. I added a whole bunch of optimizations that you saw in the optimization video, as well as a bunch that I thought of afterwards. In some cases the potential performance gains over the original versions of the functions can be pretty extreme, but this does come at the cost of readability. This may or may not be a problem if you want to edit the code yourself.
The YYC is going to help a lot here, since the whole thing is almost 100% mathematics.
Tests I've done in the New Runtime so far seem to indicate that performance is already quite a lot faster there than in YYC, which is pretty exciting.
Price
This is going to be free, like most of my other stuff. All the same, this took a really long time to make and I'd really appreciate an involuntary payment via either Itch or Patreon.
Notes
- Aside from sphere displacement, this does not include physics responses. The video series does include a video on bouncing balls but the collision system doesn't have any built-in functionality for that.
Repository
can be found here. The sample project is here.
Credits
- Gabor Szauer, author of Game Physics Cookbook - I actually bought (and even read! Weird, right?) a book for this.
- TheSnidr, for answering a bunch of my dumb questions over the last few years
- The duck sprite was made by @alemunin
- The trees and stuff in the demo were made by kenney
Other Relevant Projects
I've made a bunch of other things for 3D in Gamemaker that you might be interested in:
- Collisions
- Basic 3D lighting
- Toon shading
- Terrain editor
- 3D model converter
- Better D3D emulation
Status | Released |
Category | Other |
Platforms | HTML5 |
Rating | Rated 5.0 out of 5 stars (1 total ratings) |
Author | Dragonite |
Made with | GameMaker |
Tags | 3D, collision, collision-detection, GameMaker, sourcecode |
Code license | Apache License 2.0 |
Average session | A few minutes |
Inputs | Keyboard, Mouse |
Links | YouTube, Source code |
Download
Click download now to get access to the following files:
Development log
- The Optimization Update™Jun 25, 2024