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.

001 Just the Beginning

Hi and welcome to the development journey of Chronicle Destiny! My intention is to release a development snapshot every month, around the 22nd, accompanied by a post detailing the progress made so far. This marks the first installment of this Devlog.

The primary objective of this release is to evaluate the feasibility of the game’s art style. I draw inspiration from various GBA titles, particularly Kingdom Hearts: Chain of Memories, Spyro: Ripto’s Revenge and Sonic Battle. Those games utilize an isometric perspective and feature a high degree of fast action gameplay. The chosen game engine, GameMaker, is capable of handling isometric games. However, instead of constructing the isometric perspective using 2D tiles as in KH:CoM or S:RR, I aim to replicate Sonic Battle’s approach by positioning 2D sprites in a 3D environment, employing simple planes and blocks for terrain and objects.

While GameMaker does offer 3D capabilities, it doesn’t excel in this area. Nevertheless, after exploring Godot for a while, I decided to proceed with GameMaker due to my familiarity with the engine since version 5. Additionally, the tutorials by DragoniteSpam, and others, have helped me to achieve the current state of development.

This first release is quite basic in terms of mechanics, implementing only a simple top-down movement system. The primary focus has been on establishing the 3D isometric visuals. The standard GML setup involves enabling ztestenable, zwriteenable, and alphatestenable to activate 3D rendering. A struct controls the camera’s movement. It holds variables for spherical coordinates, radius, angle, and elevation, allowing the camera to be positioned around the player. The elevation is set to 30 degrees to achieve the isometric look. The spherical coordinates are converted to standard Cartesian coordinates (XYZ).

Crucially, the camera’s projection matrix is set up for orthographic projection. However, an issue arises when using matrix_build_projection_ortho(), as the built-in tile system fails to render the tiles correctly based on the camera’s configuration. A solution involved creating a draw surface significantly larger than the required size. This ensures that the tiles being culled away fall outside the draw surface’s center. Only the center of the surface is drawn on the screen. This solution became overly complex, leading me to consider abandoning orthographic projections. Fortunately, DragoniteSpam released a tutorial on converting tilemaps into vertex buffers, effectively resolving the issue. The target resolution is 640 x 360 to capture the handheld pixel aesthetic. The resolution is adjusted to fit the screen’s aspect ratio, following a tutorial by Pixelated Pope.

The next task involves determining how to represent objects on the screen. The ground will be represented by tiles, or rather, vertex buffers derived from tilemaps. But how will characters, scenery, props, and effects be represented? Billboards provide a partial solution. Billboards are images that always face the camera. A key aspect to avoid is making billboards appear as flat planes in 3D space. I want to avoid the look of a cutout diorama, similar to Octopath Traveler. Rather the flat look of KH:CoM and Sonic Battle. The combination of orthographic perspective and low resolution helps prevent that effect. Currently, four methods are employed to represent objects: sprites, billboards, prisms, and .obj models.

  • Sprites – Sprites are rendered on the screen using the standard draw_sprite() function. Before the sprite is drawn, the matrix_world is transformed to position it at the object’s location. It is also rotated perpendicular to the ground plane. The variable image_angle controls the sprite’s facing direction. This method is suitable for small decorative elements like flowers and grass.
  • Billboards – Billboards function similarly to sprites, but their facing direction is determined by the camera’s location. Billboards always face the camera. This method is suitable for simple circular objects like poles or orbs.
  • Prisms – Like billboards, prisms always face the camera. Additionally, prisms track the direction of the object relative to the camera’s angle. Using this information, a sub-sprite is selected to represent the object’s direction. The sprite asset is structured such that the first sub-sprite faces right, with subsequent sub-sprites rotating counterclockwise. Each sub-sprite can be an animation but must have the same number of frames. The number of sub-sprites is arbitrary, evenly distributed across 360 degrees. For an isometric game, eight sub-sprites are ideal. This method is suitable for characters or intricate objects.
  • .OBJ models – These are imported .obj files created in Blender3d and exported as .obj files. The .obj file is then imported as a vertex buffer. It is based on DragoniteSpam’s video on importing 3D models. This method is suitable for scenery, terrain, large, complex, but static objects.

This first release focuses on establishing a foundation for a 3D isometric game using GameMaker, an endeavor that has been successful so far. It lays a solid foundation for an action-adventure game. The following releases will focus on refining player actions, input mechanisms, artwork, and dialogue. Player actions will encompass attacking, jumping, and general platforming maneuvers. The aim is to create a set of actions similar to those in Chain of Memories, with additional inspiration drawn from Zelda, Metroid, and Fable games. Input support will be implemented for gamepads and touch screens, paving the way for a mobile build. Currently, the artwork is in the form of programmer’s art, functional but lacking in polish. Subsequent releases will enhance the quality of the art assets. As development progresses, the artwork will evolve from functional programmer’s art to a polished final product. Dialogue is the game’s primary hook, and a significant portion of effort will be directed towards getting this mechanic right. That concludes the summary of this release. I hope to share the next update in approximately a month.

Thanks for your time.