- Full game title: Tibor: Tale of a Kind Vampire
- Developer: Cateia Games
- Year of release: 2009
- Type: Commercial
- Target Platforms: Windows, Linux, Mac, iPhone, Nintendo DS
For questions / suggestions about this page use this forum thread.
Table of contents
[Show/Hide]About
Tibor: Tale of a Kind Vampire is our first platform game, inspired by good ol' games like Prehistorik and Super Mario.
The game is based on a Croatian fairy tale written by author Damir ÄŒivrak.
The game targets the casual gamers audience.
Software and tools
- Programming Language: -Python 2.5
- Main library: Python-Ogre (v1.6)
- Other libraries: OIS, -CEGUI, OpenAL
- Ogre plugins: OctreeSceneManager
Ogre3D
Here are the reasons we chose Ogre3D for this game as well as the pros and cons related to this genre:
Ogre Pros
- Both OpenGL and DirectX support
- Productivity
- ParticleSystem support
- Octree support
- Various optimisation support
Cons
- No game console support
- Some exporter trouble
The Video game industry evolves rapidly and developing/maintaining your own in-house engine just isn't profitable.
Ogre is a modern, well documented and extensible 3D engine, and most importantly - open source!
Since the engine itself is free, we are morally obliged to give something back to the community. So we occasionally write patches for Ogre or develop/maintain code. Currently we're working on improving the Theora Video Plugin and writing wiki articles like these ;)
CEGUI
Cegui is a mature gui library with a relatively good set of tools and well documented API.
Our only objection would be that it is very big and difficult to set up.
Python
We chose to use the -Python programming language for the following reasons:
- Python-Ogre works very well, stable, fast, supports the latest ogre and all the plugins and libraries commonly used with Ogre.
- Productivity - Having many years of experience as a C++ developer I have to say that it takes us much much less time and lines of code to do the same thing in -Python then in C++. And since time is money, this choice makes the project cheaper. Less lines of code means statistically less bugs and a codebase that is easier to maintain.
- Ease of debugging - -Python supports the 'eval & exec' functions which gives the programmer the ability to execute code given in a string. hold here for a minute, think about what that means... Yes, it means you can have a console in the game and call your functions as well as Ogre's, create and manipulate objects etc. Basically you can write and test code on the fly.
- No long compiling - Nuf' said.
The downsides of using -Python over C++:
- python is slower, but since most of the cpu intensive work is done within Ogre, there is virtually no performance impact.
- Filesize: Another downside of using -Python and PythonOgre is that there is more data in the final product. Each wrapper (C interface) library takes space. python25.dll and other python dependencies give weight to the final version of the game which is somewhat important for a casual game whose primary distribution channel is the internet. However, the size impact is not big enough to be a problem (the entire game packaged takes about 80 MB)
- Memory usage: -Python being a high-level scripting language takes more RAM for similar datatypes then C++, plus the wrappers themselves take a few megabytes. We estimate that python and python-ogre take up to 50-60 Megabytes more RAM then the C++ equivalent.
Conclusion: Productivity and Cost are important aspects of any commercial project. The downsides listed above are all negligible within the scope of our project making -Python and Python-Ogre a logical choice.
Audio
We initially chose to use OgreAL, but it caused us a lot of unexplained problems: slowdowns and crashes so we decided to make our own simple audio library.
We made the library in C++, using OpenAL for playback and Ogg/Vorbis for ogg file loading.
The library had to be used in python so we had to wrap it somehow. We used the same tools Python-Ogre uses to achieve that goal: Py++. Wrapping C++ code with Py++ is quite easy (once you figure out all that Boost.Python stuff).
Source Control
Subversion of course ;)
Tiles
Tibor uses five different tilesets throughout the game, all of which have an identical layout: 4x4 tiles as shown in the sample tileset on the left.
Four tiles in the upper left corner are ground tiles, and they are randomized on each level load, to avoid having repetitive patterns.
Tile Planes
The game has several planes, the 'main' plane which the character walks on, usually a background plane and one or two planes coplanar with the main plane, such as cavern tiles.
Level creation
Once the program starts, it creates 16 entities (quad meshes), one for each tile. and it does so by first creating Manual Objects and then converting them to meshes from which the entities are created.
These entities are then used to generate Static Geometry for each Tile Plane. Static Geometry is a cool little object that makes sure non-movable geometry (like the tile planes) are rendered as efficiently as possible. But I won't get into details how they operate, it is explained quite well on Ogre's API reference.
Collision detection
I've defined a line for each tile Tibor can walk on or collide with and then generate one line for each such tile in the level and use that for collision detection.
A line is defined similar to a Plane equation, consisting of three elements:
- center point - the center of a line
- normal vector - the direction it faces
- radius - distance from the center to the end of the line (imagine a circle of radius r originating at the center point)
This way I can easily detect on which side of the line a point is on.
So, collision detection is done in a way that a CollidableObject ( a base class used in a couple of classes, including Tibor of course) saves it's current node position in a temporary variable, moves the node a bit in the direction it has to move (desired direction + gravity) and compares these two points on all the surrounding lines.
If a line is found on which both points are not on the same side of the line - collision has occurred!
I've thought long and hard how to represent characters and objects in collision detection, bounding sphere, box, etc, but a colleague has accidentally given me a perfect solution - a point! Each character is represented only with one point (only for tile collision ofcourse)! perfect for a platformer.
So, when colliding against a line normal, a character is stopped, but when colliding along the normal, the character is allowed to move. This way, the character can jump on platforms from below, which is a common thing in platform games.
Objects
While designing the object system I tried to use as much benefits from Object-Oriented programming as possible, which turned out to be very efficient.
Every possible object class is derived from a single class, containing some variables and functions which every object should implement (or inherit the default behavior from this class) which the Object Manager class can operate with.
The most commonly used object is a StaticObject, a simple textured, unlit quad which can be scaled, tiled, inverted and rotated. That simple set of features enabled us to create very diverse and un-repetitive levels.
Background class is a simple quad mesh which uses Identity Projection (coordinates are in screen space) and its material is scrolled by the camera position (scaled by a given x and y factor) and optionally a scaled time function (for eg. clouds)
Coin class is derived from StaticObject, making use of StaticObject's tiling capabilities. It's geometry is dynamically updated when playing, recreating the entire object every time a coin is picked up.
EnemyHorz is a class that handles an animated mesh, an enemy that walks left to right and can be jumped on.
Other classes are derived from these and provide some minor altered functionality. The ones listed above are the most important
Editor
Every class has different behavior in editor mode and in game mode, allowing easy placement and manipulation in the editor and optimized usage in the game.
Editor
The editor is tightly integrated into the codebase. Most object classes have separate code for editor behavior and separate for the game. For example, a StaticObject class is represented as a ManualObject in the editor, where as it is compiled into StaticGeometry in the game.
The editor's user interface is made using -CEGUI (WindowsLook scheme). Here you can easily lay tiles and objects, manipulate object properties, rotate, scale, move them etc.
Once ready, the level is saved into several XML files.
Everything is designed for efficient level editing. most actions have keyboard shortcuts and navigation, selection, movement etc is available in key-hold+click.
An interesting thing that I've made for the editor is PDF-style navigation: click-and drag. it has proven to be very efficient and intuitive. If you need to move the camera, you can zoom in or out (with the mouse wheel) and drag the level in the desired direction.
CPU Optimizations
Collision Lines
as explained in the Tiles paragraph, each tile has a collision line that is used in collision detection. But since there are a lot of tiles, collision detection becomes expensive. Therefore, some optimizations have to be made!
The simplest solution is to split the lines in a quad-tree, which, obviously, greatly improved collision performance, but wasn't enough.
So I had to write a program that would connect all collinear lines into single bigger lines. You can see on the screenshot in the Tiles paragraph that there are only a few longer lines. There were ten times more before this optimization.
Objects
Updating all objects each frame takes time, so we decided only to update those objects within a certain distance of the main character. Secondly, we figured that not all objects need to be updated each frame, so update some objects once in every 2,3 or 4 frames (depending on object type).
Additionally, those objects that are not updated every frame are evenly divided across the frames, so one object is updated on eg. odd frames and another on even frames.
GPU Optimizations
Static Geometry
Object orientation is a nice thing, but when it comes to performance, some compromises have to be made. In the editor, each object is constructed as a manual object (so it can easily be edited), while in the game, StaticObjects(Still images) and similar objects are compiled into StaticGeometry, while others are represented as entities.
Particle System Score
Each time Tibor picks up a coin, a score texture rises up and fades out. This was first done by creating another StaticObject with the appropriate texture (which meant adding another SceneNode and Entity to the Scene Graph). Since there are a lot of such instances, we had to think of something.
And the winner was ParticleSystem!
A particle is manually added into the system whenever a coin is picked up. This way, all the score images are bundled together into one Draw call and thus rendered faster.
UI as ManualObject
-CEGUI is great, but our in-game ui took 15 batches! Which took a toll on our FPS. To solve the problem, we construct a 2D ManualObject and update it whenever some ui information changes (like score changed).
Grouping all Coin objects
I made a Coin class that is capable of grouping M x N coins into one batch, as rectangular groups.
However, since there was a need to scatter coins widely throughout the level, this 'optimization' did not help much.
So, I introduced a larger grouping method that splits all coins in a level into a simple quad tree, grouping coins into larger objects.
Coin textures were grouped into two larger textures so they could be better batched.
This optimization reduced the batch count significantly.
iPhone port
We decided to port the game to the iPhone which means rewriting the whole game in C++ using OpenGL.
We decided to write some ogre classes we use in the PC version of the game. These classes only support basic functionality and are there to maximize the portability of the codebase.
Packaging
Windows
We used py2exe to compile our python code into a Windows executable.
I made a script that calls py2exe, copies all the data files into the distribution folder, removes all debug files, and does a few tweaks.
Thus, compiling a stand-alone build is as simple as running a python script.
The final product is packaged with the installer software NSIS and bundled with Visual Studio 2008 redistributable as a side-by-side assembly.
Linux
N/A yet
Mac OS X
N/A yet
iPhone app store
N/A yet
<HR>
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
1. Definitions
- "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
- "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
- "Licensor" means the individual or entity that offers the Work under the terms of this License.
- "Original Author" means the individual or entity who created the Work.
- "Work" means the copyrightable work of authorship offered under the terms of this License.
- "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
- "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
2. Fair Use Rights
Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
3. License Grant
Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
- to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
- to create and reproduce Derivative Works;
- to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
- to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
- For the avoidance of doubt, where the work is a musical composition:
- Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
- Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
- Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
4. Restrictions
The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
- You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
- You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
- If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
5. Representations, Warranties and Disclaimer
UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
6. Limitation on Liability.
EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. Termination
- This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
- Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
8. Miscellaneous
- Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
- Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
- If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
- No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
- This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.