Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ba8a30d
Added sprite class and new enemy image
gregormaclaine Nov 17, 2021
f023e3f
Implemented sprite class in player and staticsprite
gregormaclaine Nov 17, 2021
af50464
Added third platform
gregormaclaine Nov 17, 2021
790fb27
Added delta time fix
gregormaclaine Nov 17, 2021
4eea575
Merge branch 'main' into sprite-class-again
gregormaclaine Nov 17, 2021
3364cb0
Added control-r to also reset enemies positions
gregormaclaine Nov 17, 2021
1a88d13
Merge branch 'main' into sprite-class
gregormaclaine Nov 17, 2021
7b0abe9
TEMP: states
gregormaclaine Dec 8, 2021
dc2de67
Edits for Mac installation
gregormaclaine Feb 2, 2022
01f025c
Added update function to sprite class
gregormaclaine Feb 4, 2022
9acd164
Updates to vscode settings
gregormaclaine Feb 4, 2022
c420b68
TMP: Added main functionality of menu buttons and finite state machine
gregormaclaine Feb 4, 2022
023f51a
Moved game logic to Game class and finished FSM
gregormaclaine Feb 4, 2022
e8c0c15
Merge branch 'main' into finite-state-machine
gregormaclaine Feb 5, 2022
e557119
Added a lot of comments
gregormaclaine Feb 7, 2022
5eaf888
Added some docs on the FSM
gregormaclaine Feb 8, 2022
16f4c26
Fixed bugs to work on windows
Feb 9, 2022
62a2750
Fixed hitbox always showing bug on windows
Feb 9, 2022
c5f1c48
Sprites now don't clip through platforms during window resize
gregormaclaine Feb 12, 2022
497b1c0
Separated deltatime checker into separate file
gregormaclaine Feb 12, 2022
3ca3c02
Increased certainty of deltatime anomaly & fixed enemies clipping thr…
gregormaclaine Feb 12, 2022
ed00e4e
Hopefully fixed warning in vscode settings from deprecated key
gregormaclaine Feb 13, 2022
9585f85
Changed enemies and platforms to std::vector from basic C array
gregormaclaine Feb 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ gmon.out
*.stats
Thumbs.db
.DS_Store
PCH.hpp

# VS Code
.vscode/ipch
Expand Down
3 changes: 1 addition & 2 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
],
"includePath": [
"${workspaceFolder}/src",
"${workspaceFolder}/lib",
"/usr/local/include/**"
"${workspaceFolder}/lib"
],
"defines": [
"_DEBUG"
Expand Down
28 changes: 26 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,33 @@
"streambuf": "cpp",
"thread": "cpp",
"typeinfo": "cpp",
"variant": "cpp"
"variant": "cpp",
"__bit_reference": "cpp",
"__bits": "cpp",
"__config": "cpp",
"__debug": "cpp",
"__errc": "cpp",
"__functional_base": "cpp",
"__hash_table": "cpp",
"__locale": "cpp",
"__mutex_base": "cpp",
"__node_handle": "cpp",
"__nullptr": "cpp",
"__split_buffer": "cpp",
"__string": "cpp",
"__threading_support": "cpp",
"__tree": "cpp",
"__tuple": "cpp",
"bit": "cpp",
"complex": "cpp",
"ios": "cpp",
"locale": "cpp",
"stack": "cpp",
"__availability": "cpp"
},
"terminal.integrated.automationProfile.windows": {
"path": "C:/Program Files/Git/bin/bash.exe",
},
"terminal.integrated.automationShell.windows": "C:/Program Files/Git/bin/bash.exe",
"terminal.integrated.env.windows": {
"Path": "C:/mingw32/bin;C:/SFML-2.5.1/bin"
},
Expand Down
Binary file removed assets/QuitButton.png
Binary file not shown.
File renamed without changes
Binary file added assets/button-background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/enemy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/play.png
Binary file not shown.
72 changes: 72 additions & 0 deletions docs/Window FSM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Window Finite State Machine

To make the game engine usable and scalable, there needed to be the option to split functionality into different pages and files, such as the menu and the game itself. Therefore a **finite state machine** (hereafter FSM) is needed to keep track of what state the program is currently in and to provide methods of transitioning from different states.

The primary logic for the FSM is held in the `main.cpp` file as it runs the main game loop and the high-level process of the program. This file has two main functions: **the gameloop** & **the stateloop**. Below shows some code for the gameloop:

