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%
- make it identical to the core Artemis.
- 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.
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 :(.
ObjC has no Templates and no Generics, which makes life hard. Lots of boilerplate code required, lots of errors, etc.
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!)
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:
ComponentOrder* order = (ComponentOrder*) [self.mapOrders get:entity];
if( ((ComponentOwnedResource*)[self.mapOwnedResource get:[entity.world getEntity:order.sourceCountryEID]]).ownerEID != order.playerEID )
…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:
if( order.sourceCountry.ownerEID != order.playerEID )
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:
ComponentOrder* order = (ComponentOrder*) self.mapOrders[entity];
if( ((ComponentOwnedResource*)self.mapOwnedResource[ order.sourceCountryEID]).ownerEID != order.playerEID )
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…