// CMakeLists.txt cmake_minimum_required(VERSION 3.16) project(DangerousRacers VERSION 0.2.0 LANGUAGES CXX) # ========================= # C++ Standard # ========================= set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # ========================= # Build Type Default # ========================= if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") # ========================= # MSVC Runtime (IMPORTANT) # Use dynamic runtime (/MD, /MDd) # Reduces antivirus false positives # ========================= if (MSVC) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") endif() # ========================= # SFML Configuration # ========================= include(FetchContent) # Debug = static (easy local dev) # Release = shared (better distribution) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(BUILD_SHARED_LIBS OFF) set(SFML_STATIC_LIBRARIES ON) else() set(BUILD_SHARED_LIBS ON) set(SFML_STATIC_LIBRARIES OFF) endif() FetchContent_Declare( SFML GIT_REPOSITORY https://github.com/SFML/SFML.git GIT_TAG 2.6.1 GIT_SHALLOW ON ) FetchContent_MakeAvailable(SFML) # ========================= # Source Files # ========================= set(SOURCE_FILES src/main.cpp src/Scene.cpp src/AnimatedSprite.cpp src/SceneManager.cpp src/DrawableEntity.cpp src/Meter.cpp src/Player.cpp src/Obstacles/Obstacle.cpp src/Obstacles/ObstacleManager.cpp src/Label.cpp src/Button.cpp src/Scenes/MainMenu.cpp src/Scenes/studio_splashscreen.cpp src/Scenes/TutorialOver.cpp src/Scenes/GameOver.cpp src/Scenes/Levels/tutorial_level.cpp ) # ========================= # Executable # ========================= # WIN32 removes console window (optional) add_executable(DangerousRacers ${SOURCE_FILES}) # Only apply for Release builds on Windows if(WIN32) target_link_options(DangerousRacers PRIVATE $<$:/SUBSYSTEM:WINDOWS> $<$:/ENTRY:mainCRTStartup> ) endif() target_sources(DangerousRacers PRIVATE resources/version.rc) # ========================= # Includes # ========================= target_include_directories(DangerousRacers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ) # ========================= # Compile Definitions # ========================= if(SFML_STATIC_LIBRARIES) target_compile_definitions(DangerousRacers PRIVATE SFML_STATIC) endif() # ========================= # Linking # ========================= target_link_libraries(DangerousRacers PRIVATE sfml-graphics sfml-window sfml-system sfml-audio ) # ========================= # Compiler Flags # ========================= if(MSVC) target_compile_options(DangerousRacers PRIVATE $<$:/O2 /DNDEBUG> $<$:/Zi> ) endif() # ========================= # Copy Assets # ========================= add_custom_command(TARGET DangerousRacers POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets $/assets ) add_custom_command(TARGET DangerousRacers POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/resources/README.md $/README.md ) # ========================= # OpenAL DLL (needed for audio) # ========================= if (WIN32) if (CMAKE_SIZEOF_VOID_P EQUAL 8) set(OPENAL_DLL "${sfml_SOURCE_DIR}/extlibs/bin/x64/openal32.dll") else() set(OPENAL_DLL "${sfml_SOURCE_DIR}/extlibs/bin/x86/openal32.dll") endif() add_custom_command(TARGET DangerousRacers POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${OPENAL_DLL}" $ ) endif() # ========================= # Copy SFML DLLs (Release builds) # ========================= if(WIN32 AND BUILD_SHARED_LIBS) add_custom_command(TARGET DangerousRacers POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ $ $ $ ) endif() # ========================= # Install / Packaging (optional) # ========================= install(TARGETS DangerousRacers DESTINATION .) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ./assets) # ========================= # Output Directories # ========================= set_target_properties(DangerousRacers PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) // include/DangerousRacers/AnimatedSprite.h #pragma once #include "DrawableEntity.h" class AnimatedSprite : public DrawableEntity { private: int frames = 1; int fps = 1; int currentFrame = 1; float frameTimer = 0.f; public: AnimatedSprite(const float x, const float y, const std::string& texturePath, const int _frames = 1, const int _fps = 1); void render(sf::RenderWindow& window); void update(const float deltaTime); }; // include/DangerousRacers/Button.h #pragma once #include #include "Label.h" #include "SFML/Graphics/RectangleShape.hpp" #include "SFML/Graphics/RenderWindow.hpp" class Button { private: sf::RectangleShape buttonRect; Label label; bool selected = false; std::function onClick; public: Button(const std::string &labelString, sf::Vector2f position, const std::function& _onClick = []() {}); void setText(const std::string &labelString); void render(sf::RenderWindow& window) const; void update(const float deltaTime); void eventHandler(const sf::Event& event); void setOnClick(const std::function& _onClick) { onClick = _onClick; } void setSelected(const bool _selected) { if (_selected == selected) return; selected = _selected; label.setDarkMode(selected); if (selected) buttonRect.setFillColor({255, 255, 255}); else buttonRect.setFillColor({0, 0, 0, 64}); }; }; // include/DangerousRacers/DrawableEntity.h #pragma once #include #include "SFML/Graphics/RenderWindow.hpp" #include "SFML/Graphics/Sprite.hpp" #include "SFML/Graphics/Texture.hpp" class DrawableEntity { protected: sf::Texture texture; sf::Sprite sprite; sf::Vector2f position, startPosition, targetPosition; float moveDuration = 0.f, moveElapsed = 0.f; bool moving = false; void _setPosition(const float x, const float y); public: DrawableEntity(const float x, const float y, const std::string& texturePath); sf::Sprite& getSprite(); void loadTexture(const std::string& texturePath); void _setTexture(const sf::Texture& _texture); void moveTo(const float x, const float y, const float timeToMove = 0.f); bool isMoving() const; sf::Vector2f getPosition() const { return position; }; sf::Vector2i getRelativePosition() const; int getHeight() const; void render(sf::RenderWindow& window); void update(const float deltaTime); }; // include/DangerousRacers/GameManager.h #pragma once class GameManager { public: int score = 0; int bubblesPopped = 0; int getScore() const { return score; } void addScore(const int amount) { score += amount; } void resetScore() { score = 0; } int getBubblesPopped() const { return bubblesPopped; } void addBubblesPopped(const int amount) { bubblesPopped += amount; } void resetBubblesPopped() { bubblesPopped = 0; } void reset() { resetScore(); resetBubblesPopped(); } }; // include/DangerousRacers/Label.h #pragma once #include "SFML/Graphics/Text.hpp" #include "SFML/Graphics/RenderWindow.hpp" class Label { private: sf::Text labelText; bool center; bool outline; bool darkMode = false; public: Label(const std::string &labelString, sf::Vector2f position, const bool _center = false, const bool _outline = true); void setText(const std::string &labelString); void setPosition(const sf::Vector2f position); void setFillColor(const sf::Color color) { labelText.setFillColor(color); }; void setOutlineColor(const sf::Color color) { labelText.setOutlineColor(color); }; sf::FloatRect getBounds() const { return labelText.getGlobalBounds(); }; void render(sf::RenderWindow& window) const; void setDarkMode(const bool _darkMode) { darkMode = _darkMode; labelText.setFillColor(darkMode ? sf::Color::Black : sf::Color::White); if (outline) { labelText.setOutlineColor(darkMode ? sf::Color::White : sf::Color::Black); labelText.setOutlineThickness(2); } }; }; // include/DangerousRacers/Meter.h #pragma once #include "SFML/Graphics/RectangleShape.hpp" #include "SFML/Graphics/RenderWindow.hpp" class Meter { private: sf::RectangleShape outline; sf::RectangleShape fillBar; public: Meter(int x, int y, float w, float h, sf::Color color, float value = 0); void render(sf::RenderWindow& window) const; void setValue(float value); }; // include/DangerousRacers/Obstacles/Obstacle.h #pragma once #include "DangerousRacers/DrawableEntity.h" class Obstacle : public DrawableEntity { float& speed; public: Obstacle(const float x, const float y, float& speed); void render(sf::RenderWindow& window); void update(const float deltaTime); bool outOfBounds() { const auto height = this->getSprite().getGlobalBounds().height; return this->getPosition().y > 720.f + height / 2; } }; // include/DangerousRacers/Obstacles/ObstacleManager.h #pragma once #include "Obstacle.h" class ObstacleManager { float& sceneSpeed; std::vector> obstacles; public: ObstacleManager(float& sceneSpeed); void render(sf::RenderWindow& window); void update(const float deltaTime); void reset(); void spawnObstacle(const float x = 0, const float y = 0); bool obstacleCollides(const sf::FloatRect& collisionObject) const; }; // include/DangerousRacers/Player.h #pragma once #include "AnimatedSprite.h" class Player : public AnimatedSprite { public: Player(const float x, const float y); void render(sf::RenderWindow& window); void update(const float deltaTime); }; // include/DangerousRacers/Scene.h #pragma once #include #include "SceneManager.h" class Scene { public: SceneManager& sceneManager; sf::Clock clock; std::string name; Scene(SceneManager& _sceneManager, std::string _name) : sceneManager(_sceneManager), name(std::move(_name)) {}; virtual ~Scene() = default; virtual void start(); virtual void leave(); virtual void update(float deltaTime); virtual void render(sf::RenderWindow& window); virtual void eventHandler(sf::Event& event); }; // include/DangerousRacers/SceneManager.h #pragma once #include #include #include #include "GameManager.h" #include "SFML/Graphics/RenderWindow.hpp" class Scene; class SceneManager { private: sf::RenderWindow& gameWindow; GameManager& gameManager; std::unordered_map()>> sceneFactories; std::unique_ptr currentScene; std::string activeScene; public: SceneManager(sf::RenderWindow& gameWindow, GameManager& gameManager); void addScene(const std::string& name, const std::function()>& sceneFactory) { sceneFactories.emplace(name, sceneFactory); } void update(float deltaTime) const; void render(sf::RenderWindow& window) const; std::string getCurrentScene() const { return activeScene; }; GameManager& getGameManager() const { return gameManager; } sf::RenderWindow& getGameWindow() const { return gameWindow; } void changeScene(const std::string& sceneName); void eventHandler(sf::Event& event) const; void _quit(); }; // include/DangerousRacers/Scenes/GameOver.h #pragma once #include "DangerousRacers/Label.h" #include "DangerousRacers/Scene.h" #include "SFML/Graphics/RectangleShape.hpp" #include "SFML/Graphics/Texture.hpp" class GameOverScene : public Scene { private: bool wallTileLoaded = false; sf::Texture wallTileTexture; sf::RectangleShape wallTile; Label scoreLabel{"Score: 0\nBubbles Popped: 0", {576.f/2,768.f/2}, true, true}; public: GameOverScene(SceneManager& _sceneManager); void start(); void update(float deltaTime); void render(sf::RenderWindow& window); void eventHandler(sf::Event& event); }; // include/DangerousRacers/Scenes/Levels/tutorial_level.h #pragma once #include "DangerousRacers/Label.h" #include "DangerousRacers/Meter.h" #include "DangerousRacers/Player.h" #include "DangerousRacers/Scene.h" #include "DangerousRacers/Obstacles/ObstacleManager.h" #include "SFML/Graphics/Texture.hpp" #include "SFML/Graphics/Sprite.hpp" class DebugLevelScene : public Scene { sf::Texture backgroundTexture; sf::Sprite backgroundSprite; float speed = 200.f; float bgYOffset = -720.f; float distanceTraveled = 0.f; Player player{405.f/2, 720.f/2}; ObstacleManager obstacleManager{speed}; bool reverseTime = false; float timeInReverse = 0.f; Label scoreLabel{"KM: 0", {8,8}, false, true}; float fuel = 1.f, health = 1.f; Meter fuelMeter{ 8, 712, 16.f, 320.f, {175, 198, 151} }; Meter damageMeter{ 8, 384, 16.f, 320.f, {250,231,19} }; unsigned char currentTutorial = 0; const std::vector tutorialDistance { 1750.f, 3750.f, 5750.f }; std::vector