top of page

Dynamic Object Creation in Objective-C

  • Writer: Yi Yin
    Yi Yin
  • Apr 23, 2018
  • 3 min read

Updated: May 2, 2018


This is the first one of the series of articles on technical tips of game engine development. It comes from my experience making the engine for our award-winning games, the Ballade series. Since they are iOS games, the description will be majorly in Objective-C, and sometimes other scripting languages.


Dynamic Object Creation, is the process that creates an object whose type remains unknown until runtime. It is very useful in game programming, or where we need to add different types of new objects irregularly. Dynamic Object Creation usually goes with the "Factory" design pattern, giving your program both elegance and flexibility.


Problem

Assume you are using a scripting language - let's say Lua - to control the creation of different game objects, whose corresponding classes are written in Objective-C. At first, you have only one type of game objects to create: grass. So you implemented that in Objective-C:


[Objective-C]

@interface Grass

// ...

@end


You hope you can use Lua code in your script files to create a grass asset at (50,50) like this:

[Lua]

game.createGrass(50,50)


Therefore, you have to implement a "bridge" function (Lua => Objective-C):

[Objective-C]

-(Grass*)createGrassAt:(int)x y:(int)y;


Very well. With the bridge function and Lua engine, each time you call game.createGrass(x,y) in Lua, it is then forwarded to function -(Tree*)createGrassAt:(int)x y:(int)y in Objective-C. Everything looks so good so far, until you decide to add another 2 types of game objects: sand and blueberry. Now the Objective-C files look like this:


// Grass.h & Grass.m

@interface Grass

// ...

@end


// Sand.h & Sand.m

@interface Sand

// ...

@end


// Blueberry.h & Blueberry.m

@interface Blueberry

// ...

@end


And you want to use them in your Lua files like this:

[Lua]

game.createGrass(100,200)

game.createSand(300,50)


Or even batch-create a lot of game objects (e.g. blueberries because you REALLY love them):

[Lua]

local pos_all_berries={ {x=10,y=10}, {x=50,y=50},{x=100,y=100} --[[and many others]]-- }

for i,pos in ipairs(pos_all_berries) do --in this for loop, i is the index, and pos is pos_all_berries[i]

game.createBlueberry(pos.x, pos.y)

end


Therefore, you have to write 3 bridge functions (Lua => Objective-C):

[Objective-C]

-(Grass*)createGrassAt:(int)x y:(int)y;

-(Sand*)createSandAt:(int)x y:(int)y;

-(Blueberry*)createBlueberryAt:(int)x y:(int)y;


Very well. You feel satisfied about all those functions...until your boss tells you that there are 20 types of game objects for you: grass, bush, tree, forest, woodlands, sand, pebble, stone, rock, boulder, blueberry, strawberry, apple, watermelon, pumpkin, ..., etc. Now what will you do? Writing 20 bridge functions?


Analysis

Actually you would hope you can do something like this:

[Lua]

game.createObject("Tree", {x=50,y=50});

game.createObject("Stone", {x=100,y=100});

--and many other game objects


Or even:

[Lua]

local objects=[

{type="Tree", args={x=50,y=50}},

{type="Stone", args={x=100,y=100}}

--and many other game objects

]

for i, entry in ipairs(objects) do --same as previously, entry will be objects[i] in this for-loop

game.createObject(entry.type, entry.args)

end


[Objective-C]

-(GameObject*)createObject:(NSString*)type with:(NSDictionary*)args{

}


Solution

Now here's the solution: Dynamic Object Creation. In Objective-C, you can use an NSString object (as the class' name) to create the class' instance:


[Objective-C]

Class cls=NSClassFromString("Tree");

assert(cls);

[[cls alloc] init];


Therefore, we can redesign the Objective-C classes of all game objects like this:

[Objective-C]

@interface Tree

-(instancetype)initWithArgs:(NSDictionary*)args;

//...

@end


@interface Stone

-(instancetype)initWithArgs:(NSDictionary*)args;

//...

@end


// and so on. Just make sure all game object classes should implement -(instancetype)initWithArgs:args


Now, in the bridge function, we write this:

[Objective-C]

-(GameObject*)createObject:(NSString*)type with:(NSDictionary*)args{

Class cls=NSClassFromString(type); // now cls is the class type like Tree/Stone/Apple

assert(cls);

return [[cls alloc] initWithArgs:args];

}


Summary & Further Discussion

The real question here is: can we use a string to create a class instance? Objective-C is quite dynamic and it offers such mechanic to create a class with a string. You may think further: can we make a function call from a string? The answer is YES. So far as you know the signature of the function, you can construct a function call dynamically. In Objective-C, a function is called a "selector" with its "implementation". Here's the code to construct a function call from a string:


// assume caller is the object, funcName is the function's name, and arg is the argument

SEL selector=NSSelectorFromString(funcName);

if ([caller respondsToSelector:selector]) {

IMP imp=[caller methodForSelector:selector];

id (*func) (id,SEL,NSString*)=(void*)imp;

assert(func);

return func(caller,selector,arg);

}else{

NSLog(@"Object does not respond to the function!");

return nil;

}


This is dynamic function call. If you want to get many properties of a game object via Lua, the worst thing to do is writing as many "bridge" functions in Objective-C. You should use dynamic function call like this:

[Lua]

game.getProperty(obj1, "x"); // get the x position of the game object "obj1"

game.getProperty(obj2, "collisionMask"); // get the collision mask bound to game object "obj2"


[Objective-C]

-(NSValue*)getProperty:(GameObject*)obj propertyName:(NSString*)name{

// here is a question for the reader of this post:

// can you implement this function by yourself?

}


Comments


YI YIN
Austin, Texas

Personal Email - if you like me

*Please replace "_at_" with "@"

kthtes_at_gmail.com

Studio's Email - if you like our games

*Please replace "_at_" with "@"

286studio_at_gmail.com

©2017 BY YI YIN

bottom of page