vibbit! a fun 2d graphics and game framework!
I teased this a little bit in my rust thread, and then later on twitter after news around the Unity licensing changed, so now feels like a good time to write a bit more about what this is and what the goals are for it.
Vibbit is a graphics and game framework designed to be hyper lightweight, and act as a solid springboard for larger engines/frameworks. It should feel pretty familiar to anyone with experience with frameworks like love2d, but the api is largely inspired by the minimalism of raylib.
features
graphics!
it draws shapes, lines, and sprites! load custom .ttf fonts, including pixel art ones. it’s quite optimized for 2d sprite games. The above gif shows it rendering two scenes to their own textures, then combining them both for a split screen game. The whole thing is ~120 lines of code, and should be even shorter by the time it actually releases.
In a popular stress test called a bunnymark, it can render about 960 thousand individual textures with updating positions and rotations each frame. I have no clue when you’d need that many but it’s very neat. For comparison, the same test can do about ~120k with raylib and ~200k with love2d. I’d love to talk about some of the tricks and research that went into that another update.
no concepts!
Okay - Well of course there are some concepts. But the primary goal behind the design of vibbit’s API is that you shouldn’t have to learn any sort of structure, or game pattern to start working with it. A lot of code-only frameworks try to bring along all these ideas like a “game world” with a tree of “game objects”, or things like entity-component-systems. Those things sound great at a glance, but if you have a vision of how your game should work, they really only get in the way and force you to circumvent them anyways! vibbit comes out of the box with none of those things, so you can bring one in or write one yourself if you like.
In essence vibbit is just a handful of functions, (i’d like to aim for 1.0 to have a nice clean number like 128, but then what do i do about updates X__X…) each doing exactly what it says. Want to load a texture? load_texture()
. want to draw it? draw_texture()
. how do you end the frame and display it all? end_frame()
. I’d like for all the “How do I do THIS” questions to feel really straightforward just by looking at all the functions available, no need to dig through 20 classes and sub classes before finding what you want.
highly "port"able
The codebase is written in Rust, which I had decided on after spending a while studying and trying out other frameworks made in rust and getting familiar with the language. However it’s been designed in a way that should make things very easy to get it set up with other languages too. Having a super lightweight and performant base is important for slower, interpreted languages like wren or lua (non-JIT). The idea is to let the graphics framework stay out of the way as much as it can so your game logic has plenty of headroom. I got the idea to work on this after spending a lot of time contributing to GitHub - RobLoach/node-raylib: Node.js bindings for Raylib - I really liked the simplicity of the drawing API, and being able to work in Javascript was a lot faster to me than working in C/C++. After a while I got a lot of ideas on how to improve the performance of the bindings, but it involved enough work that it just felt right to put together a new library from scratch with porting in mind. There are two major things that give this an edge for portability:
- active assets are stored on the side of the framework in a generational arena for easy allocation without working with pointers or a hashtable. Basically, from your scripting language like lua, when you are referencing a texture to draw, you only need a 64 bit “key” that can be stored inside a value like an int, so you don’t have to pass whole large objects between language to language just to do a draw.
- seperate render thread. a lot of other frameworks, when you request to draw something, immediately begin doing work to send data over to the GPU and perhaps draw or batch it into something. In a scripting language that means on top of the cost of calling the external function, you also have to wait for a bunch of gpu work. vibbit is designed to make use of it’s own thread for all rendering, so when you make calls all it does is prepare a list of commands that get consumed at the end of the frame. so your game logic gets a ton of extra cpu time each frame, and you don’t have to worry about implementing something like that yourself. It does all this in the background while still working exactly how you expect.
non-features
these are things that don’t really fit into the scope, or really are just better as their own library:
- physics / collision lib. I might add some funcs that do shape overlaps, but physics is something that is usually pretty game dependent, or tied heavily into your engine’s object tree. I DO think that rust needs an arcadey collision lib, (no integration step, just overlap checks in pixel increments like celeste/towerfall) instead of all the box2d kind of stuff. If i worked on that it would be way later and it’s own package though.
- networking, file reading. Chances are you would just be better off using the standard library for this. though some languages maybe don’t have them, and maybe bindings to those would get custom ones tailored to that lang instead of a general approach.
- 3d. It should be possible to do some 3d rendering by drawing triangles, and I may include a mesh system so you could in theory roll your own 3d if need be. but 3d scenes require so much setup to actually perform well with instancing and such, compared to the style of 2d “just add some triangles to the batch” approach. By the point you do all that you’d be better off using a larger engine…
goals / todos
These are some major things that I want to get before putting up any public builds or previews:
- audio. I think I could quickly set up basic sound + music playing, but I’d like to get some feedback from developers on what they actually need for this, particularly music/rhythm devs. let me know if you have thoughts on this element (any frustrations with current things you use? etc)
- better shader setup. before multithreading you could load and set uniforms on custom shaders, but this needs to be reimplemented
- joypad input. I need to think a bit about how to handle this in a clean way everyone would like. Smooth unplug/replug support is a must for me personally, and I think handling multiplayer is important too.
- deltatime isn’t calculated correctly right now, last i checked
- error output and debug logging. There is SOME rust-specific stuff that needs to be done with it, errors are kind of a special part of rust. I also need a good debug output so users know what’s happening behind the scenes when they make calls from a scripting language
- decide on a main scripting language to support. I’d like to have at least one supported out of the box on launch (other than rust) to demonstrate a) how simple it should be to create a binding and b) how well they work. lua is the obvious choice but so many other things have it. I quite like statically typed ones like wren, and umka is pretty interesting too.
- a million other small things. input stuff like mouse support + being able to record what’s been typed in as a string.
- website + docs. before going fully public and open i want this library to actually feel USABLE instead of just some hobby project. A lot of my hesitation with rust’s ecosystem is that the documentation in other game crates is VERY tech-oriented, and doesn’t match the questions that a game developer just checking it out want answered. So on top of documenting the actual API it really needs a site with written content on basic tasks everyone typically needs, “how to XYZ” pages, and maybe a resources page with other libraries and info on filling in things not implemented in vibbit itself. This may take about as much time as the library itself honestly, but I think it’s vastly important for any amount of longevity for a project like this.
These are some larger concepts that are kind of on the backburner:
- auto texture atlasing. it would be neat to load in all your game’s textures and then say “hey, batch these all into one texture atlas for me”, but then still be able to use all your original texture objects. It would just check the atlas for that individual texture, then convert the draw command to instead use the subcoordinates of the atlas. This would be a big performance boost for texture rendering if you have a lot of small sprites in their own files, and in theory it should just work seamlessly behind the scenes.
- default “baked in” font in case you don’t want to bundle and load your own at runtime
- more modular systems. I do have the groundwork in place for a bit, but I think for longevity sake it should be easy to add new modules to replace the core external libraries for the project. The main offenders for this are your windowing library and the graphics API. Right now it’s on SDL2 and OpenGL for those, but opening up for users to just pick what they like before compiling and not have to worry about it will be nice. That kind of thing is also really important way down the road if this ever needs extra platform/console support
- Some kind of fully-fledged example game. In addition to little individual code snippet examples I think it needs an actual playable game that isn’t just pong or flappy bird or whatever. The idea would be something small enough to have code you can read through in one sitting, but big enough to use third party things like loading tilemaps + collision. I feel like some kind of older flash-style platformer would be fine scope. nothing too “juicy”
That’s what I got so far! Totally open to suggestions or questions - in fact I’d love to hear if you’ve used barebones frameworks like love2d, monogame, haxeflixel, etc so I can maybe iron out some pain-points people have with those ahead of time. I’ll try to get more images and gifs as I add more flashy stuff