Skip to content

Release 2

Latest

Choose a tag to compare

@Mtax-Development Mtax-Development released this 05 Sep 17:05
9f1a9bb

This release consists of three and almost a half years worth of changes in response to the release of 2026 Long-Term-Stable version of GameMaker.

Optimization is the focus of this release and constructors rendering graphics received the most attention in this release, becoming one of the pivotal systems of GML-OOP. Rendering functionality of most of them is now based on vertex building. Shape constructors were overhauled, supporting multicolor rendering for their filling and outlines, with outline sizes being completely configurable. These and other changes in this release mandated a change to methods, provided arguments and constructor properties, so take extra care while porting existing projects to this release. It also should be noted that the behavior of other keyword was changed in the GameMaker engine, but GML-OOP was designed for the old behavior that is only replicable by enabling an option in each project for Game OptionsMainGeneralDeprecated BehavioursLegacy Other Behaviour.

When using GameMaker to render graphics for several frames, chances are, the code was written to recalculate rendering information each of these frames. When that graphic is completely static, recalculating them is redundant and it could instead be received from a Vertex Buffer, where it was saved for reuse. In cases where a graphic is meant to not be static, various data from Vertex Buffer can optionally be modified through a Shader. Vertex Buffer data consists of information about each vertex, which describe properties of a single point of a graphic, such as its location, coloring, transparency and optionally, texturing. Most rendering in computer graphics is achieved by connecting data from three vertices to form a triangle, multiple of which make up the rest of the shape. More information about their use is available in relevant GameMaker documentation.

GML-OOP greatly removes redundancy when using Vertex Buffers, handling Vertex Formats and Primitive Types automatically. It simplifies this process to three steps:

  1. Calling the toVertexBuffer() method to prepare vertex data, which returns the VertexBuffer.PrimitiveRenderData constructor.
  2. Calling the VertexBuffer.PrimitiveRenderData.render() method to render prepared graphics.
  3. Calling the VertexBuffer.PrimitiveRenderData.destroy() method to free the memory after its data is no longer necessary.

As operating already prepared data is faster than preparing it anew, the usage of Vertex Buffers can lead to improved rendering performance. An optimization guide was made to introduce some of those concepts. There also are many more applications for vertex-based primitive rendering:

  • Both Vertex Buffer rendering and most already existing render() methods now use primitive rendering data. This provides access to changing properties of every vertex each constructor will render, with complete control over a drawn graphic, such as changing its shape, other properties or adding new vertices. When GML-OOP constructors build a primitive, resulting data is passed through its event system, the new getPrimitiveRenderData event. Data can be modified in its method to be returned for use by remaining vertex-building code.
  • Each Vertex Buffer is compatible with the pr_trianglelist primitive type, meaning different graphics can be combined into one Vertex Buffer, by specifying an already active Vertex Buffer as an argument of a toVertexBuffer() method. This enables the possibility of combining different graphics into one rendering call, for as long as each graphic does not need to have a differently applied Shader or texture. This reduces the necessity of Surface usage, which exhibits potentially unwanted behaviors, namely the possibility of being removed from memory without it being explicitly specified or not reproducing alpha exactly as provided.
  • Complete vertex information is now utilized to achieve precise collision detection in shapes:
    Precise collision example of a cursor collision with a RoundRectangle shape

Vertices can also contain three-dimensional location information, meaning GML-OOP now supports working in three dimensions of space through new constructors: Vector3, EulerAngle, Camera, Plane and Cube. Their graphical representation is also prepared for use with orthographic projection, making them visible even without the use of three-dimensional perspective. It should be noted that calculations made by GML-OOP do not reverse the Z axis and its negative value will need to be used when passed to native GameMaker code. These constructors comprise a rudimentary support for three-dimensional graphics, which should be sufficient to achieve a diorama-style or orthographic visual representation. It can function as an introduction to concepts of third dimension in GameMaker, akin to what is being introduced in its new runtime. This is of particular interests for developers considering professional work in GameMaker once its own support for three-dimensional workflow is expanded in GMRT.

The use of uniforms in the Shader constructor is now based on a new Shader.Uniform constructor, which remains mostly the same for existing uses, but its value must always be in an array.

