Categories
entity systems programming

Artemis Entity System in ObjectiveC

I wanted to try the latest version of Artemis, and I had an old game project that was quickly written in OOP style. So I went looking for an ObjC port…

Existing port: outdated

There was a port linked on the Artemis site that was OK – but had no documentation or updates, and Artemis has moved on since then.

There were also no unit tests etc – and the current Artemis getting-started wouldn’t work with this port (because so much has changed). So I started a new port…

New port: ObjC Artemis 100%

Primary aim:

  • make it identical to the core Artemis.


Secondary aims:

  • include getting-started, unit-tests, etc, so that even if Artemis changes … this version will still be usable
  • use as many Obj-C features as possible to simplify writing code – but ALWAYS keep it so that code written in Java-Artemis ports directly to ObjC with zero code changes
  • …except: some of Java-Artemis is literally impossible to port because it uses Java features that don’t exist in ObjC

Github project: https://github.com/adamgit/ArtemisObjC

Features I had to write / re-write

Since launching the iPhone … Apple has been trying to bring ObjC up to modern standards. They’re making good progress (e.g. blocks!) – but they have a long way to go.

Naming

ObjC’s big problem for library writing is its lack of namespace support.

I had to rename every class to have the word “Artemis” on the front. Otherwise, names like “Component” clash with existing libraries :(.

Generics

ObjC has no Templates and no Generics, which makes life hard. Lots of boilerplate code required, lots of errors, etc.

Missing classes

Almost every class ported directly (ObjC’s standard libraries are very similar to Java’s).

But there were two classes that needed special effort to implement in ObjC:

  • Bag / ArtemisBag — for ObjC, this needs to be implemented in raw C for performance; I haven’t done that!
  • BitSet / ArtemisBitSet — ObjC has no direct class for Java’s BitSet, but it has a very similar C library around CFBitSet (methods behave slightly differnetly though!)

Progress…

Emulating Generics in ObjC

I’m hoping someone else will try this: tomersh’s clever one-file hack that simulates Generics in ObjC. It creates @protocol’s that extend NSSet/NSArray/etc to behave like Generics (but only for simple operations) — and because @protocol uses the same </> syntax as Generics, it even reads the same!

Reducing boilerplate code typing, even without Generics

Artemis has ComponentMapper’s. In Java, using Generics, these automatically output the correct type for each object. In ObjC, you have to type some horrors like:

[objc]
ComponentOrder* order = (ComponentOrder*) [self.mapOrders get:entity];
if( ((ComponentOwnedResource*)[self.mapOwnedResource get:[entity.world getEntity:order.sourceCountryEID]]).ownerEID != order.playerEID )
{

}
[/objc]

…because of ObjC’s lack of Generics, there’s so much crud there it’s almost impossible to read WTF the programmer was doing. In OOP it would have been:

[objc]
if( order.sourceCountry.ownerEID != order.playerEID )
{

}
[/objc]

This is a disaster! The ES code is literally 3 times as much typing as the OOP code, AND IT HAS NO TYPESAFETY! The worst of all worlds

So, I added subscripting (a relatively recent ObjC feature) to ArtemisComponentMapper. The new code is:

[objc]
ComponentOrder* order = (ComponentOrder*) self.mapOrders[entity];
if( ((ComponentOwnedResource*)self.mapOwnedResource[ order.sourceCountryEID]).ownerEID != order.playerEID )
{

}
[/objc]

Note: “self.mapOwnedResource” is an ArtemisComponentMapper. In traditional Artemis, you have to pass it an Entity instance (ObjC: ArtemisEntity*). But in cases where you had only the “id” (ObjC: Id), you need to first fetch the Entity.

…with subscripting, you can have TWO subscripts: one that uses int’s, one that uses NSObject*’s. Conveniently, that means you can use EITHER the raw id (which is an int! Very lucky!), or the Entity instance … and the library now intelligently does the right thing.

More to come…

If you contribute to the project and add fixes/improvements, let me know and I’ll add info here…

2 replies on “Artemis Entity System in ObjectiveC”

Hmm.. The verbosity and lack of type safety really kills this effort as you note. Have you considered investigating if RoboVM (http://www.robovm.org/) could provide a way to port the Java version of Artemis for cross-compilation? That is a task I need to look into at some point to verify all the Java tricks I employ in my ES / component architecture efforts conceivably work via RoboVM.

There’s a clever hack that gets you the “generic return type” part of Generics in ObjC, which is worth checking out (as mentioned above).

That alone would cut another 20-30% of the code out.

Interestingly, it would *only* work well with Artemis, because of an accident of how Artemis is designed (the use of custom Arrays-of-components to give performance boost over crappy HashMap implementations)

Comments are closed.