BitterBuster Implementation
Overview
This document provides implementation details on BitterBuster. For further Unity documentation see their User Manual.
Code Structure
BitterBuster follows the standard folder structure of Unity Projects. Most edits to the game will take place in the Assets
folder, which contains:
Assets
| - Animations # All animations + animation controllers
| - Art # All art assets
| - Materials # All object materials
| - Photon # Photon Package
| - Plugins # Other Unity Plugins
| - Prefabs # All prefabs
| - Presets # Editor presets
| - Rendering # Rendering-related assets
| - Resources # Special Resources folder
| - Scenes # All game scenes
| - Scripts # All C# scripts
| - Sounds # All SFX + BGM sound assets
| - TextMesh Pro # Text rendering package
| - Textures # All textures
Most of these folders contain various assets that are used within the game, such as 3D models, 2D art, and sound. The below sections will highlight some of the key folders to be familiar with.
Scenes
Each "level" within Unity is captured within a Scene. For BitterBuster, there are three main scenes:
- StartScreen: The first scene when a user opens the game, provides options to host, join, or quit the game.
- PreGame: The setup scene after a player joins or creates a room. This is where hosts update the settings of the game and players can select their role of Selector or Explorer.
- Game: The main game scene. This contains the entire Candy Kingdom world along with all of the components of the gameplay.
To edit a particular Scene, double-click its corresponding file in this folder.
Resources
The Resources folder is a special folder in Unity that allows for runtime loading of assets. It contains prefabs used by Photon to create multiplayer instances of during runtime (such as the player objects), along with default Bitter/Sweet candies that are loaded at runtime as well.
Scripts
All of the code within the game is located in this folder. Each of the code files is also separated into more specific directories based on functionality:
Scripts
| - Controllers # GameObject controller scripts
| - Logging # Logging-related scripts
| | - LogEvents # Defined log events
| - SceneManagers # Scene manager scripts
| - Sound # Sound-related scripts
| - UI # UI-related scripts
| | - Components # Subset of scripts for specific UI components
| - Util # General utility scripts
Controllers
Each of the scripts in this directory relate to a key GameObject used in the main game - for instance, controllers for the Explorer, for traps, for houses, etc. Each instance of this object will contain the corresponding controller script as a component, which will contain common functionalities and parameters for the object.
Logging
All logging-related logic is contained in this folder. This consists of the overall Logger
that is used by the host along with all of the different events that the Logger
will log.
SceneManagers
Each scene in BitterBuster has a corresponding manager class, which handles the overall logic of that given scene.
The GameManager
class is the most important script in this folder as it contains all of the overarching logic for the main game. This includes handling game events by broadcasting them to all machines.
An additional class defined in this directory is the EventManager
class, which is used in the main game to broadcast and add/remove listeners to all gameplay events.
Sound
This directory contains managers and related scripts for SFX and BGM used in the game.
UI
This directory contains managers and related scripts for all of the UI used in the game.
Util
This directory contains utility scripts used in the game. Some key ones to note are:
- Candy.cs: Contains definitions for Candy fields and other related classes, such as a CandyLoader
.
- FileSelector.cs: Contains wrapper functions for a file selector plugin that allows for runtime usage of a local file selector. This is currently used to upload JSON files, candies, etc.
- RoomCustomProperties.cs: Allows for access and storage of room-based properties, such as the global game settings and Explorer/Selector ID. This is used to persist values between the PreGame
and the Game
scenes.
Multiplayer Structure
For more details, Photon provides a documentation wiki describing how their many functionalities work.
To start a multiplayer session, one machine must first create a room with a room code. The creater of the room is considered the MasterClient
. This room code can then be used by other machines to join the same room. If the MasterClient
disconnects, one of the other players will then become the MasterClient
; if there are no other players in the room, it is shut down.
When using Photon with Unity, all GameObjects are instantiated locally, and all function calls will only affect the local objects. In order to make cross-machine calls, we use Remote Procedure Calls (RPCs). This will be what we make use of throughout the game to synchronize core game events between the different machines.
General Game Flow
The following sections provide a general overview of what happens behind-the-scenes in a given play session.
Start Scene
On the start screen, a player can select whether they would like to host or join the game. In clicking the button, the game will prompt them for a game code, then create or join the room respectively depending on which option they pick.
Here, a host status is determined by setting the playerType
field of the Player
class to be Host
. This value persists onto future scenes. From Photon's perspective, this host is also the MasterClient
.
PreGame Scene
Once a host has created a game, they are led to the pre-game screen:
Here, they can also access the settings for the game:
The host can then use this screen to upload any gameplay settings through the JSON configuration file. This value is stored using Photon's support for room-based custom properties, of which the main functions are wrapped in the RoomCustomProperties
class.
From a player perspective who has joined the room, they can select their role on the pre-game screen:
This updates the playerType
in the Player
class to whichever they select, along with storing the user ID within the RoomCustomProperties
class. Both values will persist into the main game scene; the former will be used for most of the gameplay, and the latter is used to ensure exclusivity of role selection (i.e. making sure we don't let both players choose the same role) and to handle player disconnects.
Once two players have joined and selected their roles, the host can click a button to start the game.
Main Game
Initialization
Upon entering the game, there are a number of items that are immediately processed behind the scenes.
Firstly, in using the playerType
field, many objects will initialize/destory themselves based on what the local player's role is. For instance, the Selector will only see the Selector UI; houses will only initialize their own mailbox colors on the Explorer side; only the Host will be able to toggle between the two cameras; and so on.
The Host also will do some additional setup, such as loading the Candies. Because Candy images are only configurable on the Host side, only the Host will upload and store the images, and most future communication about Candies will be through passing their corresponding ID numbers around; only when the Candy really needs to be displayed (i.e. for the Selector's selection process) will the actual image bytes be passed along.
The two players are presented with their respective onboarding screens (which will differ depending on the player's roles), and then can start the game together.
Main Loop
Afterwards, the two players enter the main gameplay loop. Some aspects, such as the Explorer's position and rotation, are values that are automatically synced by Photon. Otherwise, the general gameplay loop will result in the following:
- Player performs an action (visits a house, clicks a button), which causes some local changes
- Action communicates with the GameManager through making a call
GameManager.Instance.Broadcast...(...)
- The
GameManager
function will call a corresponding RPC function on each machine - The RPC function will locally broadcast the
GameEvent
- Any listeners to the broadcasted event will respond accordingly
This continues until time runs out and a game over state is triggered in the GameManager
.