A Callback constructor was introduced as a handler for storing methods, their arguments and execution scope, for execution at later timing than its construction. It can store a single method directly or multiple of them in an array, in which case all they will also be provided with a single argument or each with different ones, depending if they are stored directly or in an array. All constructors, which used the event system, now do so through this constructor. The new VertexBuffer.PrimitiveRenderData constructor is exempt from this and will still operate and call its methods directly for sake of attaining fastest possible execution time, while maintaining event support.

Following project-wide changes have been introduced to constructors:

  • Each now has a static constructor property, which refers to a numerical index GameMaker runtime assigned to it. This provides the fastest possible alternative to instanceof() and is_instanceof() checks, but must be used only with surety that a checked variable has this property, otherwise causing a crash.
  • They no longer have the argument_original variable, which stored arguments provided during its construction.
  • Some constructors now construct their own static instances with recognizable values at runtime start. Their references can be reused to avoid delegating memory and execution time to construct a new constructor, for example by specifying Scale.one instead of new Scale(1, 1) in cases where the default value would be used. They always refer to a single copy of a constructor, meaning it is usable only when their values are not going to be changed, as such change would be reflected in every variable referring to that specific copy of a constructor.
  • Horizontal whitespace management of constructor code has been changed to fit more code before needing a line-break.

Lastly, this release features an introduction of audio support with new Audio and AudioPlayer constructors. That support is rudimentary as of this release, but can be expected to be expanded over time and included in Release 3. There likely will be somewhat similar waiting time for that release. In the meanwhile, it can be useful to periodically give attention to code committed to the primary branch. Working with a version of GML-OOP directly from that code is an option to have access to most recent features, although it is always a work-in-progress code and it will contain some errors and be ongoing design changes, before becoming polished for a full release.


Changes from Release 1:

Changes to existing content:
| Updated validity checks to comply with in-engine updates and replaced their reiterations with isFunctional() method calls.
| Replaced all simple instanceof() calls to use is_instanceof() instead.
| Made various improvements to removal of data that could linger after an error being caught by a try statement.
| Made the event system be based on the Callback constructor.
| Removed the use of noone keyword in cases unrelated to object instances.
| Simplified the code of every error reporting call.
| Overhauled whitespace management in the entire project.
| All shape constructors: Overhaul to implement consistent features and vertex building-based multi-color fill and outline rendering.
| Vector2/Vector4: Renamed the difference() method to absoluteDifference() and added a new difference() method in its place.
| ErrorReport: Made the entire constructor static to have it function without constructing it.
| SpriteRenderer/SurfaceRenderer: Prevented constructor copy from duplicating the resource.
| TextRenderer: Overhauled the constructor to support Scale and Angle properties.
| TextRenderer: Prevented stringifying its value on construction, so that a constructor can be provided as its value and result of its toString() method used during method calls.
| TextRenderer: Allowed full construction using undefined as the value.
| ParticleType: Made constructor properties be updated instead of replaced when setting new values.
| Shader: Overhauled the use of uniforms to base them on a new Shader.Uniform constructor.
| Sprite: Changed the type of the size property from Vector2 to Scale.
| Surface: Made size be updated instead of replaced when setting a new size and made it be validated before setting it, falling back into size of one pixel in case of failure.
| DateTime.modify(): Made every argument optional.
| Layer.destroyInstance(): Changed returned variable to support inline assignment of the constructor calling the method.
| ParticleType.setStep()/ParticleType.setDeath(): Made the number argument optional.
| Sprite.getTexel(): Simplified the method by removing argument requirement, as the result is the same regardless.
| StringParser.getSubstringPosition(): Added argument support for finding multiple positions.
| General code, documentation and error handling changes and fixes.

