Ignifuga Game Engine
Ignifuga is a multi platform (Windows/Linux/OS X/iOS/Android) 2D hardware accelerated engine based on Python and Cython, inspired by similar offerings like Cocos2D, Cocos2D for iPhone, and AndEngine. All your game logic code along with the engine’s and supporting tools is converted to C during the build process, and compiled into one big standalone binary for each of the supported platforms (please refer to the FAQ for more information). The project is currently in heavy development as the engine for The Gaucho, and it should be fairly usable already, but we don’t give any guarantees, so the expected performance may range from not even working to attaining consciousness and starting the third world war for all we know. At the very least, we hope that it lives up to its name and it doesn’t catch fire.
Why Ignifuga? Who is he/her/it? What’s in a name?
Ignifuga means fireproof in spanish. That doesn’t explain much, and it has little to do with game engines, I know. But it’s a catchy name, I already had the art from a dead-and-now-revived project, and the Linux project has a penguin in their logo, so don’t come here judging me!
Who’s this guy that’s staring at me?
Don’t mind him, it’s just The Gaucho, who hangs around the site looking after his chicken.
Can the chicken talk?
Well, of course not, but she can tweet!
She's also very active, and much more eloquent, in our Google Group / Mailing List
Get The Code
Source
Use the bootstrap script to get started
Issue Tracker
Please report bugs and issues here.
Wiki
An ever growing collection of tips, tricks and tutorials that should tide you over until we put proper documentation in place.
Getting Started
Let's get started by making it clear that there's no "hello world" to be found here as Ignifuga doesn't believe in social norms or programming conventions or saying hello. What you'll find below is a walkthrough over the steps required to build the engine and a quick explanation of what makes the demo tick, which hopefully will be enough to get you experimenting.
The easiest way to get going on Ubuntu (Precise/12.04 or Quantal/12.10) or OS X (Lion/10.7.x or Mountain Lion/10.8.x) is to use the bootstrap script. One note before you go rushing ahead and then get dissapointed. If you are developing on OS X, you have to install XCode from the App Store, start it, accept the license, go to XCode->Preferences->Downloads and install the Command Line Tools (hey! OS X is super easy, or so they say!). Then you can proceed.
Download the bootstrap.py script with your browser, or via the command line:
# On Linux wget https://bitbucket.org/gabomdq/ignifuga/raw/tip/tools/bootstrap.py # On OS X curl https://bitbucket.org/gabomdq/ignifuga/raw/tip/tools/bootstrap.py > bootstrap.pyThen run it with:
python bootstrap.py
By default, on Ubuntu the bootstrap utility will download a bunch of development packages and require your sudo password. On OS X, Mac Ports will be used if available, and if it is not available it will be auto installed as an unprivileged user (so no need for you to provide the sudo password). The Android SDK and NDK will also be installed. If you want to disable any of this default behaviour or change some of the install paths, run
python bootstrap.py --helpto view the available options.
Once the process completes, all dependencies should be set up (if not, please make sure to fill a bug report), and you can build the engine itself...doing so will create a statically compiled interpreter that you can use for playing around... Inside ignifuga/dist/[platform]/bin/ you'll find a Python binary that's quite like the standard Python runtime, but it also contains the whole Ignifuga machine (SDL, libRocket, one or more chickens) inside it. First, get a list of available target platforms:
schafer -AThen run something like:
schafer -P linux64Or
schafer -P osx
And that's it! Fame, fortune and a flock of attractive individuals of the opposite or same sex (or both) should quickly follow.
Ok, I know you are wondering what you can do in the meantime while you patiently wait for fame, fortune, etc. As a baseline reference of a few things our engine can do, check out our demo project. It's not the flashiest of demos, but it will help you get an idea of how things work. You can download it via Mercurial, or better yet (given the size of the repository's history) directly as a zip or bz2 tarball
cd ~ wget https://bitbucket.org/gabomdq/ignifuga-demo/get/tip.tar.bz2 tar jxvf tip.tar.bz2 # Note: The folder name varies with each commit! cd gabomdq-ignifuga-demo-4cc3532ad91e ./demo.py
demo.py is very simple:
#!./ignifuga-python from ignifuga.Gilbert import Gilbert, BACKENDS from ignifuga.Log import Log def run(): Log(0) Gilbert().init(BACKENDS.sdl, 'intro', 'demo') if __name__ == '__main__': run()
The first line states that this Python file should run using the ignifuga-python binary (i.e. the engine!) in the same directory, which is no other than a renamed copy of the Python runtime Schafer generates if you run schafer -P linux64. This binary is compiled for Linux 64 bits (Ubuntu 12.04 more specifically), but you can replace that file for any of the other variants Schafer produces (For example, for Windows you could generate the binary with schafer -P mingw32 on Ubuntu, then use it on a Windows machine to develop without having to rebuild the engine
Next up we import three objects from Ignifuga, Gilbert (the main Singleton, Controller, Overlord, whatever you want to call it), BACKENDS which actually only contains "sdl" as the only backend (but hey, extra points for me for planning ahead!), and Log which is the logging facility (though between us you can use print if you are in a hurry).
Finally, we initialize the logging facility asking for a log level of zero (the higher the level, the less messages you'll see logged), and initiate the game by asking Gilbert to initialize using the SDL backend, loading the "intro" scene from the demo.json file (which needs to go in data/scenes/demo.json)
That's it, the rest is mostly data driven from demo.json!For the lazy among you, the demo repository contains a bunch of pre compiled static apps for Linux 32/64, OS X, Win 32, Android and iOS. In the special case of iOS you won’t be able to install the demo_ios.ipa package unless you are using a jailbroken device, but if you are a member of the Apple iOS Developer Program you can regenerate the IPA and sign it with your own provisioning certificate following the instructions in the README.
These static apps are all built using Schafer as well, and they are examples of what you would distribute if you were to ship a product with Ignifuga.The demo repository holds demos in binary form
As an example of how simple is to get a project ready for deployment, the contents of build_android look like this:
#!/bin/bash schafer -P android -m demo.py -p org.ignifuga.demoandroid -a data -a images -a fonts -a sound --android-keystore="/home/gabo/android.keystore/keystore" --android-keyalias="mdqinc.com key 1"
This asks Schafer to build a deployment package for Android (optionally building the Android version of Ignifuga if it's not been built already), and to include in it the contents of the data, images, fonts and sound directories. It also requests that the package be signed for direct uploading into the Google Play Store.
The deployment procedure will also gather any other Python source files present in the current directory and sub directories, and treat them as sources for your game. The -m parameter merely tells Schafer which is the "main" file, the entry point. All these Python files will be Cythonized and linked into the final product, so no source is distributed
Let's now turn our attention to the demo.json file, where most of the data driven magic actually happens
{
"intro":{
"resolution":{
"width":1920,
"height":1200
},
"keepAspect":true,
"autoCenter": true,
"size":{
"width":1920,
"height":1200
},
"entities":{
"title":{
"components":[
{
"type":"Sprite",
"file":"images/title.png",
"z":1,
"alpha": 0.0,
"interactive":false
},
{
"type": "Action",
"duration": 0.5,
"alpha": 1.0,
"runNext": {
"duration": 2.0,
"runNext": {
"duration":1.0,
"alpha":0.0,
"onStop": "Gilbert.changeScene('menu')"
}
}
}
]
}
}
},
"menu":{
"resolution":{
"width":2048,
"height":1536
},
"size":{
"width":2048,
"height":1536
},
"keepAspect": true,
"autoScale": true,
"autoCenter": true,
"userCanScroll": false,
"userCanZoom": false,
"components":[
{
"type":"Rocket",
"id": "gui",
"file":"data/rocket/menu.rml",
"interactive":false,
"fonts": ["fonts/Asap-Regular.otf"]
},
{
"type":"Music",
"id": "menumusic",
"file":"sound/menu.ogg",
"loop": 2,
"onStop": "print 'music stopped'",
"onStart": "print 'music started'",
"onLoop": "print 'music looping'",
"active": true,
"fadeIn": 5000,
"fadeOut": 3000,
"stopOnDeactivation": false
},
{
"type":"Sound",
"id": "chickensound",
"file":"sound/chicken.ogg",
"active": false
},
{
"type":"Sound",
"id": "cowsound",
"file":"sound/cow.ogg",
"active": false
},
{
"type":"Action",
"targets": ["bkg", "dp", "igni", "sun"],
"duration":1.0,
"loop":0,
"relative": true,
"alpha":1.0,
"runNext": {
"duration": 2.0,
"runNext": {
"targets": ["bkg"],
"duration":0.3,
"loop":0,
"relative": true,
"alpha":0.3,
"runNext": {
"targets": ["bkg"],
"duration":0.3,
"loop":0,
"relative": true,
"alpha":-0.1
}
}
}
}
],
"entities":{
"spine":{
"components":[
{
"type":"Spine",
"id": "spinec",
"atlasFile":"data/spine/spineboy.atlas",
"skeletonFile":"data/spine/spineboy-skeleton.json",
"animationFile":"data/spine/spineboy-walk.json",
"interactive":false,
"float": true,
"x": 200,
"y": 400,
"z": 200
}
]
},
"bkg":{
"components":[
{
"type":"Sprite",
"id": "bkg-sprite",
"file":"images/menu/UI1_gradient.png",
"z": 0,
"x": 0,
"y": 0,
"interactive":false,
"alpha": -0.3
}
]
},
"bkg-menu":{
"components":[
{
"type":"Sprite",
"id": "bkg-sprite",
"file":"images/menu/UI1_Piso.png",
"z": 1,
"x": 0,
"y": 1153,
"interactive":false
}
]
},
"dp":{
"components":[
{
"id": "dpidle1",
"type":"Sprite",
"file":"images/dp/dp_idle.png",
"z": 2,
"x": 894,
"y": 722,
"width": 260,
"height": 485,
"interactive":false,
"red": 0.07,
"green": 0.07,
"blue": 0.07,
"alpha": 0,
"loopMax": 1,
"onStop": "self.paused=True;idleaction1.active=True;",
"remainActiveOnStop": true
},
{
"type":"Action",
"id": "idleaction1",
"duration":5.0,
"active": false,
"onStop": "dpidle1.loop=0;dpidle1.paused=False;"
}
]
},
"igni":{
"components":[
{
"id": "idle",
"type":"Sprite",
"file":"images/ignifuga/igni_idle.png",
"z": 10,
"x": 1800,
"y": 1045,
"interactive":false,
"width": 128,
"height": 128,
"red": 0.07,
"green": 0.07,
"blue": 0.07,
"alpha": 0,
"loopMax": 3,
"onStop": "self.paused=True;idleAction.active=True;",
"remainActiveOnStop": true
},
{
"type":"Action",
"id": "idleAction",
"duration":2.0,
"active": false,
"onStop": "idle.active=False;jump.reset();jump.paused=False;jump.active=True"
},
{
"id": "jump",
"type":"Sprite",
"file":"images/ignifuga/igni_jump.png",
"z": 10,
"interactive":false,
"active": false,
"width": 128,
"height": 128,
"red": 0.07,
"green": 0.07,
"blue": 0.07,
"alpha": 1.0,
"loopMax": 1,
"onStart": "jumpAction.active=True",
"onStop": "self.paused=True;",
"remainActiveOnStop": true
},
{
"type":"Action",
"id": "jumpAction",
"active": false,
"y": -15,
"relative": true,
"duration": 0.4,
"runNext": {
"y": 15,
"relative": true,
"duration": 0.4,
"onStop": "jump.active=False;walk.reset();walk.paused=False;walk.active=True"
}
},
{
"id": "walk",
"type":"Sprite",
"file":"images/ignifuga/igni_walk.png",
"z": 10,
"fliph": true,
"x": 1800,
"y": 1045,
"interactive":false,
"active": false,
"width": 128,
"height": 128,
"red": 0.07,
"green": 0.07,
"blue": 0.07,
"alpha": 1.0,
"onStart": "walkAction.active=True"
},
{
"id":"walkAction",
"type":"Action",
"active": false,
"fliph": true,
"runNext": {
"relative": true,
"duration":10.0,
"x": -500,
"runNext":{
"fliph": false,
"runNext":{
"duration":2.0,
"x": 100,
"relative": true,
"runNext": {
"fliph": true,
"onStop": "walk.active=False;idle.reset();idle.paused=False;idle.active=True;igni.x=2048 if igni.x<-128 else None;"
}
}
}
}
}
]
},
"sun":{
"components":[
{
"type":"Sprite",
"file":"images/menu/UI1_Sol.png",
"z": 1,
"x": 887,
"y": 1537,
"interactive":false,
"alpha": 0
},
{
"type":"Action",
"duration":4.0,
"loop":0,
"y": 220,
"easing": "outelastic"
}
]
},
"title":{
"components":[
{
"type":"Sprite",
"file":"images/menu/UI1_Title.png",
"z": 1,
"x": 2048,
"y": 530,
"interactive":false
},
{
"type":"Action",
"duration":1.5,
"loop":0,
"x": 689,
"easing": "outback"
}
]
},
"subtitle":{
"components":[
{
"type":"Sprite",
"file":"images/menu/UI1_SubTitle.png",
"z": 1,
"x": 2048,
"y": 700,
"interactive":false
},
{
"type":"Action",
"duration":2.0,
"loop":0,
"x": 697,
"easing": "outback"
}
]
}
}
}
}
This file is a simplified version of the initial title and menu from The Gaucho Game. It defines two scenes, "intro" and "menu".
"intro" is simple enough, it contains a single entity which has attached a Sprite component (the title image), and two chained Action components that fade in and then fade out the entity. When the fading out is over, the Gilbert singleton is called upon to change the current scene and show the menu
The menu scene is a bit more complex, as it uses many of the engine's features. You'll see it has a Rocket component (which wraps libRocket, the HTML+CSS rendering engine), sound, sprites, tweening, and components attached directly to the scene which can affect more than one entity at the same time.
You'll also see bits of Python code, and a nice bit of development sugar as you can reference components or entities by their id (the methods of the walkAction Action can be referenced by walkAction.something).
Finally, let's have a quick look at menu.rml, the Python scriptable HTML look-alike that libRocket understands