Developing a 2D Game for Windows, Mac, & Linux with Cocos2d-x
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.
Nearly two years ago I posted a summary of the tools, frameworks, and languages we use to create our 2D mobile games. At the time we were just getting started on our upcoming title, which also happens to be our first PC game (Windows / Mac / Linux). Over the past couple of years we’ve had to make a few changes to our development environment in order to accommodate our new goal of cross-platform PC development. In this post, I’d like to cover these editions we’ve made while also highlighting some obstacles we’ve had to deal with. I will not be covering why we chose these tools and frameworks because I find that things change all the time and a decision made 6 months ago may no longer be valid today. Hopefully this will be useful to those who are considering or using any of the frameworks discussed below.
For the most part our programming environment has not significantly changed since 2014. The frameworks we use include Cocos2d-x, Spine, Box2D, and SQLite. The languages: C++, C, Java, Objective-C, and various scripting languages. Our development tools include Xcode, Eclipse, and Visual Studio, and for version control we still use SVN and Git. The most significant changes came in the form of modifications to our existing frameworks and compiling environments. Some, but not all, of the improvements described below have been integrated into the latest version of Cocos2d-x. Developers who need the source code for these changes can find them in our branch of Cocos2d-x version 3.4. Specific changes can be found by looking at Chad’s or Sheado’s commit history.
After a few months of working with Cocos2d-x on PC, I started to realize that many necessary desktop-specific features were missing at the time. So I branched Cocos2d-x v3.4 and started making the necessary changes. These included the following:
Although controller support existed on mobile, desktop support was not yet implemented. After digging around the code a bit I realized that Cocos2d-x uses GLFW, which is a framework that handles window management and controller input amongst other things. To keep things simple, I went ahead and implemented controller support in GLFW. It worked great, but as I discovered later on, it lacked Steam controller support and at the time lacked a few controller features that would have been nice to have. So I started over - this time by adding SDL 2.0.4 into the mix. SDL controller support is working great so far and includes all the features we could possibly need for a 2D action game. The main downside of using SDL is that now I have to worry about using and shipping two media frameworks (SDL & GLFW). Ideally it would have been nice to remove GLFW altogether by re-implementing window management with SDL - but time did not allow for that. Although it didn’t work out for my development needs, I’d like to emphasize that GLFW is an excellent framework, and I highly recommend it as a strong alternative to SDL.
Also missing from Cocos2d-x was fullscreen toggling and window resizing. Fortunately, GLFW 3.2 supports these features so it was a relatively straightforward addition to make.
Cocos2d-x full screen windows capability does not prevent computer screens from sleeping or screensavers from enabling. Although SDL’s video subsystem includes a screensaver suppression function, I was not able to use it due to a conflict between GLFW and SDL’s video subsystem. Instead, I had to tediously implement the feature for each operating system. The task was tedious due to a lack documentation or conflicting information on solving this seemingly simple issue.
Stable Cross-Platform Audio
Version 3.4 of Cocos2d-x had a fragmented audio rendering solution for each operating system, with each lacking several features, such as volume control or OGG support. More recent versions of Cocos2d-x have since moved to a more stable and cross-platform solution, but at the time that solution was still in early development. Rather than wait for it to be completed, we decide to implement our own solution. Since SDL was already integrated into our branch (for controller support) I decided to use its stable extension SDL_mixer, which allowed us to quickly add cross-platform OGG support.
Some of the improvements I made included a bit depth fix and some culling modifications. I changed the default bit depth from 16 to 32 bit. I also added some additional culling functionality, since Cocos2d-x v3.4 drew all nodes regardless of whether they were within the 3D camera’s view or not.
For 2D physics, we are still using Box2D, and we love it. We use RUBE as our level and physics editor. It’s a bit clunky at times, particularly for laying down parallaxing layers, but it gets the job done and saved us a ton of time from having to develop our own editor. The biggest features we added in regards to physics for our current project have included collision detection of animated characters, ragdoll animation support, and atlas caching.
Our collision detection solution starts with our artist creating bounds in the Spine editor. On the code side of things, Box2D polygon shaped bodies are created for each of these bounds at load time. Then Box2D joints are created where these bounds meet - approximating their animated Spine bones’ pivot positions. During gameplay, the AI moves around the central physics body of each animated character. The joints keep the other associated bodies in tow. Each frame, the physics update first then each body’s corresponding Spine animation’s position is updated. The result is the physics determines the general location of the Spine animation, but the individual limbs of a character are positioned based on the state of the current animation. For a dead enemy though, the Spine animations are switched off and the Box2D joints start being used to update the Spine bone rotations, which in turn update the position and rotation of their associated sprites. This results in a ragdolling effect as the bodies fall and cause their joints to rotate. Here’s a forum post discussing the subject in more detail. Other, more feature complete solutions have been implemented since - I recommend scouring the forum for details on the latest ragdolling implementations.
Another important feature we needed was atlas caching. By default, as of March 2016, Spine’s Cocos2d-x loader did not automatically cache atlases. The same animation would have its atlas reloaded for every instance. I implemented caching as discussed in this forum post.
As described in my previous post our development environment consists of Xcode, Eclipse, and Visual Studio. For our current project, I’ve also added CMake to that list. I use it to create our Linux builds. We also occasionally use gdb for debugging and valgrind for profiling.
Previously we used iMacs and OS X to develop our mobile games. In moving to PC development, we’ve found that dual-booting or dedicating a machine to each operating system to be the least frustrating option. Working in virtual machines tended to be significantly slower, causing compile times to take an eternity. Dual-booting works great, but on a busy day multiple reboots can be pretty annoying.
We are still using R.U.B.E. for physics and level editing, and we still like it - for the most part. The main issue in making a larger game has been the editing of parallax layers. Having an external editor causes us to work slower since we have to go back and forth between editor and game often to check our work. Ideally, if time permits, we would develop an in-game editor for our future projects.
Developing for the desktop can be quite intimidating due to the various operating systems and infinite hardware combinations. Likewise, there are endless combinations of frameworks and tools developers can use to create games for these platforms. In this article, with the hopes of it being useful for other developers, we shared the tools and frameworks we use and the obstacles we encountered as we modified our Cocos2d-x development environment for cross-platform desktop game development.
Founded in 2010 by two guys and a gal, our goal is to make the world a happier place, one game at a time. We are currently completing work on our next game, a twin-stick shmup with a feathery twist for Windows/Mac/Linux called A Quiver of Crows.