How to write code that is relevant for both Cocos2D and Sprite Kit, and as an extension to that the Kobold (2D/Touch/Kit) projects?
Because for the past months I shifted my attention to Sprite Kit, in order to create Kobold Kit and an accompanying Starterkit. While it’s obvious that Sprite Kit has everyone’s attention, I don’t want to turn my back on cocos2d-iphone and KoboldTouch. So from that came the need to create as much code as possible in a portable way.
The result is OpenGW, the world’s first game world simulation engine available to the public (in Nov/Dec). This is the holy grail I’ve been unknowingly searching for the past couple years!
What is OpenGW?
OpenGW stands for Open Game World.
It is a data-driven, engine-agnostic, cross-platform game world simulation engine.
I’ve set up a stub page where you’ll find more info on OpenGW.
The gist of game programming – this may come as a surprise to some – has absolutely nothing to do with putting things on the screen! What you see on the screen is just the result of the game’s current simulation state. All data and processes, no visuals. That is, if you write it “properly”.
It’s natural for a beginner to take the most direct, magic numbers, global variables, singletons, copy & paste, “add more stuff in whatever class/method you’re currently in” type of approach.
As you gain experience, your code becomes better, more structured, more modular. But you’re always tempted to take shortcuts (and you *will* take them) and do things quickly which you’ll come to regret further down the road.
How I learned to love the code
For me it was very hard to grasp “separation of concerns”, and even to understand why it’s always important. Programming for a AAA game title was an eye-opener. But I believe where I learned the most from was doing game programming and simultaneously working on a different, almost entirely self-designed internal app.
On the game side we had a clearly modular game and rendering engine, with one big module being responsible for driving the game simulation. It was completely decoupled from audio, visuals and networking. It gave me a framework to work in, providing guidance on how to do things just by looking at existing code. Almost naturally my code became modular, reusable, flexible, extensible.
On the other hand I was tasked to create a database frontend application. In C# using .NET and several 3rd party frameworks for the user interface with Hibernate connecting to a SQL database, while also designing and administering the database. I had nothing to build on – zero, zilch. C#, .NET, Hibernate were new technologies to me, as was designing and writing a User Interface application that needed to satisfy both end users while avoiding the pitfall of having to custom-code each and every new table or data column.
Suffice it to say, this wasn’t my best code despite good intentions. Some good decisions didn’t work so well with the database or user interface layer. Some bad decisions haunted the project for a long time. What’s worse, the design of hibernate and the database and the design of the user interface were diametrically opposed. There are techniques to handle all of this, but having no blueprint and not keeping the different layers cleanly separated made this a very difficult, taxing project with a less than optimal codebase.
This taught me one thing: if you have a good, solid framework to work in that provides plenty of good examples, writing code is not only going to be easier but it will naturally fit much better into the design of the framework. If the framework is designed well, you’re going to write better code as a consequence!
Your code is only as good as the engine’s code
Yet that means bad engine design leads to bad developer code, or at least greater effort to write good code.
I always disliked cocos2d-iphone’s design because of what it does to and teaches its users. It is an example of not-so-great-but-it-works-ok design from the perspective of the engine. I’m happy Apple cleaned up the mess, I’m disappointed that their design also lends itself towards subclassing rather than encouraging to write code that works on any node (that’s what behaviors do in Kobold Kit btw).
Bad engine designs teach users the wrong priorities: that it’s okay to subclass specific views to put your game code in it. That it’s okay to use singletons in the first place, and in the worst case to “bridge gaps” (connect nodes) between branches of the node hierarchy. That it’s acceptable that multiple nodes will process the same input events. That actions can be used as a substitute for actual game logic. That code is always tightly coupled with the engine.
Worst of all: that there is no separation of visuals, audio and input from game logic code. This mixing becomes a problem real quick, even for the smallest projects.
I took a different approach with KoboldTouch, building a MVC framework where cocos2d is demoted to the view, controllers create the hierarchy, and models (and/or controllers) host the game logic. One issue was that cocos2d kept fighting back, always trying to penetrate the framework’s design. Ultimately the separation isn’t as clean as I had hoped it to be, but it was an improvement.
Out of necessity Kobold Kit has a slightly altered design, where the view (nodes) form the hierarchy and the controller attaches itself to a node, and is optional. The controller still hosts the model but it also hosts behaviors, which are code plugins to alter the behavior of any node (ie turn a sprite into a button that responds to touches and clicks).
Component based programming is very powerful. Interestingly I didn’t know until I searched for a good article on CBD that I saw Ray Wenderlich has an introduction to component based development that will help you understand what OpenGW does. And here’s a very instructive article that shows by example how a monolithic class can be refactored to one that uses components.
Anyhow, both Kobold Kit behaviors and Kobold Touch controllers are tightly connected to the view, and thus ultimately they would only work with a specific rendering engine. Sharing code between the two projects seemed rather pointless.
How OpenGW came about
Not sure what exactly triggered the idea to create OpenGW. It definitely came out of writing the Platformer Starterkit for Kobold Kit. Perhaps the whole idea of running a business with a partner, creating multiple games and realizing what we’re creating isn’t exactly going to be a beginner’s starterkit covering the basics with complete tutorials.
What I was producing was game code and workflows that satisfy a professional developer’s needs, with a necessarily higher learning curve. And that learning curve was even higher before I started rewriting what I had in OpenGW exactly because it was so interwoven with the visuals, and no distinction made between code that only serves the user interface and visual layer, and code that runs the game logic.
One area where this lack of separation became a problem was the TMX object model. It is a complete representation of all the aspects of a TMX file: the map, its layers, tiles, tilesets, gids, objects, properties. In itself it is almost perfectly decoupled from anything engine-specific, minus the tileset textures.
But to run a game on the TMX object model meant looking up gids (tiles), cross-referencing them with what a gid does, checking properties of a tileset or object, and all that stuff. On top of that coordinates need to be converted because TMX has its origin in the upper left corner, not in the lower left. This wasn’t just bad for performance, it made the code clunky, bloated, difficult to grasp.
Somewhere in the process I remembered how we did things in the game simulation. I’ve always wanted to work within such a system again, but it never occurred to me that I could just rewrite it and that it would have great benefits.
How things change
For example regarding the tilemap information spread over tiles, tileset, objects, layers and their properties. What I’m doing now with OpenGW is collecting all the information I need from the tilemap at load time – combining information from gids, objects and properties and creating a single memory buffer that contains bit-coded information for each tile. That becomes the world map in OpenGW.
There’s actually a class named OGWMap that manages a memory buffer as a “map” and enables you to do cool things like enumerating the tiles in a certain rectangle. Certainly Sprite Kit was an inspiration there.
While I’m working on and with OpenGW I can actually see my code improving. The Platformer starterkit code that used to be tied with Sprite Kit has shrunk down to under 200 lines of code, with thousands of lines of code now in OpenGW and at least half of it so generic (ie kinematics, collision detection) that it can be used for any type of game. It’s great knowing that this code is now preserved for all eternity – Sprite Kit or Cocos2D may come or go, this code will now remain relevant and useful past those engines and for multiple projects.
It’s things like these that make me believe that I’m on the right path. That OpenGW works with both KoboldTouch, cocos2d-iphone or any Objective-C (and later C++) game engine for that matter will give us the opportunity to provide the same Starterkit for all of these engines.
When and how?
The goal is to make OpenGW available this year, November or December. It will be a paid product.
The idea is to put everyone in the same boat – one purchase to get access to OpenGW which as a bonus grants you access to KoboldTouch and Kobold Kit Pro. KoboldTouch customers will get OpenGW at no extra cost through their existing subscription. Once OpenGW is released new subscription plans and pricing will come into effect, though I can’t give you details simply because no thought went into that yet.
|Follow @gaminghorror||Follow @kobold2d|