Creating A Native Extension For iOS (GMS v1.3+)

With the GameMaker: Studio update to version 1.3, the extension system was re-vamped to permit native extensions on different platforms, one of which was iOS. This simple tutorial takes you through the basic steps necessary to create an iOS extension and call a function using device native code. Note that this tutorial assumes you have a good working knowledge of Objective C as well as GameMaker.

Note: For an overview of how basic Windows extensions work, please see the article Creating An Extension For Mac, Windows, And JS Targets. If you are new to extensions it is recommended that you read this article too.

A Simple Example

The following is a very simple example of adding a single new function to GameMaker: Studio that will use custom Objective C files to return information to GameMaker: Studio..

The Basics

Before going any further, you should first create a new room, a new object and a new sprite. We are keeping this simple, so all we want is a button sprite which can be assigned to the object, and the object should be placed in the room. This "button" will call our new iOS native function.

Create The Extension

We now need to create our extension. This is done by first right clicking on the Extensions folder in the resource tree and selecting "Create Extension", which will bring up the "Extension Wizard" window and here to start with you only need to name your extension and give it a version number, then check the iOS check-box. Once you have done that you should click the Next button.

Extension_properties.png

Adding The Native Source Code

After clicking Next you will be taken to another tab where you can give the Class Name and add in information that your Java or SDK requires. For our simple extension you only need to give the class name "GenericTest".

Extension_properties_ios.png

 

Here you give the following details:

  • Linker Flags: Some frameworks and third party SDKs require the addition of extra linker flags to work which can be specified here (see the documentation that accompanies the SDK or framework in question for details).
  • ClassName: Your extension can have multiple classes, with each class having its own functions and constants, so you should give it a name that reflects its purpose

We don't need to add any linker flags here, and you only need to have the class name filled in with "GenericTest". You can see two further windows at the bottom of this tab. These are provided so that you can add weak references into in any iOS system frameworks or third party frameworks (see the documentation that came with your chosen SDK for info on the framework name) to your extension. To add information here you must first have added the appropriate files to the extension, and although our extension needs none of this, we'll briefly explain how this can be done.

To add a framework you need to right click on the extension and select Add iOS Framework From Mac and then browse to the files you wish to add. Added files will be stored in the iOSSourceFromMac directory along with your extension. You can open this location at any time by right clicking on the extension and selecting Open Extension Directory

For our example you can leave these blank, and click the Create button at the bottom to create our base extension.

Adding The Native Source Code

Once you have set this up correctly, you will need to add the required files for your extension package to work. This will consist of a *.h file and a *.mm file, so, let's create a them with our source code to add. For this you will need a text editor (like Notepad++ for example). Our extension will do several things:

  • It will return two numbers added together.
  • It will return a string made up of an input string and an input value.
  • It will return a string made up from two input strings
  • It will return values to the Social Asynchronous Event

For this example we will start by creating a file called "GenericTest.h", with the following code:

@interface GenericTest : NSObject
{
}

- (double) AddTwoNumbers:(double)arg0 Arg2:(double)arg1;
- (NSString *) BuildAString:(char *)arg0 Arg2:(char *)arg1;
- (NSString *) HowManyObjects:(double)arg0 Arg2:(double)arg1 Arg3:(char *)arg2;
- (void)ReturnAsync:(double)arg0 Arg2:(double)arg1;

@end

Save that to a location on your hard drive and then create another file called "GenericTest.mm". In this we have the following:

#import "GenericTest.h"
#include <asl.h>
#include <stdio.h>


@implementation GenericTest

const int EVENT_OTHER_SOCIAL = 70;
extern int CreateDsMap( int _num, ... );
extern void CreateAsynEventWithDSMap(int dsmapindex, int event_index);

- (void)ReturnAsync:(double)arg0 Arg2:(double)arg1
{
    int dsMapIndex = CreateDsMap(3,
        "type", 0.0, "finished",
        "argument0", arg0, (void*)NULL,
        "argument1", arg1, (void*)NULL
);
CreateAsynEventWithDSMap(dsMapIndex, EVENT_OTHER_SOCIAL);
}

- (double) AddTwoNumbers:(double)arg0 Arg2:(double)arg1
{
    double value = arg0 + arg1;
    NSLog(@"yoyo: %f + %f = %f", arg0, arg1, value);

    return value;
}

- (NSString *) BuildAString:(char *)arg0 Arg2:(char *)arg1
{
    NSString *value = [NSString stringWithFormat:@"%s%s",arg0,arg1];//[arg0 stringByAppendingString:arg1];
    NSLog(@"yoyo: %s + " " + %s = %@", arg0, arg1, value);

    return value;
}

