Skip to content

Project Design & Architecture

Sesu8642 edited this page Feb 21, 2023 · 2 revisions

Project Structure

This project uses the default project structure for libGDX as generated by the libGDX Project Setup Tool. There is a sub-project for each supported platform. The actual game code resides in the core project.

Architecture

The following are the most important components that make up the game.

Gamelogic Component

This component does everything that modifies the internal game state, for example moving a unit. It also contains the AI. The component can be seen as the domain logic or business layer.

UI and Renderer Components

The UI component renders controls (e.g. buttons, tables) that make up the user interface. It contains screens that make up different parts of the game (e.g. the splash screen, ingame screen). Each of these screens can use multiple stages. A stage contains a set of UI controls for a given purpose. Example: the ingame screen displays the parameter input stage when generating the map. After starting the game, the heads up display (hud) stage is used to display the ingame buttons.

The renderer component is used for rendering the current gameState: water, the island and it's contents.

The UI and renderer components can be seen as the presentation layer of the game.

Preferences Component

This component is used for persistence. This includes the settings as well as savegames. It is the data access layer of the game.

Input Component

This component is responsible for detecting input events that are not linked to the UI controls like clicks on the map, keyboard input or touch gestures.

Design Patterns

Event Bus

Most communication between the components is done using an event bus. This way, a loose coupling between the components is achieved. An exception to this is the gamelogic component using the preferences component directly. The reason for this is that data is requested and returned as a response. This is tedious to do using events.

The UI component uses the event bus for internal communication as well. Imho this is more elegant than attaching event listeners to every single screen.

Dependency Injection

Dependency injection is used wherever appropriate. This results in a desirable level of coupling and good testability. A DI/IoC framework is used to avoid manual creation of the object graph.

Tests

There are only a handful of automated tests in this project. One reason for this is that I did not know how to structure a game in the beginning which led to a lot of refactoring which would have been even more painful with many tests. The existing tests focus on the things that are hard to test manually. This is mostly the gamelogic component. Having some more tests would be good, of course.

Frameworks and Libraries

  • libGDX is the core framework used for this game
    • used for UI, 2D rendering, persistence
    • cross-platform
    • pure Java
    • disadvantage: maintenance only mode
  • Dagger2 is the DI framework
    • uses generated code instead of reflection
    • in theory GWT compatible
  • Guava
    • whenever I want to write a generic helper class, Guava already has it most of the times
    • provides the event bus component
  • JUnit and Mockito for unit tests
  • JUL is used via SLF4J for logging
    • the libGDX logging is too basic
    • Logback doesn't work on Android
    • JUL works on Desktop and Android but I don't like the API (hence SLF4J)
    • JUL has a MemoryHandler that can be used for data collection in crash situations

Java Version

The project uses Java 8 because it is the latest version libGDX supports for all platforms.