GUIDE TITLE: Advanced Code Architecture for Core, Part 1: Model-View-Controller
ESTIMATED COMPLETION TIME: 10 minutes
CORE VERSION: Any
SUGGESTED PREREQUISITES: Intermediate-level coding
TUTORIAL SUMMARY:
Part 1 of a series of articles on designing a better code architecture that solves the problems with the way code is typically structured in Core.
EXPECT TO LEARN:
- What MVC architecture is
- Why MVC architecture is beneficial to code in Core
- How MVC architecture can be implemented in Core
END PRODUCT VISUALS:
- (Imagine beautiful code here.)
TUTORIAL:
Once a coder has advanced beyond the novice stage of struggling with the questions, "How do I make my code work?", and, "How do I make my code do what I want?", their main concern instead becomes, "How do I make my code better?".
Code architecture (software architecture) is how code is organized and structured; it's how the code of an app is separated into parts, and how those parts are designed to work together. As a coder's ambition reaches towards bigger, more-complex apps which require larger amounts of code, having a well-designed architecture becomes increasingly important. Bad architecture will make code harder to write, harder to expand upon, and more prone to becoming a confusing mess of fragile "spaghetti code", harboring elusive bugs.
The Core platform makes creating games easier in a lot of ways. But the conventional way of structuring code in Core—by attaching scripts to game objects to define their behavior—although easy to understand, also promotes disorganized, difficult-to-maintain code that can even hinder the work of artists. Avoiding this outcome requires a radical re-thinking of architecture. Hence, the first step to a better architecture for Core games is to establish how code should be organized—that is, what code should go where.
Model-View-Controller
Model-View-Controller (MVC) is an architectural pattern originally conceived for desktop apps, but later also became one of the most popular architectural patterns for web apps. Under MVC architecture, the Model (like "mental model", not "3D model") comprises the app's domain logic and data; the View is a representation of the Model's data, portrayed in the app's user interface (UI); and the Controller reacts to user input, translating input into commands for the Model and View. The concepts of Model, View, and Controller are better-understood as structural layers for segmenting an app's code, rather than as actual objects themselves.
Model
In Core games, the Model layer is where almost all game logic and game state should belong. It should include everything needed to simulate the game's "world" and the possible interactions within it.
Scripts belonging to the Model layer should only exist as script assets in Project Content, and be loaded as modules by other scripts, using require()
(with a custom property of type "Asset Reference"). These scripts should have either an "API" suffix, or no suffix (for consistency with plain Lua libraries), to indicate that they shouldn't be added to the Hierarchy.
Because loaded modules are shared within the same environment (client-side, or server-side), this approach lets the Controller layer call command functions in the Model layer, to update the game state in response to user input; and lets the View layer call query functions in the Model layer, to retrieve the current game state and update the UI.
Notably, this approach also lets both the client-side and server-side use the same code, for governing game logic and game state. This makes it possible for the client-side and server-side to each maintain independent, but identical, simulations of the game state for validating actions, providing responsive feedback, and minimizing network transfer.
View
For both games and non-game apps, the View layer is easily understood as where all UI code belongs. However, the key insight of MVC architecture for games is in thinking of game objects in a scene as the equivalent of UI objects in a window or web page.
This concept has large ramifications for how code is organized in Core games. Game objects should be considered part of the View layer, as they are representations of the Model layer's game state. Just as domain logic shouldn't be attached to UI objects, game logic shouldn't be attached to game objects either.
Scripts attached to game objects should only be responsible for visual appearance, broadcasting events, or registering game objects with other code. These scripts should be placed in a Client Context or Server Context group, and have a "Client" or "Server" suffix (or both) to indicate their intended network context.
Moving game logic out of game objects makes code more robust and maintainable, as it prevents game logic from becoming entangled with the Hierarchy structure of game objects. Artists can work without worrying about breaking code, and changes can be made to game logic without risk of also requiring changes to every affected game object and template.
Controller
The Controller layer is where all code for handling user input belongs. In Core games, this encompasses events in general—whether arising from user input, interactions within the game world, or network broadcasts.
Scripts belonging to the Controller layer are responsible for translating events into commands for the Model layer and View layer. These scripts should only contain client-specific or server-specific code; and should either call functions in the Model layer to perform actual game logic, or broadcast their own events for other scripts to consume.
To avoid the complications of Static/Local Context and Default Context for events, and for code in modules (which inherit the network context of the caller), scripts belonging to the Controller layer should be placed in a Client Context or Server Context group, and have a "Client" or "Server" suffix (or both) to indicate their intended network context.
Too Long; Didn't Read
- The conventional way of structuring code in Core—by attaching scripts to game objects to define their behavior—leads to "spaghetti code"; Model-View-Controller (MVC) architecture helps organize code by establishing what code should go where.
- The Model layer is where almost all game logic and game state should belong; scripts should stay in Project Content, to be loaded as modules by other scripts, using
require()
. - The View layer is for UI, visual appearance of game objects, broadcasting events, and registering game objects with other code; scripts should be in Client Context or Server Context.
- Game objects in a scene are the equivalent of UI objects in a window or web page; they should be considered part of the View layer, and shouldn't have any game logic attached to them.
- The Controller layer is for handling events, and should either call functions in the Model layer to perform game logic, or broadcast events for other scripts; scripts should be in Client Context or Server Context.
What's Next
Part 2 will examine Entity-Component-System (ECS) architecture, and how it fits together with MVC architecture in Core games.