KivEnt is joining the Kivy Organization this week and I wanted to take some time to introduce the library. The first public version of KivEnt was introduced a little over a year ago, and the coolest thing built with it by far was the Boardz snowboarding game built by chozabu. A huge thanks to everyone who tried, tested, and contributed to KivEnt. A year later, there have been 344 commits from 6 contributors representing 17,663 LOC added and 4,267 LOC deleted. The codebase has nearly quadrupled in size, with a new modular architecture so that you can limit the dependencies of your app to just the functionality you need. I have used the data collected over the last year to completely rewrite how KivEnt stores and processes its game objects. You can now get fairly close to the metal, basically avoiding all python objects in your GameSystem update loops. This means that games built with KivEnt should be fairly fast, at least for Python games. It won’t compete with a real C++ engine, but I think we are within the territory of smaller frameworks such as many JS game engines, Haxe, or something like XNA. The bottom line is KivEnt will make it easier to implement complex, real-time simulations of many kinds to your Kivy applications.
What is KivEnt?
KivEnt is an addon to Kivy, introducing an efficient, real-time pipeline that makes it easy to build highly dynamic scenes and add them to your Kivy applications.
The framework maintains a dual Python/Cython API. The Python api making it easy to interact with your game objects from your python scripts, either for prototyping or in logic where speed is not as big of a concern. The Cython api allows you to speed up your game logic by statically allocate your game objects in efficient C structs, while still exposing all data for Python level manipulation. KivEnt is designed to be built upon, whether in Cython or Python; you will find all objects and functions exposed in headers for external use by your own cython modules.
KivEnt makes use of an entity-component architecture for managing your game objects. If you are not familiar with this design pattern you may want to read the 5 part blog series here. You can skip part 4 as it is more specific to mmo-style games, but definitely read part 5.
Why would you use it?
With everything from Unreal to Unity going free what does KivEnt offer? While it’s certainly not as powerful as the industry leaders, KivEnt makes it possible to create games with thousands of active objects. This should easily cover many types of hobbyist, student, independent, and mobile use-cases. KivEnt does not wrap C++ code, it is pure Python and Cython all the way down, making it easy for you to modify or extend. The whole framework is designed with the idea of having a simple optimization route to go from rapidly created, prototype python code to highly optimized cython code that doesn’t touch any python during the game system’s update loop. With just Python code, you can expect to manage 1000-5000 game objects at once, however this number increases to tens of thousands of game objects if you cythonize your game logic. If you want to read more about this process, read this tutorial. If you think your game will have under ~100,000 active game objects at any one time and you want to use Python, KivEnt should be perfectly appropriate.
Finally, the library is MIT licensed, and all of its dependencies are either MIT or a similar license. Maybe not a huge deal when everything else is free, but I think it is important to have truly open-source options; so that you can say, do, create, and monetize how you want.
Why is it necessary?
Kivy has a nice graphics API with canvas, however the graphics pipeline is designed for sparsely updated graphics based on user-interaction. This mean that when no one is touching anything and no animations are happening, nothing is drawn. It saves your device battery and makes a lot of sense for UI interactions such as touching a button or scrolling a list. However this is also the fundamental source of the problems trying to do very dynamic real-time scenes in Kivy: Many small draw calls. This enables Kivy’s widget tree approach to rendering, but also sort of works against the nature of gpu. When we want to draw many sprites, we want to ask once to draw many things, not ask many times to draw on thing each time. It largely emulates the openGL 1.1 (pre-shader) api, which is fairly rigid and defined making it easy to grok, but precludes making use of the full power of openGL ES 2 shaders and the batching of draw calls used for rendering in most games. The data flow is one-way, there is much copying that occur when we update an objects buffer. It is sanitized several times from python list to cython list to c array. We always free the old memory and allocate new any time an update is made. This is not really optimal for a real-time rendering loop. When you are drawing 100,000 sprites represented by 4 floats (an x, y coordinate and a u, v texture coordinate) a python object is a very heavy representation for this data. Making it both efficient to process and easy to modify the data from python alone is fairly difficult.
KivEnt draws many objects, called entities, to a single widgets canvas (or the canvases of several widgets depending on your games representation). Entities with the same rendering requirements are automatically combined into a single buffer, or as many buffers as needed to fit that number of entities, minimizing draw calls.
Automatically batches your entity models that share the same texture and shader into batches with a size of your choosing. You have fine tuned control over how your data is submitted to the gpu. A simple way to design shaders with new vertex formats, create those vertex formats, and write a renderer update loop that processes your game objects and writes out the data for that frame. An optimized cython and a slower python api for accessing all of your game data so that you can modify the parts of your game objects that you need to without incurring as much memory copying. Most of your game data will be stored in arrays of C structs, ensuring memory optimization, data contiguity, and keeping performance more consistent across devices, especially the more memory poor mobile devices.
There are still many features to implement, and performance optimizations to chase down, but KivEnt should be fully ready for producing a wide variety of real-time applications that are currently difficult and performance intensive when done with widgets or canvas instructions.