-
Notifications
You must be signed in to change notification settings - Fork 0
Source code walkthrough
BubbleGrow is designed to be "easier" to understand and is filled with comments to help navigate each section. The basic breakdown of the game is into two sections: the "game state", which holds all information regarding the game and processes the game; and the "Graphics" section, which handles everything the player sees, hears, and inputs into the game from his keyboard and mouse.
This walk through is divided by the two sections and covers the source code by the main classes in the source code.
The game state handles the game's logic and units, and can run completely on its own (it has no knowledge of the graphics, audio, and input). The game state is divided into 3 major classes: the World, the Players that belong to that World, and the Units that belong to each Player.
The "World" class manages a single game. It holds references to each Player (including computer players), determines the size of the game world, and has functions that process the entire game world.
- players: A map that contains each Player reference, access by their unique id.
- map_radius: Determines the size of the game world. Players who leave this area automatically lose all their units and are removed from the game.
- UpdateAndProcess(duration): Processes one "cycle" of the game (where duration the length of a single frame in the game).
- AddPlayer(): Adds a new Player to the game, automatically assigning it a unique id.
- AddResources(amount, radius, density): Adds a "resource Player" to the game, which is a special player type that only exists to create the resource bubbles you see in game.
- FindUnit(owner_id, unit_id): A helper function used by other classes to retrieve the reference for a specific Unit based on its unique id.
- EndWorld(): This cleans up the game world, removing circular shared pointer references that would normally cause memory leaks. Since both the Player and Unit class also reference World, this has to be manually called instead of being in the destructor.
The "Player" class manages the state and actions of each player, including the computers. Each player has a position and Units that follow the position of the player. Additionally, a Player has resources that allow it to buy more Units.
- ai_type: This determines whether the Player's actions are controlled by AI, by the human player, or by nothing.
- requests_array: The requests array holds a slot for each request type that exists, and it is scanned every frame to determine if any actions need to be taken for the player. This includes things like requests to move to a destination or create a new Unit.
- unit_requests: Similar to the requests_array, but holds requests for individual Units instead.
- units: A map of all Units owned by the Player, accessed by its unique id.
- position: The position of the player in the game World.
- resources: The number of resources the Player has.
- ProcessPlayerRequests(duration): Executes all requests made for the player.
- PlayerMoveRequest(destination, speed): Creates a request (in the requests_array) to have the player move toward the desired location.
- PlayerPurchaseRequest(purchase_amount, purchase_type): Creates a request to attempt to purchase a Unit of the specified type.
- Update(duration): Calls the update function for each Unit the player controls.
- ProcessRequests(duration): Calls the process request function for each Unit the player controls, then executes the player's own requests.
- MakeDecisions(duration): Calls the Unit's AI to make a decision (request) for every Unit the player owns, then for computer controlled players calls its own AI to make a decision.
- RemoveExpiredUnits(): Removes dead units from the player's ownership.
- CreateUnits(amount, type): Creates a unit of the specified type for the Player.
- RandomWanderLocation(): Provides a random location somewhere on the world map.
- PurchaseUnits(amount, purchase_type): Attempts to purchase the units (will only purchase what the player can afford).
The "Unit" class holds the state and methods for every Bubble Unit in the game. Each Unit is capable of making AI decisions and executing those decisions and are managed by a Player class.
- events: Events are actions recorded by the Unit, such as an attack, a death, or gathering resources. This is used by the sound manager to know what sounds to play.
- WalkTo(destination, duration, update_action): Processes a walk to action for the given duration.
- Attack(target, target_owner, duration): Processes an attack action for the given duration.
- Gather(target, target_owner, duration): Processes a gather action for the given duration.
- Update(duration): Updates the Unit (including any pending damage to apply).
- MakeDecision(request): Calls the AI to make a decision and create a request for it.
- ProcessRequest(request, duration): Processes a request for the Unit, such as attacking or walking.
This section covers everything involving what is seen, heard, and input on the game screen. This section of code, unlike the Game State, is dependent on SFML.
The "Renderer" handles the display of everything on the screen. Uses a combination of batch drawing and SFML supported classes like "sf::Sprite" and "sf::Text" for most of the displayed graphics.
- events: Used by the sound manager to know which sounds to play, such as clicking a menu entry or starting a new game.
- window: This is the reference to SFML's RenderWindow, which is used to display everything on the screen.
- menu_text_entries: This holds all existing menu entries that are displayed, and is used to track if the mouse clicks one of them.
- current_menu: Tells the renderer which menu stage it is currently at.
- RenderGame(duration): Based on whether at the main menu or in a game, renders either.
- RenderUnits(): Renders all players and their units.
- RenderPlayer(player_to_render, main_player_position): Renders all units owned by a player.
- RenderInterface(duration): Renders the interface, including text, logo, and directional arrows.
- RenderDirectionArrows(): Renders arrows at the edge of the screen that point toward other players.
- RenderMenu(): Renders either the in-game menu or the main menu.
The "InputHandler" class is a helper class to the Renderer and is tightly coupled to the Renderer. It handles processing all input from the user.
- renderer: The InputHandler class is a "friend" of the Renderer and, using this reference, the InputHandler class has access to all the private members of the renderer.
- QuickMatchGame(num_players = 16): Initializes a new game by creating a new world and new players in that world. One of the players are assigned to the user.
- PollEvents(): SFML adds all input events (such as a keyboard or mouse click) to a stack that is processed by the InputHandler. This allows the InputHandler to know all input events that occurred the previous frame.
- ProcessInputs(): This function handles all non-event related inputs, such as the current mouse position, to determine if any other input handling is needed.
- GamePollEvents(event): This is called by "PollEvents()" if the current mode is in game.
- MenuPollEvents(event): This is called by "PollEvents()" if the current mode is at the main menu.
The "SoundManager" class goes through all events that have occurred and plays the corresponding sounds, whether that be an attack, gather, death, mouse click on a menu, etc. Additionally, handles which song is playing.
- sound_files: A map of all sound files loaded in the game.
- sounds: A vector of 255 "channels" from which sounds can be played on. This is due to SFML only supporting up to 255 simultaneous sounds.
- sound_availability: Keeps track of which sound channels are currently available for new sounds to play on.
- master_volume: Scales the volume of all sounds.
- PlaySound(id, screen_position): Plays a sound at the given screen position (which, using stereo sound, changes where you hear the sound directionally and volume-wise).
- PlaySound(event): Plays a sound depending on which event occurred, such as an attack or death.
- PlayEventSounds(events): Goes through all pending events and plays their corresponding sounds.
- ProcessPendingEvents(): Goes through all events in both the game World and Renderer to determine what needs to be played.