- (NSString *) HowManyObjects:(double)arg0 Arg2:(double)arg1 Arg3:(char *)arg2
{
    double value = arg0 + arg1;
    NSLog(@"yoyo: %f + %f = %f", arg0, arg1, value);
    NSString *arg2ns = [NSString stringWithFormat:@"%s",arg2];
    NSString *myString = [NSString stringWithFormat:@"%f %@", value,arg2ns];
    NSLog(@"yoyo: %@", myString);

    return myString;

}

@end

Once you have created your file, save it to a location on your hard disk as "GenericTest.mm", and then go back to GameMaker: Studio to add it to the extension. To do this you need to right click on the extension and select either Add iOS Framework From Mac or Add iOS Source Directory and then browse to the directory containing the files you wish to add. In this case we want to choose Add iOS Source Directory and browse to the directory where we saved our file. Added files will be stored in the iOS Source directory along with your extension and can be edited directly there with any changes being detected automatically by Gamemaker: Studio. You can open this location at any time by right clicking on the extension and selecting Open Extension Directory

You can download copies of the code given above here:

GenericTest.mm
GenericTest.h

Creating the GML Functions

Next you need to add the function declarations to our extension in GameMaker: Studio so that your code can call your new Java extension. To do this, right click on the group file "GenericTest.ext" that we added earlier, and select Add Function. This will open a new window where you can add your function and assign its arguments and properties.

To match the code you created earlier, you should give the function names and details the same as those listed below:

function / external name: AddTwoNumbers
help: AddTwoNumbers(value1 , value2);
argument0: double
argument1: double
return type: double

function / external nameHowManyObjects
help: HowManyObjects(value1, value2, string);
argument0: double
argument1: double
argument2: string
return type: double

function / external nameBuildAString
help: BuildAString(string1, string2);
argument0: string
argument1: string
return type: double

function / external name: ReturnAsync
help
: ReturnAsync(value1, value2);
argument0: double
argument1: double
return type: double

With that finished, we can start to program our test game to use these functions.

Calling The New Extension Functions

Our new extension functions can now be used just as you would any of the built in GameMaker: Studio functions, and if you have added the "help" string then your function will also appear in the auto-complete and syntax checker. To call our functions we need to create an object and assign it a sprite, then add a Create Event to initialise the variables required:

result1 = "!! Not run !!";
result2 = "!! Not run !!";
result3 = "!! Not run !!";
result4 = "!! Not run !!";

You can now add in a Mouse Left Pressed Event with the following code:

result1 = string(AddTwoNumbers(irandom(100), 50));
result2 = BuildAString("Hello", "World");
result3 = HowManyObjects(irandom(1000), irandom(1000), "Targets");
ReturnAsync(irandom(1000), irandom(1000));

You should also add a Draw Event to show the result variables on the screen so that you can see whether the extension has worked.

The ReturnAsync() function will require that you add a Social Asynchronous Event to the object, as it will be triggered by the function. In this we can have the following code:

var type = async_load[? "type"];
if type == "finished"
    {
    result4 = "value1: " + string(async_load[? "argument0"]) + ", ";
    result4 += "value2: " + string(async_load[? "argument1"]);
    }

You should now be able to test your game on an iOS device. You can test directly from GameMaker: Studio using the "play" button or compile and test an xarchive. Both methods should work, but remember that the first time you test you will need your device connected to your Mac over USB..

Things to Note

It is worth noting that you should store your source files with the folder structure (this example assumes you're doing cross-platform versions of your extensions and "C:\MyExtensions" can be replaced with any folder on your machine):

  • C:\MyExtensions\Extension1\Source\Extension1.h
  • C:\MyExtensions\Extension1\Source\Extension1.mm
  • C:\MyExtensions\Extension2\Source\Extension2.h
  • C:\MyExtensions\Extension2\Source\Extension2.mm

Not the simpler setup of:

  • C:\MyExtensions\Source\Extension1.h
  • C:\MyExtensions\Source\Extension1.mm
  • C:\MyExtensions\Source\Extension2.h
  • C:\MyExtensions\Source\Extension2.mm

As the way the iOS compiler works is to build all files in the Source folder into one package, not simply the class you point to inside GM:S. So if you wish to do this, every file will need to be error-free and you're adding bulk into your project (assuming it even built okay).

Setting Export Options

You should also set the export options for the extensions that you make. This ensures that your files will only be exported with the final executable for the appropriate target platform. To set this, you need to right-click on the GenericTest.ext file in the resource tree and select Properties. un-check everything except iOS.

FileProperties.png
Have more questions? Submit a request

0 Comments

Article is closed for comments.