003 Can Touch This

It’s been a few months, but I have finally uploaded the third alpha build of Chronicles Destiny. This devlog summarizes the second and third alpha builds, both dealing with implementing a 3D collision system. Previously, the focus was on drawing objects in 3D space to maintain an isometric look. While objects can be positioned and rotated freely, collision detection remained confined to 2D. GameMaker offers a robust built-in 2D collision system. The plan was to extend the built in system for 3d use. An initial XY plane check is done with GameMaker’s collision functions. A follow up check comparing Z related variable is done afterwards. This will determine if the initial XY check is valid or not. This approach would allow movement similar to classic 3D Zelda games like Ocarina of Time and Majora’s Mask.

Well, that was the plan. After viewing DragoniteSpam’s series on native GML 3D Collision, I decided to implement at least some of that solution. Since it is implemented in GML, it works in all supported platform, specifically HTML5.  This system leverages GameMaker’s relatively new struct syntax to organize related data and functions.

  • Vector3(x, y, z) – This is the core struct that enables anything 3D. It consists of the xy, and z variables to store a position or a direction (normal) in 3D space. It also has many functions that are crucial, ranging from simple arithmetic like add(val) or multiply(val) to more complex ones like dot(val) and cross(val).
  • Collider(shape) – This struct stores the collision shape to be used. It acts as an interface between the object and the collision system, passing the relevant information to the relevant shapes.
  • Point(position) – This struct simply stores a Vector3(x,y,z) for its position.
  • Sphere(position, radius) – This is like a thick point, very simple and fast to calculate. If the distance between its position and the other shape is less than the radius, then a collision occurs.
  • AABB(point_min, point_max, [min_max]) – Axis Aligned Bounding Box. This defines a rectangular volume that is always parallel to the axes. It stores its center and half the length of each side. However, an AABB can also be defined as the volume between two points. This struct uses min_max as an optional argument. If not provided, it defaults to true and calculates the position and half-extents from two points. If false is provided as the third argument, it just assigns the first two arguments for the position and half-extents.
  • Line(start, finish) – This simply represents a line segment between the start and finish points.
  • Ray(origin, direction) – This is similar to a line, but it has no endpoint. It starts at the origin, following a direction, and extends to infinity. It uses a helper struct to store information about the collision.
  • Ray_Hit_Info() – This is a helper struct used by rays to store information about the collision between the ray and another shape. This information includes the distance between the ray’s origin and where it collided on the shape, and the point where the collision occurred.
  • OBB(position, size, orientation) – Oriented Bounding Box. This is similar to an AABB but can be rotated. In addition to the position and half the size of the sides, it also stores the orientation in a 3×3 matrix.
  • Capsule(start, finish, radius) – A thick line, commonly used for player characters, so I implemented it.

This is everything so far. There are just a few shapes I haven’t implemented yet: triangles, meshes, and planes. Additionally, there’s no hierarchy system in place, like octree or spatial hash. However, even with this partial implementation, basic movement is possible. All collisions now use the 3D system, replacing the built-in 2D system. Since there’s no hierarchy yet, a jimmy rigged solution is in place. The Wall object creates an AABB collider. It calculates its position and size based on its built-in variables, including z and image_z_scale. Then, it adds itself to a global array. The Player object iterates against the global array to see if there’s a collision. For the various test objects, each generates a wireframe based on its properties and checks if it’s colliding with the Player object. If they collide, they turn red; otherwise, they stay blue. The ray test object also generates a point wireframe based on where it collided with the Player

And that is all for now. Next, I’ll focus on implementing a collision hierarchy. I also plan to add an input manager. A struct that the Player or other objects can poll instead of directly polling the hardware. Additionally, I’ll be working on animations and player actions. I hope to start on the dialogue system afterward. I aim to share the next update within a month, give or take a month or so.

Thanks for your time.