libGDX - Architecture

Architecture plays a key role in software systems around the world, it defines the way a system is structured. The architecture of libGDX is an interesting topic to study. In order to get to know more about the system behind this framework we discuss the important architectural aspects of the libGDX framework in this blog post.

First of all, we discuss the general architectural style. Next, is a description of the different architectural views. Furthermore, one can read about how libGDX functions as an API. Finally, we present a short review of the libGDX architecture.

Architectural style

To reason about the architectural style of libGDX, it is important to first think about how this framework is used. As libGDX is only a development framework, it is imported by the developer that would like to create an application with the help of it. This means that libGDX does not offer a graphical user interface to the developer, but rather it is used as an abstract tool to develop applications. This means that for example a model-view-controller design pattern1 would not be applicable to the framework.

The major design methodology of the libGDX project is domain-driven design. With the main focus of libGDX being to serve as a framework for developing video games, different components within the system are named after this. For example, there are distinct components associated with the sound or graphics of the game.

A second point that becomes clear when inspecting the codebase of the libGDX framework, is that files are categorized. The project defines multiple packages, each containing files that are created for one specific part of the framework. There are also packages that contain sub-packages. This packaging makes it very clear what each part of the codebase is for and makes the project look well-structured.

Views

libGDX is packaged and deployed as a regular library. This fits well with its developer-centric approach, as developers are familiar with utilizing software in this manner. There are separate libraries for each supported platform. This way, a project does not have to include implementations for platforms it does not support itself2.

Since the Java ecosystem heavily features “jars”, archives of compiled code and resources, the final product will likely be delivered as one as well3. It is possible to create a “fat jar”, a jar of the product that has been merged with the jars of its dependencies, such that shipping the product with all of its dependencies can be easily done using a single file. The Java runtime knows how to execute jars and therefore this one fat jar is sufficient to ship and run the final product.

In broad strokes, the framework is very loose, as it embraces the “only pay for what you use” concept and only delivers the bare necessities in the core project. Even backends, as mentioned earlier, are pick-and-choose. However, for most components there still is some form of interop. The backends are an obvious case where there is a standard interface, which we’ll investigate first.

The main class of every libGDX project is an implementor of an ApplicationListener4. For ease-of-use there also is an ApplicationAdapter that is simply an abstract class that provides defaults for every method of ApplicationListener5. Instances of this interface are then passed to the chosen backend’s Application instance, which handles building the right environment for GDX applications and translates the platform’s events into something the ApplicationListener can process.

ApplicationListeners are one-way: signals from the backend travel to the application and there is no direct return path except by raising exceptions, at which point the application will halt. Instead, the application communicates with the backend via a set of global interfaces that are populated by the backend during startup6:

public class Gdx {
	public static Application app;
	public static Graphics graphics;
	public static Audio audio;
	public static Input input;
	public static Files files;
	public static Net net;

	public static GL20 gl;
	public static GL20 gl20;
	public static GL30 gl30;
}

If the application is running using the JWJGL3 backend and uses the graphics, audio and input services, the situation is as follows:

Individual components of a backend are largely independent. Some components, such as audio, can even be disabled, and allow the developer to remove even more dependencies that are not always necessary.

Aside from backends, libGDX also provides utilities for dealing with typical game development concepts such as maps and scenes. These components will interact with the backend on behalf of the application to deal with common use cases. However, different from how other frameworks are designed, in libGDX it is not expected for the end user to use these tools. libGDX is designed around the user having full freedom and implement such features in their own way.

Because libGDX is integrated in the application, the main communication channel is via simple function calls across objects. This is important especially in code that handles graphics and audio, because most audiovisual operations require high speed and low latency messaging. In the case of graphics, operations are also limited to the main “graphics” thread and calls from other threads are likely to be rejected or cause errors. While the main thread could set up a message queue or a similar cross-thread messaging system, the overhead of this can easily dominate the time spent rendering.

As stated before, there is typically no communication between the different components of a backend. While it is not expressly forbidden, it is unlikely that a backend will require significant communication channels between its components since their domains are very dissimilar. Components outside of the backend will use the same interfaces as the application can.

The application developer’s perspective

