Have you ever played Geometry Wars? I hadn’t—at least, not the original version released by Microsoft Games Studio in 2003. My version, created 20 years later, is probably only 26.8% as sophisticated as the original. But let me tell you, game development involves a lot of hard work, and this was my first game.
In my case, I used the Entity-Component-System (ECS) pattern to manage architecture concerns, simplifying the development process. I also relied on Dave Churchill’s Assignment 2 to understand the game logic and functionality. I recreated the files from scratch, extracting everything I could from his video. After completing the snippet code and handling the mountain of compilation errors from my compiler, I implemented the functionality, and voila—a shooter primitive, the dream of any kid learning geometry and trigonometry.
Project objetive
Create a game using the ECS (Entity-Component-System) architectural pattern to ensure streamlined game logic implementation.
Game logic
The player starts in the middle of the window and can move in four directions using the W, A, S, and D keywords. The player shoots bullets towards the mouse pointer when the left mouse button is clicked. Enemies spawn an x time with random properties like size and speed. When the player defeats an enemy, it explodes and gives birth to a specific number of smaller enemies, positioned based on the original enemy’s vertices.
ECS arquitecture
The table below consolidates the abstraction of the various terms and concepts used in the ECS design pattern within the game structure.
Entities | Components | Systems |
---|---|---|
Player | CTransform: Manages position and velocity | sEnemySpawner(): Spawns enemies at a predefined rate |
Enemy | CShape: Handles data for drawing entities | sMovement(elapsedTime): Handles player movement based on elapsed time |
SmallEnemy | CCollision: Manages the collision radius of entities | sCollision(): Manages collisions between game entities |
Bullets | CScore: Handles the score | sUserInput(): Processes user input |
CLifespan: Stores the lifespan | sRender(): Renders the current state of the game | |
CInput: Initializes keyboard inputs as up, down, right, left, shoot | sLifespan(): Manages the lifespan of entities |
ECS Introduction
In ECS, systems are functionalities that implement specific parts of game logic. For instance, in the sRender() function, its role is to update entity positions and draw them within the game window. This includes rendering textures as the background and text as the title and the score.
To interact with data within each entity, systems access components associated with each entity. In this game, every entity requires rendering, so the sRender() function accesses relevant components to carry out its tasks. Entities can possess different combinations of components, and systems logically invoke operations based on their specific requirements.
Project Structure
-
Components.h: Contain the 6 components structs in the game: CTransform, CShape, CCollision, CScore, CLifespan and CInput.
-
Entity.h: Contain the Entity class and defines the attributes of game entities. This class encapsulates the creation and manipulation of entities, providing methods for initialization, updating, retrieval, and removal.
-
EntityManager.h: The EntityManager class is responsible for managing the lifecycle of entities within the game, including their creation, update, and removal.
-
Game.h: The Game class is pivotal in the project, responsible for initializing the game, reading configuration data from “config.txt,” and managing all systems. It oversees the creation of entities, defining their components, and setting initial properties. Additionally, the class manages input through the window interface, ensuring seamless interaction with the game environment. As the central orchestrator, the Game class integrates subsystems and ensures the cohesive operation of all game elements, contributing significantly to the overall gameplay experience.
-
main.cpp: The main function initializes an instance of the Game class, passing the configuration file path “config.txt.” It then calls the run method, which executes the main game loop. The program returns 0 upon completion, indicating successful execution.
-
Vec2.h: The Vec2 class represents a 2D vector and serves as a fundamental building block for mathematical operations in the program. The class includes default and parameterized constructors for creating instances of 2D vectors with specified coordinates.
Technology
- Programing lenguage : C++17
- External libraries : SFML
- Build system generator: Make
Creation Process
For an effective creation process, I highly recommended to follow Dave’s project methodology, known for its clear explanations and structured approach. To enhance your workflow, consider the following steps:
-
Define Technological Needs and Documentation Requirements. Document key aspects such as APIs, libraries, and any third-party integrations required for the project.
-
Gain a comprehensive understanding of the project’s underlying logic and requirements. This includes studying game mechanics, architectural patterns, and overall design goals to ensure alignment with project objectives.
-
Set Up the Project Environment and ensure that dependencies are correctly installed and versioned to avoid compatibility issues.
-
Incremental Debugging and Version Control its vital.Adopt a progressive debugging approach, testing and debugging components as they are implemented. Utilize version control systems such as Git to manage code changes effectively and maintain a stable codebase throughout development.
-
Define Entity Creation and Management: Clearly define how entities are created, managed, and interact within the game environment. This involves specifying components and systems mechanics.
Lessons Learned
-
Time Management in Game Development: Time management is crucial due to the dynamic nature of games. Implementing a delta time parameter for controlling entity movements helps maintain smooth gameplay and consistent performance across different hardware configurations.
-
Understanding Coordinate Systems in Graphics APIs: While OpenGL has the origin (0,0) coordinate in the bottom-left, SFML has it in the top-left corner. This subtle difference can drastically change where entities move, render, and transform on the screen.
-
Handling Position and Velocity as Vectors: Utilizing vector mathematics for handling position and velocity in both 2D and 3D spaces simplifies calculations and enhances realism in game physics and movement mechanics.
-
Dynamic Entity Management: The entity manager plays a pivotal role at runtime by dynamically creating and removing entities in the game. This is, of course, just one way to handle entities in a game; everything depends on what you want to create and how you intend to achieve it. One strategy to alleviate pressure on the entity manager is implementing the object pool design pattern.
References
Ref[2] = https://www.sfml-dev.org/download.php
Ref[3] = https://github.com/SanderMertens/ecs-faq
Ref[4] = https://github.com/DanielaHz/GeometryWars_SFML_Version.git