```c++
template <class T>
WindowStates MainStateLoop(T current_state, sf::RenderWindow& window)
{
sf::Event event;
// When next_state is NONE then do not leave current state
WindowStates next_state = WindowStates::NONE;
while (next_state == WindowStates::NONE && window.isOpen())
{
deltatime = deltatime_clock.restart().asSeconds() * 450.f;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();

current_state.handle_event(event);
}

current_state.update(next_state);

window.clear();
current_state.show();
window.display();
}
return next_state;
};
```

The above code makes use of the `template <>` c++ feature which allows a function to take in an object of any type and just assume that it has the correct interface (functions). This means that states can do the same process even when represented as different classes such as the `GameState` and the `MenuState`.

The function then initialises some variables and begins the gameloop. It runs as long as the window is open or `next_state` is changed. `next_state` pretty much always takes the value of NONE, as that means that no state transitions should occur.

When `next_state` changes, we see that the gameloop will terminate and the new state will be returned by the function. Then it will move back to the state loop shown here:

```c++
// Here define all states that can be moved to in the program
MenuState menu_state(window);
GameState game_state(window);

// Begin the program in the MENU state
WindowStates current_state = WindowStates::MENU;
while (window.isOpen())
{
window.setView(window.getDefaultView());
// Depending on WindowState run gameloop with correct class instance
// Once gameloop ends, it will return the next state and then redo this switch
// statement with the new state.
switch (current_state)
{
case WindowStates::GAME:
current_state = MainStateLoop(game_state, window);
break;

default:
std::cout << "Error: Window State is not defined - Defaulting to menu" << std::endl;

case WindowStates::MENU:
current_state = MainStateLoop(menu_state, window);
break;
}
}
```

Here you can see the stateloop, which stays open as long as the window is open. It starts by resetting the window view, and then looks and the current state, which at the start is set the MENU.

Depending on the current state it will run the `MainStateLoop` function which holds the gameloop on a particular state object. Once the gameloop ends, it will return the new state and that will be set to current_state.
38 changes: 16 additions & 22 deletions src/Enemy.cpp
Original file line number Diff line number Diff line change
@@ -1,39 +1,33 @@
#include "Enemy.hpp"
#include "Sprite.hpp"

Enemy::Enemy(sf::Vector2f _pos, sf::Vector2f _size) :
Hitbox { _pos, _size }
{
// Load Texture
texture.loadFromFile("assets/enemy.png");
sprite.setTexture(texture);
size = sf::Vector2f(texture.getSize());
Enemy::Enemy(sf::Vector2f _pos, sf::Vector2f target_size) :
Hitbox { _pos, target_size },
Sprite { "assets/enemy.png", _pos, target_size }
{}

// Initialise Position
pos = _pos;
Enemy::Enemy() :
Hitbox { sf::Vector2f(), sf::Vector2f() },
Sprite { "assets/enemy.png", sf::Vector2f(), sf::Vector2f() }
{}

sprite.setPosition(pos);
}

void Enemy::updatePosition(StaticSprite* platforms, sf::Vector2f player_pos)
void Enemy::updatePosition(std::vector<StaticSprite*>& platforms, sf::Vector2f player_pos)
{
float speed = 0.4f;
const float horiz_vel = player_pos.x > pos.x ? speed : -speed;
update(horiz_vel);

for (unsigned int i = 0; i < 3; i++)
for (StaticSprite* platform : platforms)
{
if (overlaps(platforms[i]))
{
correctHitboxOverlap(platforms[i]);
}
if (overlaps(*platform))
correctHitboxOverlap(*platform);
}

// Gets direction to player

sprite.setPosition(pos);
}

sf::Vector2f Enemy::getPosition()
void Enemy::setPosition(sf::Vector2f _pos)
{
return pos;
pos = _pos;
update_sprite(pos, size);
}
10 changes: 6 additions & 4 deletions src/Enemy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define Enemy_H

#include "Hitbox.hpp"
#include "Sprite.hpp"
#include "StaticSprite.hpp"

/**
Expand All @@ -10,18 +11,19 @@
* @param pos The initial position
* @param size The width and height of the enemy's hitbox
*/
class Enemy : public Hitbox
class Enemy : public Hitbox, public Sprite
{
private:
sf::Texture texture;

public:
sf::Sprite sprite;
Enemy(sf::Vector2f pos, sf::Vector2f size);
Enemy();

// Updates the position of the sprite
void updatePosition(StaticSprite* platforms, sf::Vector2f player_pos);
sf::Vector2f getPosition();
void updatePosition(std::vector<StaticSprite*>& platforms, sf::Vector2f player_pos);
// Force sets the position of the sprites
void setPosition(sf::Vector2f _pos);
};

