Introduction to Raylib for engine developers

Trivia

Raylib is not a game engine in a typical sense, but it describes itself as

a programming library to enjoy videogames programming. 1

It is used not only for game development, but also to create new engines on top of it or any graphical/visualization applications. Raylib’s main language is C, but there are more than 70 2 bindings to other languages such as C#, Lisp, Go, Python and whatnot. Target platforms include all major desktops, Android, Web and ARM-based devices like Raspberry Pi. This framework is relatively simple and easy to learn: 558 functions and 34 data types; only ~10% of its functions deal with data pointers; more than 140 code examples. There is no standard API documentation so the main way to learn it is by reading a cheatsheet that briefly describes provided functionality, as well as to read through the examples. Although some might argue that such approach is not beginner-friendly, I would parry by saying that learning the very fundamental principles of game development requires you to be comfortable reading the code and the documentation.

Architecture

Raylib

But how is the architecture structured here and how similar is it to RTEA (Runtime Engine Architecture) 3? Raylib is broken down into 7 self-contained main modules, visual representation of which is depicted above, and each module is organized according to its primary functionality. The modules are as follows:

  • rcore - window creation and management, setting up and populating graphic context, shader management, files management (file system operations, compress and decompress files), input handling, camera management and other smaller utility functions;
  • rlgl - graphic API wrapper and pseudo-OpenGL 1.1 translation layer, that contains all OpenGL API calls. This module works as an abstraction layer for multiple versions of OpenGL: 1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0;
  • rtextures - texture/image loading, generation and manipulation and drawing. 15 different file formats are supported;
  • rtext - font data loading and text drawing;
  • rshapes - basic 2D shapes drawing functions (pixel, line, circle, polygon, spline) as well as shape’s collision detection;
  • rmodels - loading and drawing of 3D models or basic 3D shapes, their materials, animations and collision detection;
  • raudio - audio device management and sound/music loading and playing.

Apart from 7 main modules raylib also provides extra modules that expand engine capabilities. They are designed to be as decoupled as possible from other modules and therefore can be used as standalone libraries in other projects. The extra modules are:

  • raymath - math functions to work with Vector2, Vector3, Matrix and Quaternions. Compiled with raylib by default;
  • rcamera - basic camera system with support for multiple camera modes (free, 1st person, 3rd person, custom). Compiled with raylib by default;
  • rgestures - gesture detection and processing (Tap, Swipe, Drag, Pinch) based on touch or mouse input events. Compiled with raylib by default;
  • raygui - a minimalistic immediate-mode-gui library that can be used for all sorts of tools development.
  • rres - a file format designed to package game assets (images, fonts, text, audio, models etc.) into a self-contained comprehensive format that is efficient to read and write to.

Raylib vs RTEA

The most subtle difference between the architecture of raylib and RTEA is that raylib’s rcore module unites COR (Core Systems) and PLA (Platform Independent Layer) by doing conditional defines based on a relatively small number of target platforms, as well as including HID (Human Interface Device) module. What is LLR (Low-level renderer) in RTEA, in raylib is being split into 5 parts: a graphics device interface rlgl and 4 specialized modules that communicate with it - rtextures, rtext, rshapes and rmodels, with last one also implementing the functionality of SKA (Skeletal animation). RES (Resources) module from RTEA instead of being a single place responsible for resource management, in raylib is split between aforementioned 4 specialized modules, as well as PHY (Collision and Physics). Such approach greatly facilitates modularity of an engine, but when a game grows larger, this separation might become harmful because there are increasingly more places to keep track of where memory allocations and de-allocations are happening. RTEA’s FES (Front End) and AUD (Audio) are raygui and raudio respectively.

Last point to note about architecture is that raylib relies on a number of external libraries to load specific file formats for images, fonts, audio and 3D models. SDK module implies that any external library should pass through PLA to provide a unified interface for all other engine modules, however in raylib only OpenGL follows this convention, while every other external library is used inside modules directly, bypassing PLA. Despite the fact that all the dependencies and reasoning for using them are specified in the documentation, in my opinion it is still missing some sort of guideline on how to get and use them - a point raised by Joanna May in “Enjoyable Game Architecture” when she mentions that one of the responsibilities of the game architecture is structure. 4

Game World Model

Raylib does not enforce any particular game world model and there are no real game objects, only intermediary types that are being passed to functions, and functions either draw something on screen or perform user-specified logic - a perfect example of a Procedural model. This approach is what allows this engine to be highly modular. In fact not only modules from extra can be used as standalone libraries, but also rlgl and raudio. This modularity, as well as an excellent documentation are the main reasons why raylib is such a magnificent learning tool - developers can pick out and swap any part of an engine without worrying that it will affect other components. However, the biggest drawback of this approach is that thinking about a game in terms of procedures is not very productive when it starts to scale up, and to combat this, one would need to implement an actual game world model, either from scratch or by importing an external solution.

Target platforms, graphics & community

Portability of raylib does not come close to Unity’s level - no support for iOS, nor for modern consoles (we mentioned why open source engines cannot support consoles in section 2.1.3). However, in theory certain modules of raylib can be reused, in which case they would be viewed as third party tools in another engine, rather than being an engine on its own. Nonetheless, it supports both Desktop and Android platforms which covers the biggest chunk of the game market. The versatility and graphics are probably the weakest aspects which is directly correlated with raylib’s philosophy of being simple and easy to learn. By limiting the amount of possible features, as well as using an outdated but simpler graphics API, it lowers the skill floor for beginner game developers. Community, on the other hand, is the strongest suite - more than 70 bindings to other languages, unofficial ports to other platforms such as iOS and countless open source hobby projects that use raylib at their core and showcase its full power.

Conclusion

Overall, the purpose of Raylib seems to be to explain the basic components of video games and how they are interacting, as well as to teach C programming by making games. Ramon Santamaria, Raylib’s author assures that:

…many hours have been put into the API design; thinking about the best name for every function, the minimum number of parameters required, the right name of each parameter, the data types structure and more. 5

This is undoubtably showing when reading the documentation - every design choice is explained in exhaustive details, from syntax analysis to expounding which particular functionality is being imported from external libraries. It is also worth pointing out that all external libraries are under permissive licenses which allows raylib to be used on commercial projects without any legal obligations.