When creating an application using the libGDX platform, it is possible to create the application with the libGDX Project Generator7. This generator will create the file structure of a basic project that the application developer can build upon.

It is possible to (de)select certain sub projects to fit your project’s needs. Extensions that are maintained by libGDX can be selected as well as some third party extensions. By pressing “generate”, a project will be created that can launch on all selected platforms.

The structure of the project will be as follows with each sub project as its own separate project:

The majority of the codebase should be inside the core folder; only platform-specific code should be placed inside the subproject folder for the given platform.

The MyGdxGame class is the core of the entire project. It extends the aforementioned ApplicationListener and houses basic application functionality. This class drives the render loop and listens for actions that listen to changes to the application state, such as resizing the window and pausing and resuming the game. There is also a dispose entrypoint that is activated when the application should exit, in order for it to clean up any resources that are not automatically managed by the Java platform, such as network connections.

When running the application, the render loop of the application will be called and should be used to activate other features that rely on time. Environments such as the display of the stage (Scene2d8) and the 2D physics environment (Box2d9) should have their update event called inside the render loop.

During runtime an extensive number of additional listeners can be added to listen to specific inputs10. These listeners/adapters can be implemented/overriden to apply essential code based on the calls made by the listeners/adapters. The adapters are essentially an implemented version of the listener. This allows the user to only override certain methods instead of requiring all the methods to be implemented.

Developers are free to create their own interaction between the components of libGDX. As explained before, libGDX’s own components require little to no interaction between them. Your application can, however, allow for this interaction by implementing it yourself. There is a lot of freedom for the developers to design their run-time behavior; this is one of the strengths of libGDX.

libGDX as an API

In our previous post we made extensive mention that libGDX is by itself a broad API11. Almost all of libGDX is a developer’s interface with various components that it offers to aid the developer in shaping their application. Developers are not required to interface with the whole ‘API’ when creating their application. This is good, since libGDX is very broad in the features it provides. As discussed previously, not every developer will need every feature that libGDX provides. As an example, a developer might not need audio in their application and can choose not to interface with this system. They can even outright exclude it from their final (packaged) application.

Review

The architecture of libGDX is very much like that of a library. It is one broad system of components packaged as one API. It allows developers to ‘pick and choose’ what they need and is thus very flexible. This architecture is chosen to not limit developers when creating their application, while attaining a feature-rich library that can be interfaced with.

References


  1. Model View Controller. (n.d.). Wiki C2. Retrieved March 15, 2021, from https://wiki.c2.com/?ModelViewController ↩︎

  2. libGDX. (n. d.) Starter classes and configuration. Retrieved March 15, 2021, from https://github.com/libgdx/libgdx/wiki/Starter-classes-and-configuration ↩︎

  3. oracle. (n. d.) JAR File Overview. Retrieved March 15, 2021, from https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jarGuide.html ↩︎

  4. libGDX. (n. d.) The life cycle. Retrieved March 15, 2021, from https://github.com/libgdx/libgdx/wiki/The-life-cycle ↩︎

  5. J. Zukowski. (2007) Listeners vs Adapters. Retrieved March 15, 2021, from https://blogs.oracle.com/corejavatechtips/listeners-vs-adapters ↩︎

  6. libGDX. (n. d.) Modules overview. Retrieved March 15, 2021, from https://github.com/libgdx/libgdx/wiki/Modules-overview ↩︎

  7. libGDX. (n. d.) Project Generation. Retrieved March 15, 2021, from https://libgdx.com/dev/project-generation/ ↩︎

  8. libGDX. (n. d.) Scene2d. Retrieved March 15, 2021, from https://github.com/libgdx/libgdx/wiki/Scene2d ↩︎

  9. libGDX. (n. d.) Box2d. Retrieved March 15, 2021, from https://github.com/libgdx/libgdx/wiki/Box2d ↩︎

  10. libGDX. (n. d.) Input. Retrieved March 15, 2021, from https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/Input.java ↩︎

  11. B. Dorland, M. P. H. Lips, L. Everse, C. Geukes. (2021) libGDX - Product Vision. Retrieved March 12, 2021, from https://2021.desosa.nl/projects/libgdx/posts/product-vision/ ↩︎