#endif
22 changes: 22 additions & 0 deletions src/Game/Background.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "Game/Background.hpp"

Background::Background(sf::RenderWindow& window) :
Sprite { "assets/background-image.png", sf::Vector2f(0, 0), sf::Vector2f(window.getSize()) }
{}

/**
* Calculates where to place the background based on the player view,
* and draws it behind all other sprites.
*
* @param window A reference to the main window object
* @param view A reference to the currently active player view
*/
void Background::draw(sf::RenderWindow& window, sf::View& view)
{
const sf::Vector2f view_center = view.getCenter();
const sf::Vector2f view_size = view.getSize();

const sf::Vector2f left_top = sf::Vector2f(view_center.x - view_size.x / 2, view_center.y - view_size.y / 2);
update_sprite(left_top, view_size);
window.draw(sprite);
}
20 changes: 20 additions & 0 deletions src/Game/Background.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef Background_H
#define Background_H

#include "Sprite.hpp"

/**
* A sprite that draws a static image to the background during the game.
* It takes into account the moving window view and adjusts the background position to
* account for it.
*
* @param window A reference to the window
*/
class Background : public Sprite
{
public:
Background(sf::RenderWindow& _window);
void draw(sf::RenderWindow& window, sf::View& view);
};

#endif
103 changes: 103 additions & 0 deletions src/Game/Game.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#include "Game/Game.hpp"

/**
* Initialise all objects and classes inside the game
*/
Game::Game(sf::RenderWindow& _window) :
window { _window },
player { sf::Vector2f(500.f, 0.f), sf::Vector2f(215.f, 258.f) },
platforms {
new StaticSprite("assets/platform.png", sf::Vector2f(10.f, 500.f), sf::Vector2f(760.f, 107.f)),
new StaticSprite("assets/platform.png", sf::Vector2f(1100.f, 100.f), sf::Vector2f(760.f, 107.f)),
new StaticSprite("assets/platform.png", sf::Vector2f(2100.f, 500.f), sf::Vector2f(760.f, 107.f))
},
enemies {
new Enemy(sf::Vector2f(0.f, -100.f), sf::Vector2f(215.f, 258.f)),
new Enemy(sf::Vector2f(2700.f, -100.f), sf::Vector2f(215.f, 258.f))
},
player_view { sf::FloatRect(0, 0, window.getSize().x, window.getSize().y) }
{
reset_positions(true);
}

/**
* Resets the positions of the player (and possibly enemies)
*
* @param hard_reset Whether to reset enemies as well
*/
void Game::reset_positions(bool hard_reset)
{
player.setDetails(sf::Vector2f(500.f, 0.f), player.size);
if (hard_reset)
{
enemies[0]->setPosition(sf::Vector2f(0.f, 250.f));
enemies[1]->setPosition(sf::Vector2f(2700.f, 250.f));
}
}

void Game::handleKeyPress(sf::Keyboard::Key key_code)
{
player.handleKeyPress(key_code);
}

void Game::handleKeyRelease(sf::Keyboard::Key key_code)
{
player.handleKeyRelease(key_code);
}

void Game::update(WindowStates& next_state)
{
if (player.health < 0)
{
// If the player has died, go to Menu screen (TODO: implement GameOver screen)
next_state = WindowStates::MENU;
return;
}

player.updatePosition(platforms);

for (Enemy* enemy : enemies)
{
enemy->updatePosition(platforms, player.pos);
player.handleCollide(*enemy);
}

player.updateHealth();

// Update information in frame_tracker text widget
frame_tracker.add_info("Vel", std::to_string(player.vel.x).substr(0, 4) + " | " + std::to_string(player.vel.y).substr(0, 4));
frame_tracker.update();
}

void Game::moveViewToPlayer()
{
player_view.setCenter(sf::Vector2f(player.pos.x, 300));
window.setView(player_view);
}

void Game::draw()
{
for (Enemy* enemy : enemies)
{
enemy->draw(window);

if (show_hitboxes)
window.draw(enemy->get_hitbox_outline());
}

for (StaticSprite* platform : platforms)
{
platform->draw(window);

if (show_hitboxes)
window.draw(platform->get_hitbox_outline());
}

player.draw(window);

if (show_hitboxes)
window.draw(player.get_hitbox_outline());

window.draw(frame_tracker.text);
window.draw(player.health_display);
}
Loading