Additions:
+ Static constructor property in every constructor, referring to their own asset index.
+ Audio, AudioPlayer, Callback, Camera, VertexBuffer, VertexFormat, Cube and Plane constructors.
+ Angle/Color2/Color3/Color4/Range/RangedValue/TextAlign/Scale/Vector2/Vector4: Added static constructions.
+ SpriteRenderer/SurfaceRenderer/TextRenderer/every shape constructor: toVertexBuffer() method.
+ Color2/Color3/Color4/TextAlign/Rectangle/Font/Surface/SpriteRenderer/TextRenderer: equals() method.
+ Color2/Color3/Color4: interpolate() method.
+ Vector2/Vector4: exceeds() and subceeds() methods.
+ Range: sum(), difference(), product(), quotient() methods.
+ Scale: sum(), difference(), product(), quotient(), getSign(), add(), substract(), multiply(), divide() methods.
+ Vector4: isDegenerate(), roundToBorder() methods.
+ PriorityQueue: getFirstPriority() and getLastPriority() methods.
+ ArrayParser: addUnique() method.
+ TextAlign: getMultiplier() method.
+ TextRenderer: getPixelSize() method.
+ StringParser: capitalize(), formatStruct(), toFile(), fromFile(), fromJSON() methods.
+ Sprite: getTextureTrim() method.
+ Scale/Vector2/Vector4: Real number argument support in approach() methods.
+ Font: Signed Distance Field support.
+ TextRenderer: Support for an integer number scale to specify target font size.
+ Buffer: Support for construction from VertexBuffer.
+ Sprite: Support for multiple frames being specified to getTexture() and getUV() methods.
+ TextRenderer.getBoundaryOffset(): Replacement argument support for scale.
+ TextRenderer.wrapText(): Replacement argument support for location and scale.
+ StringParser.getPixelSize(): Replacement argument support for scale.
+ Layer.ParticleSystem.destroy(): Event system support.
+ Shader.setUniformInt()/Shader.setUniformFloat(): Added Vector3 argument support.

Removals and fixes:
- Discontinued the Arrow constructor.
- Deprecated support for fromEncodedString() reading legacy data structure format for files saved in GameMaker versions prior to GameMaker Studio 2.
- RoundRectangle(): Removed the collision() method.
- Font: Fixed a wrong variable being used during constructor copy of a Sprite font.
- ParticleType: Fixed wrong variables being used in multiple setter methods.
- Sprite: Fixed properties not being set upon construction from Surface.
- ErrorReport.ReportData.equals(): Fixed the detail struct not being checked properly.
- Layer.destroyInstance(): Added instance validation before reading its values.
- Layer.destroyInstance(): Corrected method name while reporting an error.
- Ellipse.toString(): Added a missing separator mark.

Instructions for conversion from the previous version:
GML-OOP is intended for use with enabled project option for Game OptionsMainGeneralDeprecated BehavioursLegacy Other Behaviour. Enable it, then ensure the code relying on other keyword behaves as intended and adjust it if necessary.
• If only selected GML-OOP constructors were included in a project and is_instanceof() checks now cause a crash due to a constructor not being present in the project: Include either those selected constructors or all GML-OOP constructors in the project.
• If code relied on comparing to noone: Ensure the method is still able to return that instead of undefined on failure state, otherwise either make it be based on comparing to success state or compare to undefined instead.
• If code relied on the use of argument_original property: Save the arguments manually prior to the construction and use that data instead.
• Event system: Use the Callback.set() method whenever event struct properties would be set.
• All shape constructors/TextRenderer: Adjust how arguments are provided during construction and method calls to have them be functional with new argument listing and ordering.
Vector2/Vector4: Change the use of difference() method to its new name, absoluteDifference().
ErrorReport: Use the constructor directly without constructing it using the new keyword.
SpriteRenderer/SurfaceRenderer: Ensure the code relying on constructor copy not relied on the resource a different ID, as it will now refer to the same resource.
TextRenderer: If used with non-string values: Stringify that value if reading it outside methods of this constructor.
Shader: If values of the uniform struct were directly operated, adjust to its value property now always being an array.
Sprite: Provide a Scale constructor instead of Vector2 for its size property.
Arrow: Either use draw_arrow() function directly or reproduce its graphic using Line and Triangle constructors.
Grid.fromEncodedString()/List.fromEncodedString()/Map.fromEncodedString()/PriorityQueue.fromEncodedString()/Queue.fromEncodedString()/Stack.fromEncodedString(): If legacy format was used, call an appropriate ds_*_read() function directly with legacy argument set to true.
Layer.destroyInstance(): If code relied on it returning undefined: Adjust it, as it now returns self.
Ellipse.toString(): If code relied on comparing its exact result: Adjust it to the fix of its returned value.
RoundRectangle.collision(): Use Rectangle.collision() instead.