Now that you have followed Introduction to Creating Extensions in GMS2 you understand how to use the extension editor and make simple GML-based extensions.
In this guide we will now extend that knowledge to show how you can create a Xcode project and from that project make yourself a basic .dylib ("dynamically linked library") that can be used with GMS2 to provide an alternative means of detecting a collision between two instances.
There is a pre-made project available for download at the bottom of this page, but we would strongly recommend you follow along with the guide and only use the supplied copy as a sanity-check if things aren't working correctly in your own attempt.
Important note: This guide assumes a little knowledge of programming in C/C++ and a basic familiarity with Xcode. It is not a C/C++ tutorial, nor is it a guide to navigating Xcode's UI.
Why Create DyLib Extensions?
When making macOS games you may sometimes need to create entirely new functionality that GML cannot provide - for which you will need to write code outside GMS2 and create a more advanced form of extension.
(While GMS2 does already provide a good collection of collision methods, it is conceivable to have extra requirements for your game that are not available in GMS2 itself, so this is a nice little sample project we chose to make for this guide.)
Create Your Xcode Project
When you create a new project you should select the Library option, as this will populate the settings correctly for the creation of a DyLib.
You will then have to provide some options for the project:
Whilst we have not added our own details above, as we didn't want to confuse the screenshot, it is essential that the Team and Organisation Identifier fields are filled in correctly before you proceed. This should be the same values that you would use for your GMS2 projects inside Game Options.
Framework is fine to leave on "STL C++".
Type should be "Dynamic".
When you click Next you will be asked where to save the project, and once that has been done a project will be generated. This will have some files pre-made:
These extra .hpp files should be deleted just now, as we will not be using them for this guide.
The contents of the .cpp file should also be removed just now (but leave the empty file there), as we will add our own code to it during this guide.
Code Your DyLib
Within your empty cpp file it is now time to start adding code that will be part of the DyLib. But first, adding the following line should make things a little easier to read and write our code:
#define func extern "C"
This line looks pretty complicated, but here's a breakdown to show what is going on:
"#define func" will replace all instances of func in the code with the text that comes after.
'extern "C"' is a compiler flag that makes it use C-style compilation for the function. This is done because C++ may add extra information to function declarations, which we don't want.
This one line is added first so that each time we declare a new function we just have to use the keyword "func" as shown in the snippet below (this is just an example, don't add this to your own code):
func double TestFunction() {
return 1;
}
Now that we have added that one line and explained what it's for, we can get to work properly...
The idea behind this library will be to provide collision detection functions, so let's start with a simple test for "Is a point inside a circle?". The code would be:
func double IsPointInCircle(double xx, double yy, double cx, double cy, double cr) {
//xx,yy is the point, cx,cy is center of circle, cr is circle radius
// x^2 + y^2 < r^2 - distance betwen point and center compared to radius
// if its less that radius the point is inside so returns 1, else it will return 0
return ((xx - cx)*(xx - cx) + ((yy - cy) * (yy - cy)) < cr*cr);
}
You can see the explanation of the maths in the comments in the above code, so we won't repeat that, but the important part for our DLL is the use of the defined "func" to declare the function and the type being a double. GMS2 will only accept types of double or string from extensions and as we are here intending on passing a number back from the extension we must say we will use a double.
Note that your Mac will likely ask you for your keychain access password and confirmation you allow using the Team identifier that was selected during the project creation before it will actually do the build in the step below - say yes to this and add your password, etc.
Now you have some code, go ahead and build the Xcode project and when the build has finished, you can find the DyLib simply by selecting the "Products" node in the project tree:
The .dylib shown here can be right-clicked (Ctrl+click) to bring up a context menu and then select "Show in Finder". It is this file you will be adding to your GMS2 extension asset.
Now move that file to a more easy-to-access location.
Create Your Extension Asset
Inside GMS2, create an Extension asset. (For a reminder of this process see the Introduction to Extensions guide again.)
In our example there are 5 arguments and they all need to be Double. Be aware that DLL extensions require that all arguments are the same value type and due to this requirement you will likely see a warning when you start changing these values, but this is expected.
Below is how you need to configure this extension in order to work with the example C++ code we wrote earlier in this guide:
Use Your New Extension In Your Game Code
We can now use this function to create a test to check if the mouse has "collided" with the instance.
Obviously, GMS2 has an existing "Mouse Enter" event which does much the same as this, but limits you to the collision data of the current object's sprite and it may be you wish to have collisions happen without a sprite or happen well outside the sprite's area, so hence our extension.
Inside the project, create an Object and a Sprite. The sprite needs to have some content of your choice and then be assigned to Object1.
Add Object1 to the instance layer within Room1 and then just close the room editor again.
Back in the workspace, give Object1 a new Step event. Now simply type the code required to setup your game values and call your extension function:
xx =mouse_x;
yy =mouse_y;
cx = x;
cy = y;
r = 100;
if(IsPointInCircle(xx,yy,cx,cy,r)){
instance_destroy();
}
Because our function returns 1 or 0 depending on if the given point (xx,yy) is within a circle centred at (cx,cy) and having radius (r), when the mouse moves within that radius a collision response can be activated. In this example we then destroy the object.
Once that code is typed out, it's time to run your game.
Running The Project
In GMS2 2.3.1 and before, we would recommend you run this project using YYC. The reason for this is due to macOS's "hardened runtime" requirement for the runner to be signed with your team ID along with all extensions in the project, this means that you currently need to manually codesign the dylib outside GMS2 before bringing it into your project.
If you still wish to run VM in 2.3.1 or older, please see this guide on notarisation.
Ahead of 2.3.2 we are investigating if it is possible to lift this requirement when developer testing on your machine (you would always have to codesign/notarise any package you want send to someone else) and therefore you would not need to have already pre-codesigned the dylib for your project to run/debug in VM.
Regardless of which route you take, once you have your game build, move your mouse towards your instance of Object1 and you will see when it gets close to the centre of its sprite (exactly how close depends on the size of Sprite you created) the instance will be destroyed.
It works! (No point in us adding a screenshot of an empty game window here, but you should of course prove it to yourself.)
You Made A DyLib Extension
That's it for DyLib extensions! You now know how to add further functions into your .cpp file, add them into the Extension Function editor, and call them in your code.
Remember that DyLibs can only be used for macOS platforms. Other desktop platforms' native extension types, plus cross-platform GML extensions, are covered in other guides here on the Helpdesk, so you're welcome to follow those also.
Proxy Files: Same Code, Different Platform
If you have already completed (or plan to complete next) the guide for creating a Windows DLL extension, you will realise that we used essentially the same code for both extensions but they are added to your GMS2 project as separate extensions and so you needed to repeat the Extension Editor steps twice. Obviously, if you have many functions in your extension this is undesirable, as it would take a substantial amount of time.
The solution here is to include the other file (seeing as we're in the DyLib guide, that would be the DLL) as a Proxy File within our existing extension.
It is important to be aware that proxy files follow a strict naming convention: they must share the same base filename and may require a suffix depending on the target, so you may need to rename your .dll/.dylib files before adding them.
To add a proxy file, simply click the + icon on the extension file properties:
Select the other library file and select OK.
Ensure that the Target option is set to the correct platform for the type of library package you just added and now it's ready for use.
If you had any issues with your own attempt when following the main part of this guide (before everything to do with Proxy Files), please see this working copy and compare it to what you did: