Welcome to GameMaker 2.3.0! This major new release introduces many significant improvements to the software, which you can find detailed further down this page: new resource types and editors; important quality-of-life changes throughout the IDE; a brand new manual; an overhauled project format; lots of new GML Code language functionality; and much more.
This one is quite a long read, as there are a lot of things to cover and there is also an important section below about things to be aware of migrating from 2.2.5, but please do take the time to review all of this page and you'll have no problems adapting to all the changes in 2.3.0 and be ready to take your games to new heights.
For further details on all the below points, please see the manual inside GameMaker later - but keep reading here for now, as even the manual has been changed for this release.
Now, if you're sitting comfortably, I'll begin...
New IDE Additions
The Asset Browser
The old GameMaker resource tree and its fixed “by resource” layout has been removed and now GameMaker has the Asset Browser. You can now create any resource type at any place in the tree, create multiple resources at once using the new Create Assets menu at the top, add Tags to your resources, and sort the tree A>Z/Z>A or your own custom order.
Note: Each time you change the sorting method, your GameMaker project file gets rewritten, so if you're working in a team and using source control, you probably want to establish either A>Z or Z>A and then all stick with it. (In this same scenario, you likely do not want to use Custom as this is per-person and so one team member could cause conflicts for others as your browsers get out of sync.)
In 2.3.0 organising your resources by gameplay/functionality area like this is perfectly valid and achievable now:
The preference to mimic the old folder structure in new/imported projects is enabled by default, so that tutorials and teaching materials transition easily, but you can turn this off in Preferences > Asset Browser if you wish to work in the clean new 2.3.0 way.
You can also colour your assets via the new colour picker window if you wish (doing this on a group folder will assign the colour to all assets in that group, and you can then go into the group and change individuals to a different colour if required):
Also, you can now use the new Create Asset window and create multiple new assets at once (here, I would get 9 new assets - 3 of each type selected):
There are also now additional organisational tools available to you from the Quick Access menu at the top of the browser:
-
Recent: Here you can find a sorted list of the most recently-opened assets.
-
Favourites: Once you have added lots of assets to the asset browser you may have some that you access frequently at any given time. To make things easier you can flag those as "favourites" from the right mouse button menu on the asset itself, which will add it to this list and let you always quickly find it again.
-
Room Order: From here you can change the order that your rooms will run in. We'll discuss this in a bit more detail further down, as there are a few changes with how rooms work.
-
Saved Searches: Here you have a list of any saved search options that you can switch to instantly.
-
Tags: All assets in the Asset Browser can be assigned user-defined Tags. Again, we'll discuss this in a bit more detail further down.
-
Game Options: This no longer appears as if it was a resource and you will find it in the Quick Access tools section instead (and on the toolbar, as always).
You will also find that Included Files and Configurations have been moved out of the resources list and now into the menu at the top (which also contains links to the Room Manager and Game Options):
The Room Manager
In the bulleted list above, you'll have seen that we have a section now for room order. Previously, you always had a "Rooms" folder in your resource tree and rooms would run in the order they were listed in that folder. Now, however, since you can place assets in any folder and in any order, that's not possible. So, you can use the Quick Access Room Order section and drag your rooms around or you can use the new Room Manager window, which you can open by clicking the "Extras" button at the top of the Asset Browser and selecting it from the menu that opens (or you can simply click the icon to the left of a room in the asset browser, as shown below):
After importing a 2.2.5 project, please do check that your starting room is set correctly before trying to run the game! (If required, manage your room order to set multiple rooms correctly.)
Adding Tags To Assets
The final thing we want to bring your attention to in the new asset browser is the use of Tags. Tags are a very powerful tool, not only for organising and filtering your assets, but also when programming your game in general as you can check for tag information in-game.
To create a tag you need to use the RMB menu on either a folder group or an asset and then select the Edit Tag option. This will bring up the Tag Editor, where you can type in the tag you want to use and then press enter to apply it:
You can assign multiple tags to a single item, and you can also select multiple assets and assign a tag to all of them at once. Once created, all tags are visible in Quick Access and also in the Filter options for the asset browser.
Finally, as we mentioned, tags can also be used in your project code to identify any asset and then act on it, making it a very powerful tool. For example, imagine you have a project with a "DamageOnTouch" tag added to some of your assets. When programming your player object you can check if things being collided with have this tag and then make the player object react, then at any time your artists and level designers can create new assets of any type and tag them with the "DamageOnTouch" tag to automatically pick up the same behaviour.
Animation Curves
The next new feature we want to discuss here is a new top-level resource called Animation Curves. An animation curve is an asset that contains one or more curves that represent how a value changes over time, and can use linear interpolation or smooth interpolation to go between different points in the curve. The values you set can be between -1 and 1 (by default) on the vertical axis and the duration along the horizontal axis is always normalised form 0 to 1, making it easy to target different time ranges using multipliers in your game code.
Animation curves are comprised of "channels" and each channel can have its own curve settings, which permits you to - for example - describe a spacial position with two channels for the x/y position, or a colour gradient with 4 channels to represent a colour format. Animation curve assets can be used when working with sequences and can also be accessed using code, making them a powerful tool when creating your games.
Sequences
And now we come to the big one... Sequences! This is a new top-level resource which is essentially a collection of assets that perform a dynamic animation over time. Sequences can contain sprites, instances, sounds and even other sequences - and each of these assets can be set to move or change colour, or start/stop animating over time within the sequence itself. The assets you add are assigned to specific tracks within the sequence and these tracks can then have different attributes applied to them - called keys - which can be static or change over time.
All editing of a sequence takes place in the Sequence Editor, which is set out similarly to much of the video/animation editing software that's available today:
The editor is comprised of the following three sections:
-
the Canvas - this is the main area at the top of the image above and where you place the assets that make up the sequence
-
the Track Panel - bottom-left on the image above, is where you add/remove asset tracks and parameter tracks to your sequence
-
the Dope Sheet - bottom-middle/right on the image above, is where you add/edit the keys and keyframes of the tracks and control how they behave over time.
Given the complexity of this asset, it's impossible for us to cover all its features in the space of this post, but let's briefly explain how a sequence is created and introduce some of the new keywords so you'll have a basic understanding of how the Sequences Editor works and can start playing with it straight away. The general workflow for creating a sequence is as follows:
-
First you'd create your sequence in the Asset Browser, which will open the editor also.
-
Next you'd drag an asset (sprite, object, sound or another sequence) onto the editor canvas.
-
This will create an Asset Track in the track panel and add an Asset Key to the Dope Sheet. The asset track is simply the name of the track that contains the asset, while the asset key is a bar that is drawn to symbolise the number of frames that the asset will be animated over in the sequence.
-
At this point, you'll usually want to position the asset in the canvas at its initial position for the start of the sequence and add in any initial transforms, like scale or rotation.
-
When you are happy with the initial settings, you will want to record the parameter keys - which is simply the name given to the values for scale, rotation, position, etc., that a specific asset key has at any point in the dope sheet timeline.
-
Next you'd change the position of the playhead in the dope sheet timeline and then move or change the asset in some way and then record another parameter key. This would then make your asset animated differently at these two keys.
-
Now you would repeat the above process for the length of the sequence, moving, rotating and scaling the asset as required, and then adding more assets as you need them.
All parameter keys are stored in additional parameter tracks, which are a subset of tracks under the main track in the track panel. You can edit the parameter key data for each parameter directly in the track panel as well as manipulating the asset on the canvas. The available parameters will depend on the type of track, but in general you can edit things like position, scale, colour, and volume.
That's the general idea behind creating a sequence, and we highly recommend that you read the manual before diving in and doing anything more than basic animations! You can also see our initial YouTube tutorials on the basics of Sequences for a good introduction to what sequences can do and how you create them in the IDE.
That said, it's worth calling out a couple of interesting features that may not be immediately obvious, the first of which is adding Moments and Events to them. Both of these features use functions and these can be added to the sequence to be called at specific times. When using Events, these work similarly to general object events, in that you can set an event to call a function, and when that event is triggered then so will the function (note that the functions used for Moments and Events can take no arguments). The events available are shown in the image below:
Moments are functions that can be called at a specific "moment" (frame) on the timeline, and these are added using the Add Moment button in the sequence editor:
Similarly, you can also add Broadcast Messages to a sequence, which is a string that is added to a frame of a sequence and when that frame is reached this string is "broadcast" to all instances with a listener event. This listener event is the new Broadcast Message Event which can be found in the Other event category of the object editor.
Adding a broadcast message is simply a case of moving the dope sheet playhead to the desired frame and then clicking the Broadcast Message button and then typing the string to be broadcast:
Another feature that's worth a specific mention here is the ability to create a Clipping Mask Group in the Track Panel of the sequence editor. A clipping mask group contains two sub-sets of assets that can be used as either the subject or the mask of the group. Basically the subject is the asset that is going to be masked, and the mask is the asset that is going to do the masking:
The idea behind using a clipping mask is that you add a subject sprite, and then a mask sprite, and the mask sprite will "hide" (mask) the subject. You can then use the mask sprite animation, or move or scale the mask sprite to change the visibility of the subject (or parts of the subject). To add a subject and mask, simply drag the sprite, object or sequence from the asset browser over the folder that you want to add it to (note that you can have multiple subjects and masks in one folder):
The mask sprite alpha will be used to "hide" whatever subject is underneath it, where the 100% alpha (opaque) will mask completely and 0% alpha (will show it). For example, we have this sprite for masking:
We can then apply this as a clipping mask to icons to create a "cooldown" effect, like this:
Note that clipping mask groups can be treated as a single item in the track list and dope sheet, and you can add rotation and position parameter tracks to the group as a whole. You can also add the usual parameter tracks to each of the assets used as the mask or the subject.
One final thing worth mentioning about sequences, is that they have a whole set of new functions associated with them, split into two general groups: room layer functions and dynamic sequence functions. The room layer functions are essentially the same as the ones that already exist for other layer elements like sprites and instances, while the dynamic sequence functions permit you to edit the contents and behaviours of a sequence at runtime as well as create new ones from scratch.
The Inspector
The new Inspector allows you to customise the values for your Sequence assets. You can open this from its command in the Window menubar when editing any sequence, then it will by default be docked below the asset browser.
This window shows all the details and editable properties for a single element in the IDE, and you can change values here which will also change them for the selected element.
You can lock an inspector to a single IDE element and then open another Inspector window to inspect a different element too, meaning you don't have to have the selected element in focus to edit its properties.
Right now, the Inspector is only enabled for use within the Sequence Editor and will only edit the asset types which can be added to a Sequence, but in time it will grow to be widely-used within several of the editors in GameMaker.
The Manual 2(.3).0
The manual has been changed to use a new technology and has a fresh new look:
As well as the usual tree layout and index, you will also see it's much better for searching now:
And for new users of GameMaker there is an excellent new introduction guide, which we would recommend as your first stop after installing GameMaker:
The manual also now always opens in your default web browser for maximum readability and productivity and there is no longer a preference to open it inside GameMaker instead, but please be aware that this is still a local copy of the manual as part of your GameMaker installation and shown to you via GameMaker's micro webserver - it is not an online version.
As always, you can find the manual inside GameMaker on the Help menu and by pressing F1.
Other Notable IDE Changes
When you load an older project you will get a reminder that it was created in an older IDE and may need some changes before it will run. This dialog will go again in a future version, but is important to be aware of just now as you're first starting out in 2.3.0.
The Go To tool now searches Preferences and Game Options, so you can jump to the setting you’re looking for:
There is no longer a separate “beta branch” to opt into – those of you who took part already saw the 2.3.0 Beta install was a secondary installation and does not share info with your 2.2.5 install, and this will be the system for all beta releases going forward.
We have overhauled the file-watcher system, so now project save/load is more efficient and also detects external changes in a more robust way with fewer “false-positive” dialogs being shown to you (you can still turn it off in Preferences if you don't want this functionality anyway). The new file watcher system uses the OS's own detection system rather than GameMaker polling the project folders for changes.
iOS splash screens and icons now use the newer packaging system Apple prefers, so there are fewer images you need to supply now (Generate Project Images has also been updated to account for this).
The Amazon Fire target has been removed - If you were using this target previously, please use the regular Android target from now on (just ensure you save your builds as the .apk package type).
Game Options > Add-Ons has gone - you should just use My Library to get those two Google/Amazon assets from the Marketplace as normal.
Marketplace > Create Asset no longer creates the package and uploads it first, before opening the website for you to set your metadata. Now, it simply opens your web browser so you can create your asset(s) and set your metadata on your publisher panel first, then you use Update Existing as per normal to actually upload packages when you're ready. You can also now upload packages up to 100MB, rather than the 20MB as in 2.2.5 and older.
And as a little extra, the code editor now automatically enables code-folding on all opening curly braces, so you can simply click to collapse/expand:
(Note that this has already been changed ahead of 2.3.1 so the fold point will be on the function and if lines, rather than the opening brace each time.)
New Runtime Additions
Even with all those new IDE changes, there have been just as many new goodies added on the runtime side in 2.3.0, so let's go over those now also…
However, there is one important thing to note before we carry on: Due to the project format changes, it is not possible to mix runtime and IDE versions this release. 2.3.0+ IDEs will not show 2.2.5 or older runtimes in Preferences unless you already have them installed and even then it will not let you set those older ones active. Similarly, if your update failed to set the correct runtime as active for some reason, then you will be alerted to this and stopped when you try to build any projects. (2.2.5 won't open your 2.3.0 projects, as it will say the project format is too new.)
Please do check the manual for much more detail on how all this new functionality should be used, as there is quite a lot to cover here and it may not immediately be understandable if you're new to this stuff. You may also need or wish to refactor some of your existing projects' code to take advantage of these changes, so it's definitely worth reading up later.
Arrays
The first change to the GameMaker language that we want to discuss is a change to how arrays work. Previously, GML Code only permitted 1D or 2D arrays, e.g.:
array_1d[0] = "hello";
array_1d[1] = "world";
array_2d[0, 0] = "hello";
array_2d[0, 1] = "world";
However, this is no longer the case and now all arrays are 1D. This change means that all platforms will now work consistently and also enables much larger arrays, both in number of items as well as number of dimensions.
So how do we create a 2D array now? Well this is done by chaining arrays together to essentially create arrays within arrays. Here is a small example:
array[0][0] = 1;
array[0][1] = "hello";
array[0][2] = 55.5;
array[1][0] = sprite_index;
array[1][1] = "world";
array[1][2] = -67.89;
You are also no longer limited to just 2 dimensions and can create arrays of 3+ dimensions as required - e.g. array[0][0][0][0] = 1; would give you a four-dimensional array.
This change does means that the array_length_1d(), array_length_2d(), and array_height_2d() functions have been deprecated and the new functions array_length(array) and array_resize(array, new_size) have been added. The deprecated functions will still work after converting your project, thanks to compatibility layer functions using the old names which will be added to your project, but we recommend you change your code to the new versions as these compatibility functions will eventually be removed from the language, and you can only use the new ones in new projects.
Chained Accessors
Much like you can now chain arrays together, you can now chain the different Data Structure accessors together too, making accessing the data from nested structures much easier.
A simple example of this would be a ds grid where each grid entry is a ds list. Previously you'd have to first retrieve the index of the list from the grid and then access the data in the list, but now it's as simple as doing something like result = myGrid[# 0, 0][| 5]; and this would set result to the value in the 6th position of the DS list that is being held in the grid at 0,0.
Other examples of use are:
// Access a grid that has been added to a list that is part of a map:
var _a = data[? "lists"][| 0][# 0, 0];
// Access an array nested in a list from a function and modify it:
data[| 0][@ 10] = 100;
// Access a map nested in a grid nested in a list nested in an array:
data[0][| 10][# 3, 4][? "key"] = "hello world";
One very important thing to note is that because of this new chained accessor support, using a prefix operator immediately after an expression will now require you to ensure you have used a { otherwise the compiler will give you an error. This is because the compiler cannot guarantee that the ++/-- belongs to the expression or the thing which comes after. Hence, you need to use "correct" coding standards to control this. E.g.,
- if (true) ++compilerError;
- if (true) { ++safeCode; }
Please be aware you may need to make this fix to your code in any existing projects!
Functions, Scripts and Method Variables
Above we outlined some changes to existing functionality in GameMaker Language, but now it's time to talk about the good stuff... what's been added!
To start with we want to talk about in-line functions and script functions. Previously, a script was a single resource that was created on a global scope and used to create a single custom function which would then be called using the script name as the function name. This is no longer the case, and scripts, while still global in scope, are now a type of "container" for one or more functions. What this means is that when you compile your project, scripts are no longer called individually, but are actually all run at the start of the game in the global scope, so all variables that are defined in the script (outside of function definitions) will be considered global.
Note: that said, you should still use the global identifier to explicitly define and identify global variables in your code.
The reason for this change is that scripts can now contain multiple functions, which can now be used as methods. If you've only used GameMaker to code projects, then the idea of functions may be something new to you, but think of them as being the same as scripts, only explicitly assigned to a variable. This variable is called a method and is used to call the code that is in the function, the same as you would have previously called the script name. This is easier to visualise with an example, so let's look at one...
Consider this simple script move_follow():
/// @function move_follow(_object, _speed);
/// @param {index} _object The Object to follow
/// @param {real} _speed The speed to follow at
var _o = argument0;
var _s = argument1;
if (point_distance(x, y, _o.x, _o.y) > 0)
{
direction = point_direction(x, y, _o.x, _o.y);
speed = _s;
}
else { speed = 0; }
Now, however, this would be defined as a script function like this:
/// @function move_follow(_object, _speed);
/// @param {index} _object The Object to follow
/// @param {real} _speed The speed to follow at
function move_follow(_object, _speed)
{
if (point_distance(x, y, _object.x, _object.y) > 0)
{
direction = point_direction(x, y, _object.x, _object.y);
speed = _speed;
}
else { speed = 0; }
}
or as a method like this:
/// @function move_follow(_object, _speed);
/// @param {index} _object The Object to follow
/// @param {real} _speed The speed to follow at
move_follow = function(_object, _speed)
{
if (point_distance(x, y, _object.x, _object.y) > 0)
{
direction = point_direction(x, y, _object.x, _object.y);
speed = _speed;
}
else { speed = 0; }
}
You would then call that just as you would have called the script in 2.2.5 or older - e.g., move_follow(obj_Player, 5);
Be aware also that when you create a function with a variable like this, you are creating a method variable, and they can have different scopes. Consider this function:
foo = function() { ... }
-
In the case of a script, a variable called "foo" is declared at global scope
-
If the function is declared in an event then the variable "foo" is declared on the instance that ran that event
-
If the function is declared in an event using the
var
keyword then the variable "foo" is local to the event only -
If "foo" is declared inside a
with
then it is declared on theself
that is active at that time
This means that if you have some code that you need to use only within a loop in an alarm event (for example), then you can define it as a local scope method variable at the start of the event and then use it within the loop without the need to clutter up the script assets with it.
It is worth noting that while the variable will be in the chosen scope, the actual function will be bound to the scope that it was initially defined in. Going back to script functions, these are all global scope and are considered "unbound" (i.e.: they are not associated with any instances), but if you have a script function that creates another function within it and then you call this script from an instance, the function used within the script will be bound to the instance. In general this is not something you ever need to think about but for more complex operations with method variables it's worth taking into consideration. This also applies when using other constructs like with
- when you create a method variable inside a with
, the function will be bound to the instance that is currently in scope.
The new functions is_method(variable); and method(instance_id, function); have also been added to deal with methods.
And finally for this section, as we mentioned above, scripts can now contain multiple functions and these should be defined with the format shown above where you have the JS doc comment to identify the function, then the function definition below, and then the next JS Doc comment, and then the function, etc. Something like this:
Structs
The next new feature that we want to discuss is the creation of structs. A struct is in simple terms "a variable that holds a collection of other variables" - you can think of it as a kind of "light-weight object" also if you wish. The variables that a struct holds can be of any data type and these variables can be read from and written to after the initial struct declaration, and you can also add more variables to a struct after it has been declared. It should also be noted that the contents of a struct are independent of the instance or script that created it, and as such you can if you wish reuse/override the built-in variable names like image_index
or x
and y
.
One of the benefits of using structs is that (unlike instances), after their initial creation structs have no processing overhead while they exist, although they will take up space in memory.
The struct syntax is similar to a function in that it uses a declaration followed by opening and closing braces which contain code in the middle - in this case, pairs of variable names and initial values - and it also has a semi-colon after the closing brace. So, an example of this in practice could be:
mystruct = {
a : 20,
b : "Hello World"
};
The above creates an instance-scope struct in the variable "mystruct" and populates it with some values (structs can be created at local, instance and global scope, just like any other variable).
Note that you don't have to populate the contents of a struct when it is created initially and you can create an empty struct by simply calling mystruct = {}; and this struct can then be added to at a later point in the game code.
Here is an example of a struct with various variables and data types:
var _xx = 100;
mystruct = {
a : 10,
b : "Hello World",
c : int64(5),
d : _xx + 50,
e : function( a, b ) { return a + b; },
f : [ 10, 20, 30, 40, 50 ]
};
You'll notice in the above code that you can also define methods and use runtime functions in structs, and that you can use expressions consisting of any variable previously defined within the context of the struct itself, as well as any variable defined within the scope of the struct itself.
Once a struct has been defined, you can access the data within using the "point" notation, like this:
mystruct = {
a : 20,
b : "Hello World"
};
mystring = mystruct.b + string(mystruct.a);
You can also perform operations on the variables within a structure or use them in functions, just as you would any other variable. For example:
mystruct.a += 1;
mystruct.b = mystruct.a + 20;
mydir = point_direction(mouse_x, mouse_y, mystruct.xx, mystruct.yy);
Finally, structs can have other structs nested inside of them, like this:
mystruct = {
a : { aa : "This is an example" },
b : { bb : "And another one" },
};
To access such nested structs you would still use the point notation, like this:
var _str = mystuct.a.aa + " " + mystruct.b.bb;
show_debug_message(_str);
When a struct is no longer required it can be removed from memory using the delete
operator, which is another new GML Code feature added in the 2.3 update. This de-references the struct and hints to the garbage collector that it may be available for collection. This is not strictly required as the garbage collector (more on this in a later section) may do this automatically in the following game steps if the struct is no longer referenced in your code, but it is good practice to do so and we recommend it (for example, call delete in the Clean Up event of an instance to explicitly tell the garbage collector that an instance scope struct is to be deleted).
Also note that structs can be created using functions, which requires the use of the new
operator and the keyword constructor
(two more new features to GML Code), as shown in the following example:
Vector2 = function(_x, _y) constructor
{
x = _x;
y = _y;
static Add = function( _other )
{
x += _other.x;
y += _other.y;
}
}
Here we are creating the function Vector2
and telling GameMaker that this is a function for making a struct by adding the constructor
keyword after the definition. You can then call this function like this:
v1 = new Vector2(10, 10);
Now the variable v1 will reference a struct with the variables x and y and the static method variable Add
.
Note that there are other additional functions for structs, specifically:
instanceof(variable);
is struct(struct_id);
variable_struct_exists(struct_id, variable);
variable_struct_get(struct_id, variable);
variable_struct_set(struct_id, variable, value);
variable_struct_get_names(struct_id);
variable_struct_names_count(struct_id);
One very important thing to note is that because instances are now structs, "self" and "other" used by themselves now return the whole struct of an instance, not only its "id" variable. This means any code such as
with (instance_create_layer(x, y, "Instances", Obj_fire))
{
myID = self;
myParent = other;
}
now needs to be
with (instance_create_layer(x, y, "Instances", Obj_fire))
{
myID = self.id;
myParent = other.id;
}
Please be aware you may need to make this fix to your code in any existing projects!
Handling Exceptions With Throw(), Try(), Catch() and Finally()
2.3.0 also has the ability to control to a much greater degree how errors are handled when they are encountered in your code, as well as the ability to generate your own error messages.
To start with we have the new throw
operator, which - as the name implies - can be used to "throw" a runtime error message. This has the following syntax: throw <expression>;
The expression used can be a value or a string or any other data type, and this will then generate an exception error which is - by default - shown on the screen, and on closing the error message the game will end. For example calling this: throw "Hello World!";
will cause the following unhandled exception error to be shown:
This is the default way the error can be handled, but you can "take over" this error message and use your own handler code by calling the new function exception_unhandled_handler()
. This runtime function permits you to supply a custom method to use that will be called whenever any unhandled exceptions occur in your game.
Apart from the ability to throw your own errors and handle them using functions, we also have the new try
, catch
, and finally
operators. These can be used in your game for error checking and permit you to test out blocks of code and control what happens if any runtime exceptions occur. Using these will prevent the exception ending the game and will also prevent showing the standard error message to the user, but this means that you will have to handle what happens next in this case, like - for example - saving out log files and ending the game gracefully (note that if you choose to do nothing, your game may become unstable and not perform correctly).
At its most basic the try
syntax is as follows:
try
{
<statement1>;
<statement2>;
etc...
}
However, having a try
without anything to actually handle any exceptions the code may produce will not be very helpful, so we usually pair it with a catch
, with the following syntax:
try
{
<statement1>;
<statement2>;
etc...
}
catch(<variable>)
{
<statement1>;
<statement2>;
etc...
}
What catch
does is permit you to run extra code supplied in the following block when an exception from the previous try
has been caught. If this is a runtime exception, then the supplied variable can be used to access a struct which will contain the following information:
{
message : "", // a string that is a short message for this exception
longMessage : "", // a string that is a longer message for this exception
script : "", // a string that describes where the exception came from
stacktrace : [ "", "" ], // an array of strings that is the stack frame the exception was generated
}
A simple example of use is shown below:
var a = 0, b = 0, c = 0;
try { c = a div b; }
catch( _exception)
{
show_debug_message(_exception.message);
show_debug_message(_exception.longMessage);
show_debug_message(_exception.script);
show_debug_message(_exception.stacktrace);
}
It may be that you want to run some code regardless of whether an exception was thrown or not, and so for that you can add in a finally
block. The finally
syntax is:
finally
{
<statement1>;
<statement2>;
etc...
}
It is worth noting that you can have any combination of these together, ie: try
/ finally
, try
/ catch
or try
/ catch
/ finally
, and that within the finally
block you cannot use break
, continue
, exit
or return
statements as they have no meaning in this context and the compiler will generate an error if they are used.
Other Notable Runtime Changes
The runner now has a multi-stage garbage collector to clean up in-game memory and maintain performance. This is all automatically done for you, but there are functions to allow you to call it on-demand also and even to turn it off if you wish.
All scripts are now run on game start in global scope. If you only want them to run on-demand, then you need to make them a function instead. Accordingly, you will find on project conversion that your scripts are now wrapped in a function declaration – it’s this format which you should use going forward. We added a little helper/reminder comment about this to converted scripts – future releases will allow you to turn this reminder off, but right now it’s there to stop confused tickets.
Note: if you do have any "initialisation" scripts your game needs to run on game start, then you may need to remove the function() text the conversion process added to all your scripts, so that this script then becomes global scope again.
This version contains fixes for macOS SSH issues during builds following changes in Catalina 10.15.4. There is also a fix for changes to Xcode a couple of months ago which meant that some projects would fail to actually run after building.
If you are one of our console users, be aware the SDK requirements have changed and you will need to be using newer versions than 2.2.5 supported, otherwise builds will fail – you should review the relevant Required SDK FAQ(s) for more info.
Bug Fixes
There are a tremendous number of bugs fixes compared to 2.2.5 in terms of project save/load, IDE performance, building projects, and lots of in-game fixes - far too many to list all of them here, but we do list all the public bug-tracker links on these two pages (IDE, Runtime) and so we would encourage you to review this list. Note, however, that this is a very small list compared to the full private change log and you will no doubt find a lot of old issues have been fixed.
However, just one quick final reminder to please be aware of the following “notable” major changes you may need to react to before your game will build or work correctly in-game after converting your project to the new format:
- Ensure you're using the correct SDK/tools versions still: Required SDK FAQ
- Make sure your starting room is set correctly and that if your game always runs its rooms in a specific order, then you apply this also
- If you do have any "initialisation" scripts your game needs to run on game start, then you may need to remove the function() text the conversion process added
- Ensure anywhere you used a prefix operator immediately after an expression you have then used a { around the next bit of code
- Please be aware that there are a number of new function names and keywords in 2.3.0, which may cause you to need to rename things in your own projects in order to avoid errors either when compiling or in-game (please search the manual for any name clashes if you get unexpected errors)
- We would advise (but it's not necessary) that you refactor any code which uses the old array "..._1d" or "..._2d" functions to instead use the new versions and then you can remove the compatibility function from your project
- Similarly, any two-dimensional array declarations using the [0,0] format will work fine, but are using the compatibility layer also and should be [0][0] going forward
Report any bugs!
Obviously, please do have a lot of fun trying out all the new changes and playing around with the new capabilities 2.3.0 offers. The manual should have content for all of the above changes, so if you spot something is missing or feel the level of information given is lacking, do let us know.
Should you have a project which fails to import from 2.2.5 to 2.3.0, please do report this and give us a download link for the original .yyz you're trying to import. We would note that if the project wasn’t exported from 2.2.5.481, then you check your .yyz does import correctly in 2.2.5.481 and then you re-export it, as that 2.2.5 release had a number of fixes and “preparation changes” for the move to 2.3.0 – if it still fails after re-exporting from 2.2.5.481, then please send that new .yyz to us.