Game Architecture - alexitsios/Calamity GitHub Wiki
This page is a work in progress.
Introduction
What is this?
- An ongoing reference and guide for developers when creating content using our systems in Calamity.
- A technical document providing comprehensive information on our systems.
Basic technology for everyone.
- Unity - https://unity.com/download - We're using Version 2022.1.13f1
- Github Desktop - https://desktop.github.com
- Discord - https://discord.com
Everyone should know.
- #HowWeMakeCalamityGreat - Our Design Principles
- #HowWeStayOrganized - Our organization and folder structure.
- #HowToUseGithubIssues - How we use Github Issues for tasks.
- #HowToUseGithubDesktop - How to use Github Desktop to submit content for Calamity.
- View the "Welcome-and-links" page on Discord to familiarize yourself with the team and know who to contact for collaboration.
Design Principles
- Our top goal is build a great game!
- We want our code and assets to be easy to maintain and flexible to changes.
- We're building a framework that can support all 3 episodes of Calamity's storyline.
- Our focus is on developing technology that is easy for designers to work with and promotes teamwork across different areas of the game's development.
- Our technology's modularity allows for creative exploration in design and provides numerous opportunities to innovate.
Examples Of How We Develop Modular Systems
- We maintain #high standards for our code and systems design, #assets, and file organization that ensure consistency and ease of communication throughout the project.
- We follow key design patterns like MVC and Composition over Inheritance when building new systems,
- We use assembly definition files to improve compile times and improve project organization.
- We organize our code and files by functionality and with namespaces, to help group dependencies.
- We greatly increase our Unity Hierarchy organization through abstracting Scenes by functionality and loading them together asynchronously at runtime.
- We abstract scene objects into reusable prefabs, that also prevent overlapping work and prevent scene merge issues.
- Our tools and architecture make use of Unity's component injection system with ScriptableObjects.
- Credit the ideas: https://youtu.be/raQ3iHhE_Kk , https://youtu.be/6vmRwLYWNRo
Project Management
- We manage our project and tasks using Github's Project and Github Issues Kanban system.
- We rely on Google Docs and other third-party tools for areas of design.
- Our engineers collaborate to break down large goals from the GDD into smaller ones.
- Smaller goals are documented in Github Issues and assigned on a volunteer basis through a Kanban format. Engineers choose tasks that interest them or suit their skills.
- Code and assets are submitted through our version control model. #HowToUseVersionControl
Tasks with Github Issues
What is this?
- Github Issues is our system for managing tasks for the project.
- We operate in a volunteer Kanban format. Meaning tasks are discussed as a group, then people assign themselves to tasks where they're best suited or would like to collaborate.
Get Started with Tasks
- Create a GitHub account: If you don't already have one, create a GitHub account. This will allow you to join the team's repository and view and manage issues.
- Join the team's repository: Accept the invitation sent to your email or check with a Producer. Once you have access, you can browse the issues in the repository and start working on tasks.
- Our Kanban workflow: We break down large goals as a team into smaller goals. Smaller goals are added to Github Issues as tasks. We assign ourselves to tasks on a volunteer basis. As you make progress backup your files regularly to the server branches, and update the task board by moving your task through the different stages of the board as you progress.
- Browse existing issues: Browse the existing issues in the repository to get an idea of what needs to be done. Pay attention to the labels and the status of the issues to understand what stage they are in.
- Assign yourself a task: Once you find a task that you want to work on, assign yourself to the task by commenting on the issue or using the assign feature in GitHub. This will let the team know that you are working on the task.
- If there are others assigned to the task, check in with them to see what needs to be completed and how you can contribute.
- Work on the task: Once you have assigned yourself a task, start working on it. You can use GitHub and Discord to collaborate with the team, ask questions, and share your progress.
- Move the task through the board: As you make progress on the task, move it through the Kanban board by changing the status of the issue. The team will be able to see your progress and know when the task is ready for review.
- Submit a pull request: Once you have completed the task, submit a pull request to the team's repository. This will allow the team to review your changes and merge them into the main branch.
- For details on our approach to version control check #HowToUseGithubDesktop.
- Rinse and repeat: Continue browsing the issues, assigning yourself tasks, checking in with the team and backing up your progress regularly, and working on tasks until the project is complete. Remember to communicate with the team if you have any questions or concerns. Kanban is all about collaboration, so don't hesitate to discuss with the team throughout your progress.
- Additionally everyone mentions their progress to the team verbally once a week in chat. And each department team varies on their specific approaches to tasks and regular communication. Some teams have regular voice calls and the days and timespans may vary.
Folders and Asset Structure
- Conform new assets to our existing folder structures whenever possible.
- Scripts should be kept with their relevant assets, not in a single “Scripts” folder that will grow large and unmanageable.
- This also helps keep like-systems contained and organized.
- No loose files. Everything should be organized into their proper project folders.
- Don't store all ScriptableObjects together in one folder.
- ScriptableObject is a serializable class type, not a category of assets.
- Reference the #MenuOrganization section for technical details.
Version Control with Github Desktop
Introduction
What is this?
- A way to stay organized and prevent overlapping work and conflicting files.
- The system we use is a version control model called a "Gitflow branching model".
Download Github Desktop: https://desktop.github.com
Github Desktop
is the tool we use to sync our files regularly.- Details of how to use Github Desktop will be added soon.
Submitting Files to the Project
- Most developers on the project will use
Github Desktop
to backup their progress regularly to a localfeature
branch. - When enough progress is ready to submit to the project, create a
pull request
from thefeature
branch to thedevelop
branch. - Pull requests are reviewed and merged to the project regularly, so re-sync local files from the
develop
branch frequently.
Technicals
Gitflow branching model
- Our files are split into
branches
on the server to prevent overlap. - There's two main branches:
main
anddevelop
.- tldr; most developers only work on the develop branch.
- The
main
branch is the production-ready builds with version numbers. - The
develop
branch is the main branch for ongoing development. - There's a third testing branch in the middle called the
release
branch, betweendevelop
andmain
. - There's also a merging branch called the
feature integration branch
betweenfeatures
anddevelop
.
Creating a feature branch
- To submit content to the Unity project you'll need to make a
feature
branch that's local to your computer.- You can use this to backup your progress regularly.
- When there's enough to submit to the project, use Github Desktop to create a pull request from your feature branch to the
develop
branch.
Synchronize Progress: Backup and Updates
- Pushing content to your
feature branch
is a good way to backup your progress regularly. This ensures that your work is saved in a remote repository and can be accessed from anywhere. - It's important to use descriptive commit messages with a summary log of changes you've implemented so that other developers can understand the changes you've made and why you've made them.
- Switch to the
develop
branch andfetch origin
after backing up your progress to thefeature
branch. This will update your local branch with the most recent develop branch changes, ensuring your feature branch is up-to-date with the latest changes in the main codebase. - To continue progress on your local feature, switch the branch back to the
feature
branch you'd like to work on. Now you can continue making progress with the most updated code from the develop branch.
Creating a pull request
- Details of how to create a pull request will be added soon.
- Use descriptive commit messages with a summary log of changes you've implemented.
- In Github Desktop, use the checkboxes next to files to select the files related to the changes you've made.
- Unselect files that are not related to changes of the feature you're submitting.
- If there are changed files you didn't intend you can
Discard changes
after pushing changes to thefeature
branch for backup, then create a pull request fromfeature
todevelop
branch.
The Release Branch
- Details of the release branch will be added soon.
Gitflow pipeline involves the following steps:
- The
develop
branch is made from themaster
branch. - From the
develop
branch, create afeature
branch titled as the 'feature name' for each new feature or bug fixes you'd like to add. - Once a feature is complete, create a pull request to merge the
feature
branch back into thedevelop
branch. - Automated testing: (Use of automated testing hasn't been finalized for Calamity) Automated tests are run on the programming changes to ensure that they meet the defined quality standards.
- Code review: Before a pull request is approved, after automated testing, it will be reviewed by other team members to ensure that it meets Calamity's standards and follows best practices.
- When the
develop
branch has enough features to warrant a release or build, create arelease
branch from thedevelop
branch. - Test the
release
branch, and merge any necessary bug fixes back into therelease
branch. - When the
release
branch is ready, merge it back into both themaster
anddevelop
branches. - Tag the
master
branch with the new release version.
This workflow helps keep the master branch stable and production-ready at all times, while still allowing for ongoing development in the develop branch. It also allows for easy collaboration on new features and bug fixes, as each feature or fix is developed in its own branch before being merged back into the main branches.
QA Pipeline
- Bug reporting is handled through Github Issues on a separate Github Project to maintain organization for QA.
- General flow of QA is:
- Individual bugs are reported to Github Issues Kanban backlog separated by department; Art, Design, and Programming.
- Team from appropriate department takes on tasks to fix bugs and moves task status to
In Progress
. - Completed bug fixes are moved to
Pending Verification
. - Quality Assurance Testers take tasks from the Pending section and confirm that the bugs are fixed.
- Continuing problems are returned to the fixer, moved back to in-progress.
- Bugs that are
Verified Fixed
are move to theDone
lane.
- QA also has a section on the main Calamity Project for tasks relating to testing specific areas of the game or content for new bugs.
QA Testing Process
- Most of the QA testing will be on the
release
branch.
Debug Tools
- Details of Debug Tools to be added soon.
Audio System
Unity Documentation
🎓 The Basics: https://learn.unity.com/tutorial/working-with-audio-components-2019-3
Official Documentation
- tldr; We use
.ogg
audio files because Unity converts them anyway. - https://docs.unity3d.com/Manual/AudioOverview.html
- https://docs.unity3d.com/352/Documentation/Manual/AudioFiles.html
Working with Audio Files in Calamity
Adding New Audio Files
- Our AudioFiles are stored in ScriptableObject containers that expand the audio files properties and it gives us greater control over the sound.
- Export your audio files as
.ogg
extension. - Rename the file to be something easily understandable from the title.
- Follow a standard naming convention like PascalCase, i.e. Write the title as one group of text but capitalize each word.
- Add a common suffix at the end of the title that describes at a glance how the audio is used, e.g.
Sfx
for sound effects orMusic
for music. - A full example of a well named file would be renaming from
619320__asuperiorpotato__facility-hum-ambience-loopable.ogg
toFacilityHumSfx.ogg
.
- After renaming the file, add it to the project by dragging it into its appropriate folder in our asset structure, or create folders under the Audio section as you need.
- First load audio files into the
_TestEnvironment
folder underAssets
in the project window if you're loading a large number of files or are unsure of how to immediately integrate them. - Remove or delete anything in the Test Environment folder at no risk to the project.
- This enables you to do final adjustments to the audio files before committing them to the projects assets.
- Select the audio file in the project window to adjust its base settings.
- First load audio files into the
AudioFileContainers
- To integrate the audio file into the game, create a new
AudioFileContainer
from the CreateAssetMenu.- Right click in the project folder window, select Create > Audio > AudioFileContainer.
- Drag your audio file onto the Source (Audio Clip) section.
- Set the
Clamp Volume
to somewhere around1
to test the sound at a regular volume.- A large range for the Clamp values will have the sound play at a range of volumes in the game. 0 is mute.
- Use the
Preview
button to test. Note: Use it sparingly, it's been finicky. To be safe press Play in Unity before previewing. Unity needs to compile.
- The file is now successfully setup to work with our system.
How Audio is Used:
- When should the audio be played and how?
- Instead of being a fixed to a single use, we tie the
AudioFileContainer
to anAudioEvent
. - An AudioEvent is a feedback response to something that happens in the game.
- We pair the audio file to an event to say that sound will play when this happens through event listeners.
- Instead of being a fixed to a single use, we tie the
AudioEvents
- There's a placeholder
_TestAudioEvent
to start with and use for testing AudioFilesContainers. - Create new AudioEvents in the AudioEvents folder or the test folder from the CreateAssetMenu.
- Right click in the project window Create > Audio > SimpleAudioEvent
- Drag the
AudioFileContainer
into one of the AudioFiles slots in the Unity Inspector. - Set the
volume
andpitch
ranges or other settings as needed.
- Now the Audio Event is ready to be used by the Level Designers or Audio Level Designers.
Mixers
- To open the Mixer panel in Unity go to Window > Audio > Audio mixer.
- We have a few Mixers as placeholders, they can be changed as needed.
- Audio Designers should modify or add to the Mixers, add effects, adjust any audio settings as needed.
- Consider a File and Mixer naming convention that's consistent between all involved parties. e.g. PascalCase and adding a title suffix like
Sfx
, etc.
- Consider a File and Mixer naming convention that's consistent between all involved parties. e.g. PascalCase and adding a title suffix like
Command System
Player Commands
are C# scripts that carry out a specific function then trigger callback events as needed.- Each Command is stored as a ScriptableObject that triggers callback GameEvents that are easily modifiable in Unity.
- Commands can invoke a series of callback events at once.
Creating New Commands
- An empty Command that has no functionality other than to trigger callback events and catalog the command is available from the CreateAssetMenu.
- Add it to the Commands folder in the Assets folder. Add new paths as needed.
- Right Click, select Create > Player Commands >
EmptyCommand
to create a new empty command. - Set it up with callbacks.
GameEvent Callbacks
- Commands can trigger a group of callback events when invoked.
- Our callback events are stored as ScriptableObjects called GameEvents.
FeedbackCommands
FeedbackCommands
are special callback commands designed to trigger additional commands after a PlayerCommand is invoked, e.g. play a sound, flash the screen, camera shake, etc.
FeedbackCollections
FeedbackCollections
are groups ofFeedbackCommands
that can happen at once. e.g. Multiple sounds, multiple screen effects or UI changes, etc.- // Audio is the only feedback type currently implemented.
GameEvents
GameEvents
are simple triggers that invoke a wide range of functionality in the game.- Organize new GameEvents into relevant folders in the Game Events folder.
- Create new GameEvents from the CreateAssetMenu
- Right Click in the Game Events folder from the project window, select Create > Events > GameEvent
Lighting
- Details of lighting system to come.
Test Environment
- The
Activate Launch Scene
button in the Unity Editor will toggle thePlay
button between the current open scene and the main game. - Changes to the
TestEnvironment
scene, or the "_TestEnvironment" folder under the Assets folder, won't be uploaded in a pull request so changes are at no risk to the project. - Use the
_TestEnvironment
folder to load external files into a safe area in the project before integrating them to the rest of the folder structure. - Use the
TestEnvironment
Scene to test models, features, or anything experimental.
UI Tools
- Details of UI tools to come.
Technical Details
Packages
- Only add additional packages to the project if absolutely necessary.
- Keep external dependencies to a minimum.
- Consult the team before adding new packages!
- Ideally, external plugins should be in a package format and go in the
packages
folder, not inassets
folder.
Imported Packages
- Ink
- Poly perfect
- TextMeshPro
- Toolbar extender
Code and Menu Organization
AssetMenuSortOrders
- Our asset menu organization class is called
AssetMenuSortOrders
- They're preset file paths that keep new objects in order.
- The order is alphabetized. You can find the correct order in your IDE by searching for all the references to the paths you're using.
- // If anyone has ideas for a simple alphabetize algorithm for the menus, that sorts when the editor refreshes, please share!
- In the script, set the file path with [CreateAssetMenu]
- Direct new command scripts to the following directory, add sub-directories as needed,
[CreateAssetMenu(menuName = AssetMenuSortOrders.CommandsPath + "Empty Command", fileName = "EmptyCommand", order = AssetMenuSortOrders.CommandsOrder + 2)]
- Check AssetMenuSortOrders.cs for the list of existing menu paths.
MenuItemSortOrders
- MenuItemSortOrders are similar to AssetMenuSortOrders but it has less organization functionality.
- It's used for organizing new menus in Unity, e.g.
[MenuItem(MenuItemSortOrders.SceneSettings + Emoji.EmojiConstants.Recycle + "Refresh Scene List Menu", priority = 50)]
Custom Namespaces
- One way to keep our code organized and modular is by using functional-based namespaces. This helps us to better manage dependencies and keep things streamlined.
- Namespaces can also help avoid naming conflicts with imported packages.
- Our namespace prefix is
Calamity
followed byFunctionalGroupTitle
, e.g.
using Calamity.AssetOrganization;
namespace Calamity.EventSystem
{
// class
}
- Wrap classes in namespaces starting with the prefix followed by the related system.
- Add new namespaces as needed.
- Remove any unused namespaces.
- Note any added namespaces in your pull request description.
Our custom namespaces (so far):
// Add and use namespaces as you need.
using Calamity.AssetOrganization;
using Calamity.Audio;
using Calamity.CommandSystem;
using Calamity.DebugTools;
using Calamity.EventSystem;
using Calamity.Math;
using Calamity.Primitives;
using Calamity.SceneManagement;
// external namespaces
using Seeker.StringMarkupEncapsulation;
using Seeker.Emojis;
using RoboRyanTron.Unite2017.Events;
using Ink; // And it's sub-namespaces
Math Formulas as ScriptableObjects
- Storing simple math formulas as ScriptableObjects enables us to arrange GameEvents based on these formulas.
Post-Processing
- Details of our post-processing effects to come.
Primitives as ScriptableObjects
- Storing primitive data types such as int, float, vector3, and more as ScriptableObjects enables us abstract data from our classes into standalone components in Unity.
Scene Management
- Scenes are abstracted by functional-use, e.g.
Audio
orMainMenuUI
. - Scenes are added to the Scenes list menu item after refreshing from Scenes > Settings > Refresh Scene List Menu
- Scenes are organized into folders under Scene > Game Scenes > [game scene category] >
New Scene
Shaders
- Details of our shaders to come.
- Custom shaders details and use of ShaderGraph.
Universal Render Pipeline (URP)
- Details of how we integrate URP to come.
Visual Effects
- Details of our visual effects to come.
- Details of our use of VFXGraph.
Technical Gameplay Details
Monsters Factory Module
- All monsters subscribe to this singleton and are created by it. Links directly to the monster prefab via FindAsset(). Instantiates new monsters to the scene by loading scriptable objects.
- Scriptable Object Template script
- Single Monster Prefab with mesh, animator and behavior script
- Folder for Scriptable monsters filled with all monsters in the game
- New monster features are added to the template with a default value so old objects still function.
- Monster meshes will follow a naming convention and be kept in a monster mesh folder. The scriptable objects will target this same named mesh. This link will be generated by an editor script. A second editor script will check for mismatched naming conventions, SOs without meshes and vice versa.
- A default animator will be fine for most monsters.
- A default behavior script will be fine for most monsters | This script will include the monster’s stats, populated by the SO.
Inventory Controller
- All weapons subscribe to this singleton.
- Scriptable Object Template script
- Single Weapon Prefab with mesh, animator and shooting script
- Folder for Scriptable weapons filled with all weapons in the game
- New weapon features are added to the template with a default value so old objects still function.
- Weapon meshes will follow a naming convention and be kept in a weapon mesh folder. The scriptable objects will target this same named mesh. This link will be generated by an editor script. A second editor script will check for mismatched naming conventions, Sos without meshes and vice versa.
- Depending on the weapons, we may need an animator per weapon. This can follow the same naming conventions and folder placement as weapon meshes.
- A default shooting script will be fine for most if not all weapons. | This script will include the weapon’s stats, populated by the SO.
Camera Controller
- Automatically switching to cameras by room proxy.
- Prefab for each camera area. A large hitbox with inspector sliders to adjust size, camera location and pitch/angle.
- An editor script to run a check that all camera area's are touching. All walkable area must have exactly one camera hitbox.
Bestiary
- Bestiary definitions can live in the monster and weapon SOs.
- Possibly use Ink or a similar function to manage large volumes of text data.
Level structure
- Level design is abstract
- Each level will need to be custom build
- Instead of a level creating script, we will need something to handle transitioning from one area to another.
- When moving from room to room, 1 scene is preferable but repositioning the game's origin will be required to avoid float point issues.
- Consideration for a polygon editor package within unity depending on design preferences