├── .gitignore ├── Makefile ├── README.md ├── assets ├── audio │ └── .gitkeep ├── font │ └── .gitkeep ├── img │ └── .gitkeep └── map │ └── .gitkeep ├── create-class.py ├── include ├── Camera.hpp ├── CameraFollower.hpp ├── Collider.hpp ├── Collision.hpp ├── Component.hpp ├── Game.hpp ├── GameData.hpp ├── GameObject.hpp ├── Helpers.hpp ├── InputManager.hpp ├── Music.hpp ├── Rect.hpp ├── Resources.hpp ├── SDL_include.h ├── Sound.hpp ├── Sprite.hpp ├── State.hpp ├── Text.hpp ├── TileMap.hpp ├── TileSet.hpp ├── Timer.hpp └── Vec2.hpp ├── obj └── .gitkeep ├── src ├── Camera.cpp ├── CameraFollower.cpp ├── Collider.cpp ├── Component.cpp ├── Game.cpp ├── GameData.cpp ├── GameObject.cpp ├── Helpers.cpp ├── InputManager.cpp ├── Music.cpp ├── Rect.cpp ├── Resources.cpp ├── Sound.cpp ├── Sprite.cpp ├── State.cpp ├── Text.cpp ├── TileMap.cpp ├── TileSet.cpp ├── Timer.cpp ├── Vec2.cpp └── main.cpp ├── templates ├── Component.cpp.template ├── Component.hpp.template ├── State.cpp.template └── State.hpp.template └── zip-files.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | 3 | *.o 4 | engine 5 | *.zip -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CPP = g++ 2 | CXXINCS = -I"include/" -lSDL2 -lSDL2_image -lSDL2_ttf -lSDL2_mixer 3 | CXXFLAGS = $(CXXINCS) -std=c++14 -O2 4 | SRCS = $(shell find src -name '*.cpp') 5 | OBJ = $(addprefix obj/,$(notdir $(SRCS:%.cpp=%.o))) 6 | BIN = engine 7 | RM = rm -f 8 | 9 | .PHONY: all all-before all-after clean clean-custom 10 | 11 | all: all-before $(BIN) all-after 12 | 13 | debug: CXXFLAGS = -DDEBUG $(CXXINCS) -std=c++14 -g3 -ggdb3 14 | debug: all 15 | 16 | clean: clean-custom 17 | ${RM} $(OBJ) $(BIN) 18 | 19 | $(BIN): $(OBJ) 20 | $(CPP) $(OBJ) -o $(BIN) $(CXXFLAGS) 21 | 22 | obj/%.o: src/%.cpp 23 | $(CPP) -c $< -o $@ $(CXXFLAGS) 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sdl-engine 2 | 3 | Current repository link: https://github.com/mikaelmello/sdl-game-engine 4 | 5 | Game engine developed in C++ using the SDL library, as part of small projects of the Introduction to Game Development course, from University of Brasília. 6 | 7 | There are seven projects and each one will continue from the previous one, this repository will contain all code from the seven projects, each one in their respective branches, from `trabalho-1` to `trabalho-7` 8 | 9 | The `master` branch will contain the raw engine with up-to-date improvements made to the final game of the class. 10 | 11 | ## Running 12 | 13 | If you're here to evaluate one of the projects, change your branch to the respective project number, from `trabalho-1` to `trabalho-7`, there you will see a README with running instructions. 14 | -------------------------------------------------------------------------------- /assets/audio/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelmello/sdl-game-engine/0301f353b90da83c040fe2ca41950e94d5545ebb/assets/audio/.gitkeep -------------------------------------------------------------------------------- /assets/font/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelmello/sdl-game-engine/0301f353b90da83c040fe2ca41950e94d5545ebb/assets/font/.gitkeep -------------------------------------------------------------------------------- /assets/img/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelmello/sdl-game-engine/0301f353b90da83c040fe2ca41950e94d5545ebb/assets/img/.gitkeep -------------------------------------------------------------------------------- /assets/map/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelmello/sdl-game-engine/0301f353b90da83c040fe2ca41950e94d5545ebb/assets/map/.gitkeep -------------------------------------------------------------------------------- /create-class.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import argparse 5 | import re 6 | 7 | parser = argparse.ArgumentParser('create-class.py') 8 | parser.add_argument('type', choices=['state', 'component']) 9 | parser.add_argument('name', help='Class name') 10 | args = parser.parse_args() 11 | 12 | class_type = args.type.capitalize() 13 | class_name = args.name.capitalize() 14 | 15 | if class_type == 'State': 16 | class_name = class_name + 'State' 17 | 18 | with open('templates/' + class_type + '.hpp.template', 'r') as file: 19 | data = file.read() 20 | data = data.replace('', class_name) 21 | data = data.replace('', class_name.upper()) 22 | with open('include/' + class_name + '.hpp', 'w') as header_file: 23 | header_file.write(data) 24 | 25 | with open('templates/' + class_type + '.cpp.template', 'r') as file: 26 | data = file.read() 27 | data = data.replace('', class_name) 28 | data = data.replace('', class_name.upper()) 29 | with open('src/' + class_name + '.cpp', 'w') as header_file: 30 | header_file.write(data) 31 | -------------------------------------------------------------------------------- /include/Camera.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CAMERA_H 2 | #define CAMERA_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "Vec2.hpp" 9 | #include "GameObject.hpp" 10 | 11 | class Camera { 12 | public: 13 | static void Follow(GameObject* newFocus); 14 | 15 | static void Unfollow(); 16 | 17 | static void Update(float dt); 18 | 19 | static Vec2 pos; 20 | 21 | static Vec2 speed; 22 | private: 23 | static GameObject* focus; 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /include/CameraFollower.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CAMERAFOLLOWOER_H 2 | #define CAMERAFOLLOWOER_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "Component.hpp" 9 | #include "GameObject.hpp" 10 | #include "GameData.hpp" 11 | #include 12 | 13 | class CameraFollower : public Component { 14 | public: 15 | CameraFollower(GameObject& associated); 16 | 17 | void Update(float dt) override; 18 | 19 | bool Is(GameData::Types type) const override; 20 | 21 | void Render() override; 22 | 23 | const GameData::Types Type = GameData::Types::CameraFollower; 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /include/Collider.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COLLIDER_H 2 | #define COLLIDER_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "Component.hpp" 9 | #include "GameObject.hpp" 10 | #include "GameData.hpp" 11 | #include "Vec2.hpp" 12 | #include "Rect.hpp" 13 | #include 14 | #include 15 | 16 | class Collider : public Component { 17 | public: 18 | Collider(GameObject& associated, Vec2 scale = {1, 1}, Vec2 offset = {0, 0}); 19 | 20 | void Update(float dt) override; 21 | 22 | bool Is(GameData::Types type) const override; 23 | 24 | void Render() override; 25 | 26 | void SetScale(Vec2 scale); 27 | 28 | void SetOffset(Vec2 offset); 29 | 30 | Rect box; 31 | 32 | const GameData::Types Type = GameData::Types::Collider; 33 | private: 34 | Vec2 scale; 35 | Vec2 offset; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/Collision.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COLLISION_H 2 | #define COLLISION_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "Rect.hpp" 9 | #include "Vec2.hpp" 10 | #include 11 | #include 12 | 13 | class Collision { 14 | 15 | public: 16 | // Observação: IsColliding espera ângulos em radianos! 17 | // Para usar graus, forneça a sua própria implementação de Rotate, 18 | // ou transforme os ângulos no corpo de IsColliding. 19 | static inline bool IsColliding(Rect& a, Rect& b, float angleOfA, float angleOfB) { 20 | Vec2 A[] = { Vec2( a.x, a.y + a.h ), 21 | Vec2( a.x + a.w, a.y + a.h ), 22 | Vec2( a.x + a.w, a.y ), 23 | Vec2( a.x, a.y ) 24 | }; 25 | Vec2 B[] = { Vec2( b.x, b.y + b.h ), 26 | Vec2( b.x + b.w, b.y + b.h ), 27 | Vec2( b.x + b.w, b.y ), 28 | Vec2( b.x, b.y ) 29 | }; 30 | 31 | for (auto& v : A) { 32 | v = (v - a.Center()).GetRotated(angleOfA) + a.Center(); 33 | } 34 | 35 | for (auto& v : B) { 36 | v = (v - b.Center()).GetRotated(angleOfB) + b.Center(); 37 | } 38 | 39 | Vec2 axes[] = { (A[0] - A[1]).GetNormalized(), (A[1] - A[2]).GetNormalized(), (B[0] - B[1]).GetNormalized(), (B[1] - B[2].GetNormalized()) }; 40 | 41 | for (auto& axis : axes) { 42 | float P[4]; 43 | 44 | for (int i = 0; i < 4; ++i) P[i] = Dot(A[i], axis); 45 | 46 | float minA = *std::min_element(P, P + 4); 47 | float maxA = *std::max_element(P, P + 4); 48 | 49 | for (int i = 0; i < 4; ++i) P[i] = Dot(B[i], axis); 50 | 51 | float minB = *std::min_element(P, P + 4); 52 | float maxB = *std::max_element(P, P + 4); 53 | 54 | if (maxA < minB || minA > maxB) 55 | return false; 56 | } 57 | 58 | return true; 59 | } 60 | 61 | private: 62 | static inline float Dot(const Vec2& a, const Vec2& b) { 63 | return a.x * b.x + a.y * b.y; 64 | } 65 | }; 66 | 67 | #endif -------------------------------------------------------------------------------- /include/Component.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMPONENT_H 2 | #define COMPONENT_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "GameData.hpp" 9 | #include 10 | 11 | class GameObject; 12 | 13 | class Component { 14 | public: 15 | Component(GameObject& associated); 16 | 17 | virtual ~Component(); 18 | 19 | virtual void NotifyCollision(GameObject& other); 20 | 21 | virtual void Start(); 22 | 23 | virtual void Update(float dt) = 0; 24 | 25 | virtual void Render() = 0; 26 | 27 | virtual bool Is(GameData::Types type) const = 0; 28 | protected: 29 | GameObject& associated; 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /include/Game.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GAME_H 2 | #define GAME_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #define INCLUDE_SDL 9 | #include "SDL_include.h" 10 | #include "Vec2.hpp" 11 | #include "State.hpp" 12 | #include 13 | #include 14 | #include 15 | 16 | class Game { 17 | public: 18 | ~Game(); 19 | 20 | void Run(); 21 | 22 | SDL_Renderer* GetRenderer(); 23 | 24 | State& GetCurrentState(); 25 | 26 | void Push(State* state); 27 | 28 | static Game& GetInstance(); 29 | 30 | float GetDeltaTime() const; 31 | 32 | int GetWidth() const; 33 | 34 | int GetHeight() const; 35 | private: 36 | Game(const std::string& title, int width, int height); 37 | 38 | void CalculateDeltaTime(); 39 | 40 | static Game* instance; 41 | 42 | SDL_Window* window; 43 | 44 | SDL_Renderer* renderer; 45 | 46 | std::stack> stateStack; 47 | 48 | State* storedState; 49 | 50 | int frameStart; 51 | 52 | float dt; 53 | 54 | int width; 55 | 56 | int height; 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/GameData.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GAMEDATA_H 2 | #define GAMEDATA_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | class GameData { 9 | public: 10 | enum Types { 11 | CameraFollower, 12 | Collider, 13 | Sound, 14 | Sprite, 15 | Text, 16 | TileMap, 17 | }; 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/GameObject.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GAMEOBJECT_H 2 | #define GAMEOBJECT_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "Component.hpp" 9 | #include "Rect.hpp" 10 | #include 11 | #include 12 | #include 13 | 14 | class GameObject { 15 | public: 16 | GameObject(double priority = 1000); 17 | 18 | ~GameObject(); 19 | 20 | void Update(float dt); 21 | 22 | void Render(); 23 | 24 | void Start(); 25 | 26 | bool IsDead() const; 27 | 28 | void RequestDelete(); 29 | 30 | void NotifyCollision(GameObject& other); 31 | 32 | void AddComponent(Component* cpt); 33 | 34 | void RemoveComponent(Component* cpt); 35 | 36 | std::weak_ptr GetComponent(GameData::Types type); 37 | 38 | Rect box; 39 | 40 | double angleDeg; 41 | 42 | double priority; 43 | private: 44 | std::vector> components; 45 | bool isDead; 46 | bool started; 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /include/Helpers.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HELPERS_H 2 | #define HELPERS_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include 9 | #include 10 | 11 | namespace Helpers { 12 | 13 | // Divide uma string em um vetor de strings de acordo um caractere delimitador 14 | // Criado por Evan Teran 15 | // Disponível em: https://stackoverflow.com/questions/236129/most-elegant-way-to-split-a-string 16 | template 17 | void split(const std::string &s, char delim, Out result); 18 | 19 | // Divide uma string em um vetor de strings de acordo um caractere delimitador 20 | // Criado por Evan Teran 21 | // Disponível em: https://stackoverflow.com/questions/236129/most-elegant-way-to-split-a-string 22 | std::vector split(const std::string &s, char delim); 23 | 24 | std::vector split(const std::string& text, const std::string& delims); 25 | 26 | bool is_whitespace(const std::string& s); 27 | 28 | float rad_to_deg(float rad); 29 | 30 | float deg_to_rad(float deg); 31 | 32 | } // end namespace Helpers 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /include/InputManager.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INPUTMANAGER_H 2 | #define INPUTMANAGER_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #define INCLUDE_SDL 9 | #include "SDL_include.h" 10 | #include 11 | #include 12 | 13 | #define LEFT_ARROW_KEY SDLK_LEFT 14 | #define RIGHT_ARROW_KEY SDLK_RIGHT 15 | #define UP_ARROW_KEY SDLK_UP 16 | #define DOWN_ARROW_KEY SDLK_DOWN 17 | #define SPACE_BAR_KEY SDLK_SPACE 18 | #define ESCAPE_KEY SDLK_ESCAPE 19 | #define LEFT_MOUSE_BUTTON SDL_BUTTON_LEFT 20 | #define RIGHT_MOUSE_BUTTON SDL_BUTTON_RIGHT 21 | #define W_KEY SDLK_w 22 | #define A_KEY SDLK_a 23 | #define S_KEY SDLK_s 24 | #define D_KEY SDLK_d 25 | 26 | class InputManager { 27 | public: 28 | void Update(); 29 | 30 | bool KeyPress(int key); 31 | bool KeyRelease(int key); 32 | bool IsKeyDown(int key); 33 | 34 | bool MousePress(int button) const; 35 | bool MouseRelease(int button) const; 36 | bool IsMouseDown(int button) const; 37 | 38 | int GetMouseX() const; 39 | int GetMouseY() const; 40 | 41 | bool QuitRequested() const; 42 | 43 | static InputManager& GetInstance(); 44 | private: 45 | 46 | static InputManager instance; 47 | 48 | InputManager(); 49 | 50 | ~InputManager(); 51 | 52 | bool mouseState[6]; 53 | int mouseUpdate[6]; 54 | 55 | std::unordered_map keyState; 56 | std::unordered_map keyUpdate; 57 | 58 | bool quitRequested; 59 | int updateCounter; 60 | 61 | int mouseX; 62 | int mouseY; 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /include/Music.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MUSIC_H 2 | #define MUSIC_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #define INCLUDE_SDL_MIXER 9 | #include "SDL_include.h" 10 | #include 11 | #include 12 | 13 | class Music { 14 | public: 15 | Music(); 16 | 17 | Music(const std::string& file); 18 | 19 | ~Music(); 20 | 21 | void Play(int times = -1); 22 | void Stop(int msToStop = 1500); 23 | void Open(const std::string& file); 24 | bool IsOpen() const; 25 | private: 26 | std::shared_ptr music; 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/Rect.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RECT_H 2 | #define RECT_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "Vec2.hpp" 9 | #include 10 | 11 | class Rect { 12 | public: 13 | Rect(); 14 | 15 | Rect(float x, float y, float w, float h); 16 | 17 | Vec2 Center() const; 18 | 19 | Rect GetCentered(Vec2 pos) const; 20 | Rect GetCentered(float x, float y) const; 21 | 22 | float Distance(const Rect& rect) const; 23 | 24 | bool Contains(const Vec2& point) const; 25 | 26 | Rect& operator+=(const Vec2& rhs); 27 | Rect& operator=(const Rect& rhs); 28 | 29 | friend Rect operator+(Rect lhs, const Vec2& rhs) { 30 | lhs += rhs; 31 | return lhs; 32 | } 33 | 34 | const std::string ToString() const { 35 | return std::string("x:") + std::to_string(x) + std::string(" y:") + std::to_string(y) 36 | + std::string(" w:") + std::to_string(w) + std::string(" h:") + std::to_string(h) + std::string(" center: ") + Center().ToString(); 37 | } 38 | 39 | float x; 40 | float y; 41 | float w; 42 | float h; 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /include/Resources.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RESOURCES_H 2 | #define RESOURCES_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #define INCLUDE_SDL_MIXER 9 | #define INCLUDE_SDL_IMAGE 10 | #define INCLUDE_SDL_TTF 11 | #include "SDL_include.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | class Resources { 18 | public: 19 | static std::shared_ptr GetImage(const std::string& file); 20 | static std::shared_ptr GetSound(const std::string& file); 21 | static std::shared_ptr GetMusic(const std::string& file); 22 | static std::shared_ptr GetFont(const std::string& file, int fontSize); 23 | static void ClearImages(); 24 | static void ClearSounds(); 25 | static void ClearMusics(); 26 | static void ClearFonts(); 27 | private: 28 | static std::unordered_map> imageTable; 29 | static std::unordered_map> musicTable; 30 | static std::unordered_map> soundTable; 31 | static std::unordered_map> fontTable; 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /include/SDL_include.h: -------------------------------------------------------------------------------- 1 | 2 | /* - Como usar esse arquivo: 3 | * 4 | * Onde quiser adicionar, por exemplo, SDL_image e SDL_mixer em um arquivo, faça o seguinte e 5 | * ele incluirá elas automaticamente e de forma multiplataforma (se usar o makefile tbm fornecido). 6 | * 7 | * #define INCLUDE_SDL_IMAGE 8 | * #define INCLUDE_SDL_MIXER 9 | * #include "SDL_include.h" 10 | * 11 | */ 12 | 13 | 14 | /************************************************ 15 | * SDL.h * 16 | *************************************************/ 17 | #ifdef INCLUDE_SDL 18 | #ifdef _WIN32 19 | #include 20 | #elif __APPLE__ 21 | #include "TargetConditionals.h" 22 | #include 23 | #elif __linux__ 24 | #include 25 | #else 26 | #error "Unknown compiler" 27 | #endif 28 | #undef INCLUDE_SDL 29 | #endif // INCLUDE_SDL 30 | 31 | 32 | /************************************************ 33 | * SDL_image.h * 34 | *************************************************/ 35 | #ifdef INCLUDE_SDL_IMAGE 36 | #ifdef _WIN32 37 | #include 38 | #elif __APPLE__ 39 | #include "TargetConditionals.h" 40 | #include 41 | #elif __linux__ 42 | #include 43 | #else 44 | #error "Unknown compiler" 45 | #endif 46 | #undef INCLUDE_SDL_IMAGE 47 | #endif // INCLUDE_SDL_IMAGE 48 | 49 | 50 | /************************************************ 51 | * SDL_mixer.h * 52 | *************************************************/ 53 | #ifdef INCLUDE_SDL_MIXER 54 | #ifdef _WIN32 55 | #include 56 | #elif __APPLE__ 57 | #include "TargetConditionals.h" 58 | #include 59 | #elif __linux__ 60 | #include 61 | #else 62 | #error "Unknown compiler" 63 | #endif 64 | #undef INCLUDE_SDL_MIXER 65 | #endif // INCLUDE_SDL_MIXER 66 | 67 | 68 | /************************************************ 69 | * SDL_ttf.h * 70 | *************************************************/ 71 | #ifdef INCLUDE_SDL_TTF 72 | #ifdef _WIN32 73 | #include 74 | #elif __APPLE__ 75 | #include "TargetConditionals.h" 76 | #include 77 | #elif __linux__ 78 | #include 79 | #else 80 | #error "Unknown compiler" 81 | #endif 82 | #undef INCLUDE_SDL_TTF 83 | #endif // INCLUDE_SDL_TTF 84 | 85 | 86 | /************************************************ 87 | * SDL_net.h * 88 | *************************************************/ 89 | #ifdef INCLUDE_SDL_NET 90 | #ifdef _WIN32 91 | #include 92 | #elif __APPLE__ 93 | #include "TargetConditionals.h" 94 | #include 95 | #elif __linux__ 96 | #include 97 | #else 98 | #error "Unknown compiler" 99 | #endif 100 | #undef INCLUDE_SDL_NET 101 | #endif // INCLUDE_SDL_NET 102 | -------------------------------------------------------------------------------- /include/Sound.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_H 2 | #define SOUND_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #define INCLUDE_SDL_MIXER 9 | #include "SDL_include.h" 10 | #include "Component.hpp" 11 | #include "GameData.hpp" 12 | #include "GameObject.hpp" 13 | #include 14 | 15 | class Sound : public Component { 16 | public: 17 | Sound(GameObject& associated); 18 | 19 | Sound(GameObject& associated, const std::string& file); 20 | 21 | ~Sound(); 22 | 23 | void Open(const std::string& file); 24 | 25 | void Play(int times = 1); 26 | 27 | void Stop(); 28 | 29 | void Update(float dt) override; 30 | 31 | bool Is(GameData::Types type) const override; 32 | 33 | void Render() override; 34 | 35 | bool IsOpen() const; 36 | 37 | const GameData::Types Type = GameData::Types::Sound; 38 | private: 39 | std::shared_ptr chunk; 40 | int channel; 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /include/Sprite.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SPRITE_H 2 | #define SPRITE_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #define INCLUDE_SDL 9 | #include "SDL_include.h" 10 | #include "Component.hpp" 11 | #include "Timer.hpp" 12 | #include "GameObject.hpp" 13 | #include "GameData.hpp" 14 | #include 15 | 16 | class Sprite : public Component { 17 | public: 18 | Sprite(GameObject& associated, int frameCount = 1, float frameTime = 1, float secondsToSelfDestruct = 0); 19 | 20 | Sprite(GameObject& associated, const std::string& file, int frameCount = 1, float frameTime = 1, float secondsToSelfDestruct = 0); 21 | 22 | ~Sprite(); 23 | 24 | void Open(const std::string& file); 25 | 26 | void SetClip(int x, int y, int w, int h); 27 | 28 | void Update(float dt) override; 29 | 30 | bool Is(GameData::Types type) const override; 31 | 32 | void Render(int x, int y); 33 | 34 | void Render() override; 35 | 36 | int GetWidth() const; 37 | 38 | int GetHeight() const; 39 | 40 | bool IsOpen() const; 41 | 42 | void SetScaleX(float scaleX, float scaleY); 43 | 44 | void SetFrame(int frame); 45 | 46 | void SetFrameCount(int frameCount); 47 | 48 | void SetFrameTime(float frameTime); 49 | 50 | Vec2 GetScale(); 51 | 52 | const GameData::Types Type = GameData::Types::Sprite; 53 | private: 54 | std::shared_ptr texture; 55 | int width; 56 | int height; 57 | int frameCount; 58 | int currentFrame; 59 | float frameTime; 60 | float secondsToSelfDestruct; 61 | Timer currentFrameCount; 62 | Timer selfDestructCount; 63 | SDL_Rect clipRect; 64 | Vec2 scale; 65 | }; 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /include/State.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STATE_H 2 | #define STATE_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "GameObject.hpp" 9 | #include "TileSet.hpp" 10 | #include "Sprite.hpp" 11 | #include "Music.hpp" 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | class State { 18 | public: 19 | State(); 20 | 21 | virtual ~State(); 22 | 23 | virtual void LoadAssets() = 0; 24 | 25 | virtual void Update(float dt) = 0; 26 | 27 | virtual void Render() = 0; 28 | 29 | virtual void Start() = 0; 30 | 31 | virtual void Pause() = 0; 32 | 33 | virtual void Resume() = 0; 34 | 35 | virtual std::weak_ptr AddObject(GameObject* go); 36 | 37 | virtual std::weak_ptr GetObjectPtr(GameObject* go); 38 | 39 | bool QuitRequested() const; 40 | 41 | bool PopRequested() const; 42 | protected: 43 | void StartArray(); 44 | 45 | virtual void UpdateArray(float dt); 46 | 47 | virtual void RenderArray(); 48 | 49 | bool popRequested; 50 | bool quitRequested; 51 | bool started; 52 | 53 | struct GameObjectComp { 54 | bool operator()(const std::shared_ptr& lhs, const std::shared_ptr& rhs) const { 55 | return lhs->priority < rhs->priority; 56 | } 57 | }; 58 | 59 | std::multiset, GameObjectComp> objects; 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /include/Text.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEXT_H 2 | #define TEXT_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #define INCLUDE_SDL_TTF 9 | #define INCLUDE_SDL_IMAGE 10 | #include "SDL_include.h" 11 | #include "GameObject.hpp" 12 | #include "Timer.hpp" 13 | #include "Component.hpp" 14 | #include "GameData.hpp" 15 | #include 16 | #include 17 | 18 | class Text : public Component { 19 | public: 20 | enum TextStyle { SOLID, SHADED, BLENDED }; 21 | 22 | Text(GameObject& associated, const std::string& fontFile, int fontSize, TextStyle style, const std::string& text, SDL_Color color); 23 | 24 | ~Text(); 25 | 26 | void Update(float dt) override; 27 | 28 | void Render() override; 29 | 30 | bool Is(GameData::Types type) const override; 31 | 32 | void SetText(const std::string& text); 33 | 34 | void SetColor(SDL_Color color); 35 | 36 | void SetStyle(TextStyle style); 37 | 38 | void SetFontFile(const std::string& fontFile); 39 | 40 | void SetFontSize(int fontSize); 41 | 42 | void SetBlink(bool blink, float blinkPeriod); 43 | 44 | const GameData::Types Type = GameData::Types::Text; 45 | private: 46 | void RemakeTexture(); 47 | 48 | std::shared_ptr font; 49 | std::shared_ptr texture; 50 | 51 | std::string text; 52 | TextStyle style; 53 | std::string fontFile; 54 | int fontSize; 55 | SDL_Color color; 56 | bool blink; 57 | bool display; 58 | float blinkPeriod; 59 | Timer blinkTimer; 60 | 61 | }; 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /include/TileMap.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TILEMAP_H 2 | #define TILEMAP_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "Component.hpp" 9 | #include "GameObject.hpp" 10 | #include "GameData.hpp" 11 | #include "TileSet.hpp" 12 | #include "Vec2.hpp" 13 | #include 14 | #include 15 | 16 | class TileMap : public Component { 17 | public: 18 | TileMap(GameObject& associated, const std::string& file, TileSet* tileSet); 19 | 20 | void Load(const std::string& file); 21 | 22 | void SetTileSet(TileSet* tileSet); 23 | 24 | int& At(int x, int y, int z = 0); 25 | 26 | void Render(); 27 | 28 | void RenderLayer(int layer, int cameraX = 0, int cameraY = 0); 29 | 30 | bool Is(GameData::Types type) const override; 31 | 32 | void Update(float dt) override; 33 | 34 | int GetWidth(); 35 | 36 | int GetHeight(); 37 | 38 | int GetDepth(); 39 | 40 | void SetParallax(int layer, float xFactor, float yFactor); 41 | 42 | const GameData::Types Type = GameData::Types::TileMap; 43 | private: 44 | std::vector tileMatrix; 45 | std::vector layerParallax; 46 | TileSet* tileSet; 47 | int mapWidth; 48 | int mapHeight; 49 | int mapDepth; 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /include/TileSet.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TILESET_H 2 | #define TILESET_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "Sprite.hpp" 9 | #include "GameObject.hpp" 10 | #include 11 | 12 | class TileSet { 13 | public: 14 | TileSet(int tileWidth, int tileHeight, const std::string& file); 15 | 16 | void RenderTile(unsigned int index, float x, float y); 17 | 18 | int GetTileWidth(); 19 | 20 | int GetTileHeight(); 21 | 22 | private: 23 | GameObject gameObject; 24 | Sprite tileSet; 25 | 26 | int rows; 27 | int columns; 28 | 29 | int tileWidth; 30 | int tileHeight; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/Timer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | class Timer { 9 | public: 10 | Timer(); 11 | 12 | void Update(float dt); 13 | 14 | void Restart(); 15 | 16 | float Get(); 17 | private: 18 | float time; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /include/Vec2.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VEC2_H 2 | #define VEC2_H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include 9 | 10 | class Vec2 { 11 | public: 12 | Vec2(); 13 | 14 | Vec2(float x, float y); 15 | 16 | Vec2 GetNormalized() const; 17 | 18 | Vec2 GetRotated(const float& d) const; 19 | 20 | float Magnitude() const; 21 | 22 | float Distance(const Vec2& v) const; 23 | 24 | float XAxisInclination() const; 25 | 26 | Vec2& operator+=(const Vec2& rhs); 27 | Vec2& operator-=(const Vec2& rhs); 28 | Vec2& operator*=(const float& rhs); 29 | Vec2& operator=(const Vec2& rhs); 30 | 31 | friend Vec2 operator+(Vec2 lhs, const Vec2& rhs) { 32 | lhs += rhs; 33 | return lhs; 34 | } 35 | 36 | friend Vec2 operator-(Vec2 lhs, const Vec2& rhs) { 37 | lhs -= rhs; 38 | return lhs; 39 | } 40 | 41 | friend Vec2 operator*(Vec2 lhs, const float& rhs) { 42 | lhs *= rhs; 43 | return lhs; 44 | } 45 | 46 | friend Vec2 operator*(const float& lhs, Vec2 rhs) { 47 | rhs *= lhs; 48 | return rhs; 49 | } 50 | 51 | std::string ToString() const { 52 | return std::string("x:") + std::to_string(x) + std::string(" y:") + std::to_string(y); 53 | } 54 | 55 | static float XAxisLineInclination(const Vec2& v1, const Vec2& v2); 56 | 57 | float x; 58 | float y; 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /obj/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelmello/sdl-game-engine/0301f353b90da83c040fe2ca41950e94d5545ebb/obj/.gitkeep -------------------------------------------------------------------------------- /src/Camera.cpp: -------------------------------------------------------------------------------- 1 | #include "Camera.hpp" 2 | #include "Vec2.hpp" 3 | #include "GameObject.hpp" 4 | #include "InputManager.hpp" 5 | #include "Game.hpp" 6 | 7 | GameObject* Camera::focus; 8 | Vec2 Camera::pos; 9 | Vec2 Camera::speed = {300, 0}; 10 | 11 | void Camera::Follow(GameObject* newFocus) { 12 | focus = newFocus; 13 | } 14 | 15 | void Camera::Unfollow() { 16 | focus = nullptr; 17 | Vec2 x(100, 0); 18 | } 19 | 20 | void Camera::Update(float dt) { 21 | Game& game = Game::GetInstance(); 22 | if (focus != nullptr) { 23 | pos = focus->box.Center() - Vec2(game.GetWidth() / 2, game.GetHeight() / 2); 24 | return; 25 | } 26 | 27 | InputManager& im = InputManager::GetInstance(); 28 | bool right = im.IsKeyDown(RIGHT_ARROW_KEY); 29 | bool down = im.IsKeyDown(DOWN_ARROW_KEY); 30 | bool left = im.IsKeyDown(LEFT_ARROW_KEY); 31 | bool up = im.IsKeyDown(UP_ARROW_KEY); 32 | 33 | Vec2 curSpeed = speed * dt; 34 | 35 | if (right) { 36 | pos.x += curSpeed.x; 37 | } 38 | if (left) { 39 | pos.x -= curSpeed.x; 40 | } 41 | if (up) { 42 | pos.y -= curSpeed.x; 43 | } 44 | if (down) { 45 | pos.y += curSpeed.x; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/CameraFollower.cpp: -------------------------------------------------------------------------------- 1 | #include "CameraFollower.hpp" 2 | #include "Camera.hpp" 3 | #include "GameData.hpp" 4 | #include 5 | 6 | CameraFollower::CameraFollower(GameObject& associated) : Component(associated) {} 7 | 8 | void CameraFollower::Update(float dt) { 9 | associated.box.x = Camera::pos.x; 10 | associated.box.y = Camera::pos.y; 11 | } 12 | 13 | void CameraFollower::Render() {} 14 | 15 | bool CameraFollower::Is(GameData::Types type) const { 16 | return type == this->Type; 17 | } 18 | -------------------------------------------------------------------------------- /src/Collider.cpp: -------------------------------------------------------------------------------- 1 | 2 | #ifdef DEBUG 3 | #include "Camera.hpp" 4 | #include "Game.hpp" 5 | #include 6 | #include 7 | #include 8 | #endif // DEBUG 9 | #include "GameData.hpp" 10 | #include "Collider.hpp" 11 | #include "Helpers.hpp" 12 | #include "GameObject.hpp" 13 | #include "Vec2.hpp" 14 | #include 15 | 16 | Collider::Collider(GameObject& associated, Vec2 scale, Vec2 offset) : Component(associated), scale(scale), offset(offset) { 17 | } 18 | 19 | void Collider::Update(float dt) { 20 | box = associated.box; 21 | box.w *= scale.x; 22 | box.h *= scale.y; 23 | box = box.GetCentered(associated.box.Center()); 24 | 25 | box += offset.GetRotated(Helpers::deg_to_rad(associated.angleDeg)); 26 | } 27 | 28 | void Collider::Render() { 29 | #ifdef DEBUG 30 | Vec2 center = box.Center(); 31 | SDL_Point points[5]; 32 | SDL_Point points2[2]; 33 | 34 | 35 | Vec2 point = (Vec2(box.x, box.y) - center).GetRotated(Helpers::deg_to_rad(associated.angleDeg)) 36 | + center - Camera::pos; 37 | points[0] = {(int)point.x, (int)point.y}; 38 | points[4] = {(int)point.x, (int)point.y}; 39 | 40 | point = (Vec2(box.x + box.w, box.y) - center).GetRotated(Helpers::deg_to_rad(associated.angleDeg)) 41 | + center - Camera::pos; 42 | points[1] = {(int)point.x, (int)point.y}; 43 | 44 | point = (Vec2(box.x + box.w, box.y + box.h) - center).GetRotated(Helpers::deg_to_rad(associated.angleDeg)) 45 | + center - Camera::pos; 46 | points[2] = {(int)point.x, (int)point.y}; 47 | 48 | point = (Vec2(box.x, box.y + box.h) - center).GetRotated(Helpers::deg_to_rad(associated.angleDeg)) 49 | + center - Camera::pos; 50 | points[3] = {(int)point.x, (int)point.y}; 51 | 52 | SDL_SetRenderDrawColor(Game::GetInstance().GetRenderer(), 255, 0, 0, SDL_ALPHA_OPAQUE); 53 | SDL_RenderDrawLines(Game::GetInstance().GetRenderer(), points, 5); 54 | #endif // DEBUG 55 | } 56 | 57 | bool Collider::Is(GameData::Types type) const { 58 | return type == this->Type; 59 | } 60 | 61 | void Collider::SetOffset(Vec2 offset) { 62 | offset = offset; 63 | } 64 | 65 | void Collider::SetScale(Vec2 scale) { 66 | scale = scale; 67 | } 68 | -------------------------------------------------------------------------------- /src/Component.cpp: -------------------------------------------------------------------------------- 1 | #include "Component.hpp" 2 | #include "GameObject.hpp" 3 | 4 | Component::Component(GameObject& associated) : associated(associated) {} 5 | 6 | Component::~Component() {} 7 | 8 | void Component::Start() {} 9 | 10 | void Component::NotifyCollision(GameObject& other) {} -------------------------------------------------------------------------------- /src/Game.cpp: -------------------------------------------------------------------------------- 1 | #define INCLUDE_SDL 2 | #define INCLUDE_SDL_IMAGE 3 | #define INCLUDE_SDL_MIXER 4 | #define INCLUDE_SDL_TTF 5 | #include "SDL_include.h" 6 | #include "Game.hpp" 7 | #include "InputManager.hpp" 8 | #include "Resources.hpp" 9 | #include 10 | #include 11 | 12 | Game* Game::instance = nullptr; 13 | 14 | Game::Game(const std::string& title, int width, int height) : frameStart(0), dt(0), width(width), height(height) { 15 | if (instance == nullptr) { 16 | instance = this; 17 | } else { 18 | throw std::logic_error("Game constructor called when an instance is already created"); 19 | } 20 | int return_code; 21 | 22 | return_code = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER); 23 | if (return_code != 0) { 24 | throw std::runtime_error("Failed to init the SDL library"); 25 | } 26 | 27 | int img_flags = IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF; 28 | return_code = IMG_Init(img_flags); 29 | if (return_code != img_flags) { 30 | throw std::runtime_error("Failed to init the SDL_Image library"); 31 | } 32 | 33 | int mix_flags = MIX_INIT_FLAC | MIX_INIT_MP3 | MIX_INIT_OGG; 34 | return_code = Mix_Init(mix_flags); 35 | if (return_code != mix_flags) { 36 | throw std::runtime_error("Failed to init the SDL_Mixer library"); 37 | } 38 | 39 | return_code = TTF_Init(); 40 | if (return_code != 0) { 41 | throw std::runtime_error("Failed to init the SDL_TTF library"); 42 | } 43 | 44 | return_code = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 1024); 45 | if (return_code != 0) { 46 | throw std::runtime_error("Failed to open the mixer with default parameters"); 47 | } 48 | 49 | window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, 0); 50 | if (window == nullptr) { 51 | throw std::runtime_error("Failed to create a window"); 52 | } 53 | 54 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); 55 | if (renderer == nullptr) { 56 | throw std::runtime_error("Failed to create a 2D rendering context for the window"); 57 | } 58 | 59 | storedState = nullptr; 60 | } 61 | 62 | Game::~Game() { 63 | if (storedState != nullptr) { 64 | delete storedState; 65 | } 66 | 67 | while (!stateStack.empty()) { 68 | stateStack.pop(); 69 | } 70 | 71 | Resources::ClearImages(); 72 | Resources::ClearMusics(); 73 | Resources::ClearSounds(); 74 | 75 | SDL_DestroyRenderer(renderer); 76 | SDL_DestroyWindow(window); 77 | Mix_CloseAudio(); 78 | TTF_Quit(); 79 | Mix_Quit(); 80 | IMG_Quit(); 81 | SDL_Quit(); 82 | } 83 | 84 | Game& Game::GetInstance() { 85 | if (instance == nullptr) { 86 | instance = new Game("Mikael Mello - 16/0015537", 1024, 600); 87 | } 88 | return *instance; 89 | } 90 | 91 | State& Game::GetCurrentState() { 92 | if (stateStack.empty()) { 93 | throw std::runtime_error("State stack is empty, can't get current state"); 94 | } 95 | 96 | return *(stateStack.top().get()); 97 | } 98 | 99 | SDL_Renderer* Game::GetRenderer() { 100 | return renderer; 101 | } 102 | 103 | void Game::Push(State* state) { 104 | storedState = state; 105 | } 106 | 107 | void Game::Run() { 108 | if (storedState == nullptr) { 109 | throw std::runtime_error("Can not start Game without an initial state"); 110 | } 111 | 112 | stateStack.emplace(storedState); 113 | storedState = nullptr; 114 | 115 | GetCurrentState().Start(); 116 | 117 | while (!(stateStack.empty() || GetCurrentState().QuitRequested())) { 118 | auto& topState = stateStack.top(); 119 | if (topState->PopRequested()) { 120 | stateStack.pop(); 121 | if (!stateStack.empty()) { 122 | auto& resumeState = stateStack.top(); 123 | resumeState->Resume(); 124 | } 125 | } 126 | 127 | if (storedState != nullptr) { 128 | if (!stateStack.empty()) { 129 | auto& pauseState = stateStack.top(); 130 | pauseState->Pause(); 131 | } 132 | stateStack.emplace(storedState); 133 | storedState->Start(); 134 | storedState = nullptr; 135 | } 136 | 137 | if (stateStack.empty()) { 138 | break; 139 | } 140 | 141 | auto& state = stateStack.top(); 142 | 143 | InputManager::GetInstance().Update(); 144 | CalculateDeltaTime(); 145 | state->Update(GetDeltaTime()); 146 | state->Render(); 147 | SDL_RenderPresent(renderer); 148 | SDL_Delay(33); 149 | } 150 | 151 | while (!stateStack.empty()) { 152 | stateStack.pop(); 153 | } 154 | 155 | Resources::ClearImages(); 156 | Resources::ClearMusics(); 157 | Resources::ClearSounds(); 158 | } 159 | 160 | void Game::CalculateDeltaTime() { 161 | int ticks = SDL_GetTicks(); 162 | dt = (ticks - frameStart) / 1000.0f; 163 | frameStart = ticks; 164 | } 165 | 166 | float Game::GetDeltaTime() const { 167 | return dt; 168 | } 169 | 170 | int Game::GetWidth() const { 171 | return width; 172 | } 173 | 174 | int Game::GetHeight() const { 175 | return height; 176 | } 177 | -------------------------------------------------------------------------------- /src/GameData.cpp: -------------------------------------------------------------------------------- 1 | #include "GameData.hpp" 2 | -------------------------------------------------------------------------------- /src/GameObject.cpp: -------------------------------------------------------------------------------- 1 | #include "GameObject.hpp" 2 | #include "Component.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | GameObject::GameObject(double priority) : isDead(false) , started(false), angleDeg(0), priority(priority) {} 8 | 9 | GameObject::~GameObject() { 10 | components.clear(); 11 | } 12 | 13 | void GameObject::Start() { 14 | started = true; 15 | std::for_each( 16 | components.begin(), 17 | components.end(), 18 | [](std::shared_ptr& cpt) { cpt->Start(); } 19 | ); 20 | } 21 | 22 | void GameObject::Update(float dt) { 23 | std::for_each( 24 | components.rbegin(), 25 | components.rend(), 26 | [&](std::shared_ptr& cpt) { cpt->Update(dt); } 27 | ); 28 | } 29 | 30 | void GameObject::NotifyCollision(GameObject& other) { 31 | std::for_each( 32 | components.begin(), 33 | components.end(), 34 | [&](std::shared_ptr& cpt) { cpt->NotifyCollision(other); } 35 | ); 36 | } 37 | 38 | void GameObject::Render() { 39 | std::for_each( 40 | components.begin(), 41 | components.end(), 42 | [](std::shared_ptr& cpt) { cpt->Render(); } 43 | ); 44 | } 45 | 46 | bool GameObject::IsDead() const { 47 | return isDead; 48 | } 49 | 50 | void GameObject::RequestDelete() { 51 | isDead = true; 52 | } 53 | 54 | void GameObject::AddComponent(Component* cpt) { 55 | components.emplace_back(cpt); 56 | 57 | if (started) { 58 | cpt->Start(); 59 | } 60 | } 61 | 62 | void GameObject::RemoveComponent(Component* cpt) { 63 | auto it = std::find_if(components.begin(), components.end(), 64 | [&](std::shared_ptr& cpt2){ return cpt2.get() == cpt; }); 65 | 66 | if (it != components.end()) { 67 | components.erase(it); 68 | } 69 | } 70 | 71 | std::weak_ptr GameObject::GetComponent(GameData::Types type) { 72 | auto it = std::find_if(components.begin(), components.end(), 73 | [&](std::shared_ptr& cpt2){ return cpt2->Is(type); }); 74 | 75 | if (it == components.end()) { 76 | return std::weak_ptr(); 77 | } 78 | 79 | return std::weak_ptr(*it); 80 | } 81 | -------------------------------------------------------------------------------- /src/Helpers.cpp: -------------------------------------------------------------------------------- 1 | #define INCLUDE_SDL 2 | #define INCLUDE_SDL_IMAGE 3 | #define INCLUDE_SDL_MIXER 4 | #include "SDL_include.h" 5 | #include "Helpers.hpp" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | template 16 | void Helpers::split(const std::string &s, char delim, Out result) { 17 | std::stringstream ss; 18 | ss.str(s); 19 | std::string item; 20 | while (std::getline(ss, item, delim)) { 21 | *(result++) = item; 22 | } 23 | } 24 | 25 | std::vector Helpers::split(const std::string &s, char delim) { 26 | std::vector elems; 27 | Helpers::split(s, delim, std::back_inserter(elems)); 28 | return elems; 29 | } 30 | 31 | std::vector Helpers::split(const std::string& text, 32 | const std::string& delims) { 33 | std::vector tokens; 34 | std::size_t start = text.find_first_not_of(delims), end = 0; 35 | 36 | while((end = text.find_first_of(delims, start)) != std::string::npos) 37 | { 38 | tokens.push_back(text.substr(start, end - start)); 39 | start = text.find_first_not_of(delims, end); 40 | } 41 | if(start != std::string::npos) 42 | tokens.push_back(text.substr(start)); 43 | 44 | return tokens; 45 | } 46 | 47 | bool Helpers::is_whitespace(const std::string& s) { 48 | return std::all_of(s.begin(), s.end(), isspace); 49 | } 50 | 51 | float Helpers::rad_to_deg(float rad) { 52 | return rad * 180 / M_PI; 53 | } 54 | 55 | float Helpers::deg_to_rad(float deg) { 56 | return deg * M_PI / 180; 57 | } 58 | -------------------------------------------------------------------------------- /src/InputManager.cpp: -------------------------------------------------------------------------------- 1 | #define INCLUDE_SDL 2 | #include "SDL_include.h" 3 | #include "InputManager.hpp" 4 | #include 5 | #include 6 | 7 | InputManager InputManager::instance; 8 | 9 | InputManager& InputManager::GetInstance() { 10 | return instance; 11 | } 12 | 13 | InputManager::InputManager() : updateCounter(0), quitRequested(false) { 14 | mouseX = 0; 15 | mouseY = 0; 16 | ::memset(mouseState, 0, sizeof(bool)*6); 17 | ::memset(mouseUpdate, 0, sizeof(int)*6); 18 | } 19 | 20 | InputManager::~InputManager() {} 21 | 22 | void InputManager::Update() { 23 | SDL_Event event; 24 | updateCounter += 1; 25 | 26 | SDL_GetMouseState(&mouseX, &mouseY); 27 | 28 | while (SDL_PollEvent(&event)) { 29 | if (event.type == SDL_QUIT) { 30 | quitRequested = true; 31 | } else if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) { 32 | mouseState[event.button.button] = event.type == SDL_MOUSEBUTTONDOWN; 33 | mouseUpdate[event.button.button] = updateCounter; 34 | } else if (!event.key.repeat && (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP)) { 35 | keyState[event.key.keysym.sym] = event.type == SDL_KEYDOWN; 36 | keyUpdate[event.key.keysym.sym] = updateCounter; 37 | } 38 | } 39 | } 40 | 41 | bool InputManager::KeyPress(int key) { 42 | return keyState[key] && (keyUpdate[key] == updateCounter); 43 | } 44 | 45 | bool InputManager::KeyRelease(int key) { 46 | return !keyState[key] && (keyUpdate[key] == updateCounter); 47 | } 48 | 49 | bool InputManager::IsKeyDown(int key) { 50 | return keyState[key]; 51 | } 52 | 53 | bool InputManager::MousePress(int button) const { 54 | return mouseState[button] && (mouseUpdate[button] == updateCounter); 55 | } 56 | 57 | bool InputManager::MouseRelease(int button) const { 58 | return !mouseState[button] && (mouseUpdate[button] == updateCounter); 59 | } 60 | 61 | bool InputManager::IsMouseDown(int button) const { 62 | return mouseState[button]; 63 | } 64 | 65 | int InputManager::GetMouseX() const { 66 | return mouseX; 67 | } 68 | 69 | int InputManager::GetMouseY() const { 70 | return mouseY; 71 | } 72 | 73 | bool InputManager::QuitRequested() const { 74 | return quitRequested; 75 | } -------------------------------------------------------------------------------- /src/Music.cpp: -------------------------------------------------------------------------------- 1 | #define INCLUDE_SDL 2 | #define INCLUDE_SDL_MIXER 3 | #include "SDL_include.h" 4 | #include "Music.hpp" 5 | #include "Resources.hpp" 6 | #include 7 | #include 8 | #include 9 | 10 | Music::Music() {} 11 | 12 | Music::Music(const std::string& file) : Music() { 13 | Open(file); 14 | } 15 | 16 | void Music::Open(const std::string& file) { 17 | music = Resources::GetMusic(file); 18 | } 19 | 20 | void Music::Play(int times) { 21 | if (!music) { 22 | throw std::invalid_argument("Can not play music, file not open"); 23 | return; 24 | } 25 | 26 | int return_code = Mix_PlayMusic(music.get(), times); 27 | if (return_code != 0) { 28 | throw std::runtime_error("Could not play music: " + std::string(Mix_GetError())); 29 | } 30 | } 31 | 32 | void Music::Stop(int msTostop) { 33 | int return_code = Mix_FadeOutMusic(msTostop); 34 | if (return_code != 1) { 35 | throw std::runtime_error("Could not stop music: " + std::string(Mix_GetError())); 36 | } 37 | } 38 | 39 | bool Music::IsOpen() const { 40 | return (bool)music; 41 | } 42 | 43 | Music::~Music() {} 44 | -------------------------------------------------------------------------------- /src/Rect.cpp: -------------------------------------------------------------------------------- 1 | #include "Rect.hpp" 2 | #include "Vec2.hpp" 3 | 4 | Rect::Rect() : x(0), y(0), w(0), h(0) {} 5 | 6 | Rect::Rect(float x, float y, float w, float h) : x(x), y(y), w(w), h(h) {} 7 | 8 | float Rect::Distance(const Rect& rect) const { 9 | Vec2 c1 = this->Center(); 10 | Vec2 c2 = rect.Center(); 11 | return c1.Distance(c2); 12 | } 13 | 14 | Vec2 Rect::Center() const { 15 | return Vec2(x + w/2, y + h/2); 16 | } 17 | 18 | Rect Rect::GetCentered(Vec2 pos) const { 19 | return GetCentered(pos.x, pos.y); 20 | } 21 | 22 | Rect Rect::GetCentered(float centerX, float centerY) const { 23 | return Rect(centerX - w/2, centerY - h/2, w, h); 24 | } 25 | 26 | bool Rect::Contains(const Vec2& point) const { 27 | if (x > point.x || (x+w) < point.x) { 28 | return false; 29 | } else if (y > point.y || (y+h) < point.y) { 30 | return false; 31 | } 32 | return true; 33 | } 34 | 35 | Rect& Rect::operator+=(const Vec2& rhs) { 36 | x += rhs.x; 37 | y += rhs.y; 38 | return *this; 39 | } 40 | 41 | Rect& Rect::operator=(const Rect& v) { 42 | x = v.x; 43 | y = v.y; 44 | w = v.w; 45 | h = v.h; 46 | return *this; 47 | } 48 | -------------------------------------------------------------------------------- /src/Resources.cpp: -------------------------------------------------------------------------------- 1 | #define SDL_INCLUDE_MIXER 2 | #define SDL_INCLUDE_IMAGE 3 | #define SDL_INCLUDE_TTF 4 | #include "SDL_include.h" 5 | #include "Resources.hpp" 6 | #include "Helpers.hpp" 7 | #include "Game.hpp" 8 | #include 9 | #include 10 | #include 11 | 12 | std::unordered_map> Resources::imageTable; 13 | std::unordered_map> Resources::musicTable; 14 | std::unordered_map> Resources::soundTable; 15 | std::unordered_map> Resources::fontTable; 16 | 17 | std::shared_ptr Resources::GetImage(const std::string& file) { 18 | auto it = imageTable.find(file); 19 | if (it != imageTable.end()) { 20 | return it->second; 21 | } 22 | 23 | SDL_Texture* texture = IMG_LoadTexture(Game::GetInstance().GetRenderer(), file.c_str()); 24 | if (texture == nullptr) { 25 | throw std::runtime_error("Could not load texture from file " + file + ": " + IMG_GetError()); 26 | } 27 | 28 | std::shared_ptr textureSp(texture, [=](SDL_Texture* texture) { SDL_DestroyTexture(texture); }); 29 | imageTable.insert(std::make_pair(file, textureSp)); 30 | return textureSp; 31 | } 32 | 33 | std::shared_ptr Resources::GetMusic(const std::string& file) { 34 | auto it = musicTable.find(file); 35 | if (it != musicTable.end()) { 36 | return it->second; 37 | } 38 | 39 | Mix_Music* music = Mix_LoadMUS(file.c_str()); 40 | if (music == nullptr) { 41 | throw std::runtime_error("Could not load music from file " + file + ": " + Mix_GetError()); 42 | } 43 | 44 | std::shared_ptr musicSp(music, [=](Mix_Music* music) { Mix_FreeMusic(music); }); 45 | musicTable.insert(std::make_pair(file, musicSp)); 46 | return musicSp; 47 | } 48 | 49 | std::shared_ptr Resources::GetSound(const std::string& file) { 50 | auto it = soundTable.find(file); 51 | if (it != soundTable.end()) { 52 | return it->second; 53 | } 54 | 55 | Mix_Chunk* chunk = Mix_LoadWAV(file.c_str()); 56 | if (chunk == nullptr) { 57 | throw std::runtime_error("Could not load sound from file " + file + ": " + Mix_GetError()); 58 | } 59 | 60 | std::shared_ptr chunkSp(chunk, [=](Mix_Chunk* chunk) { Mix_FreeChunk(chunk); }); 61 | soundTable.insert(std::make_pair(file, chunkSp)); 62 | return chunkSp; 63 | } 64 | 65 | std::shared_ptr Resources::GetFont(const std::string& file, int fontSize) { 66 | auto key = file + std::to_string(fontSize); 67 | auto it = fontTable.find(key); 68 | if (it != fontTable.end()) { 69 | return it->second; 70 | } 71 | 72 | TTF_Font* font = TTF_OpenFont(file.c_str(), fontSize); 73 | if (font == nullptr) { 74 | throw std::runtime_error("Could not load font from file " + file + ": " + TTF_GetError()); 75 | } 76 | 77 | std::shared_ptr fontSp(font, [=](TTF_Font* font) { TTF_CloseFont(font); }); 78 | fontTable.insert(std::make_pair(key, fontSp)); 79 | return fontSp; 80 | } 81 | 82 | void Resources::ClearImages() { 83 | for(auto it = imageTable.begin(); it != imageTable.end();) { 84 | auto sp = it->second; 85 | if (sp.unique()) { 86 | it = imageTable.erase(it); 87 | } else { 88 | it++; 89 | } 90 | } 91 | } 92 | 93 | void Resources::ClearMusics() { 94 | for(auto it = musicTable.begin(); it != musicTable.end();) { 95 | auto sp = it->second; 96 | if (sp.unique()) { 97 | it = musicTable.erase(it); 98 | } else { 99 | it++; 100 | } 101 | } 102 | } 103 | 104 | void Resources::ClearSounds() { 105 | for(auto it = soundTable.begin(); it != soundTable.end();) { 106 | auto sp = it->second; 107 | if (sp.unique()) { 108 | it = soundTable.erase(it); 109 | } else { 110 | it++; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Sound.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #define INCLUDE_SDL 4 | #define INCLUDE_SDL_MIXER 5 | #include "SDL_include.h" 6 | #include "Sound.hpp" 7 | #include "Game.hpp" 8 | #include "GameData.hpp" 9 | #include "GameObject.hpp" 10 | #include "Resources.hpp" 11 | #include "Component.hpp" 12 | #include 13 | #include 14 | 15 | Sound::Sound(GameObject& associated) : Component(associated), channel(-1) {} 16 | 17 | Sound::Sound(GameObject& associated, const std::string& file) : Sound(associated) { 18 | Open(file); 19 | } 20 | 21 | Sound::~Sound() {} 22 | 23 | void Sound::Open(const std::string& file) { 24 | chunk = Resources::GetSound(file); 25 | } 26 | 27 | void Sound::Play(int times) { 28 | int availableChannel = Mix_PlayChannel(-1, chunk.get(), times - 1); 29 | if (availableChannel == -1) { 30 | throw std::runtime_error("Could not play sound: " + std::string(Mix_GetError())); 31 | } 32 | Mix_Volume(availableChannel, MIX_MAX_VOLUME); 33 | channel = availableChannel; 34 | } 35 | 36 | void Sound::Stop() { 37 | if (chunk && channel != -1) { 38 | Mix_HaltChannel(channel); 39 | } 40 | } 41 | 42 | void Sound::Render() {} 43 | 44 | void Sound::Update(float dt) {} 45 | 46 | bool Sound::Is(GameData::Types type) const { 47 | return type == this->Type; 48 | } 49 | 50 | bool Sound::IsOpen() const { 51 | return (bool)chunk; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/Sprite.cpp: -------------------------------------------------------------------------------- 1 | #define INCLUDE_SDL 2 | #define INCLUDE_SDL_IMAGE 3 | #include "SDL_include.h" 4 | #include "Sprite.hpp" 5 | #include "Game.hpp" 6 | #include "Camera.hpp" 7 | #include "Resources.hpp" 8 | #include "Component.hpp" 9 | #include "GameData.hpp" 10 | #include "Timer.hpp" 11 | #include "GameObject.hpp" 12 | #include 13 | #include 14 | 15 | Sprite::Sprite(GameObject& associated, int frameCount, float frameTime, float secondsToSelfDestruct) 16 | : Component(associated), scale(1, 1), frameCount(frameCount), frameTime(frameTime), 17 | currentFrame(0), secondsToSelfDestruct(secondsToSelfDestruct) {} 18 | 19 | Sprite::Sprite(GameObject& associated, const std::string& file, int frameCount, float frameTime, 20 | float secondsToSelfDestruct) : Sprite(associated, frameCount, frameTime, secondsToSelfDestruct) { 21 | Open(file); 22 | } 23 | 24 | Sprite::~Sprite() {} 25 | 26 | void Sprite::Open(const std::string& file) { 27 | texture = Resources::GetImage(file); 28 | int return_code = SDL_QueryTexture(texture.get(), nullptr, nullptr, &width, &height); 29 | if (return_code != 0) { 30 | throw std::runtime_error("Could not query invalid texture from " + file + ": " + IMG_GetError()); 31 | } 32 | 33 | associated.box.w = width / frameCount; 34 | associated.box.h = height; 35 | SetClip(0, 0, width / frameCount, height); 36 | } 37 | 38 | void Sprite::SetClip(int x, int y, int w, int h) { 39 | clipRect = { x, y, w, h }; 40 | } 41 | 42 | void Sprite::Render(int x, int y) { 43 | SDL_Rect temp_rect = { x, y, (int) round(scale.x * clipRect.w), (int) round(scale.y * clipRect.h) }; 44 | int return_code = SDL_RenderCopyEx(Game::GetInstance().GetRenderer(), texture.get(), &clipRect, &temp_rect, associated.angleDeg, nullptr, SDL_FLIP_NONE); 45 | if (return_code != 0) { 46 | throw std::runtime_error("Could not copy sprite to rendering target: " + std::string(IMG_GetError())); 47 | } 48 | } 49 | 50 | void Sprite::Render() { 51 | Render(associated.box.x - Camera::pos.x, associated.box.y - Camera::pos.y); 52 | } 53 | 54 | void Sprite::Update(float dt) { 55 | currentFrameCount.Update(dt); 56 | selfDestructCount.Update(dt); 57 | 58 | if (secondsToSelfDestruct > 0 && selfDestructCount.Get() > secondsToSelfDestruct) { 59 | associated.RequestDelete(); 60 | } 61 | 62 | if (currentFrameCount.Get() > frameTime) { 63 | int framesElapsed = currentFrameCount.Get() / frameTime; 64 | int nextFrame = (currentFrame + framesElapsed) % frameCount; 65 | SetFrame(nextFrame); 66 | currentFrameCount.Restart(); 67 | } 68 | } 69 | 70 | bool Sprite::Is(GameData::Types type) const { 71 | return type == this->Type; 72 | } 73 | 74 | int Sprite::GetWidth() const { 75 | return round(scale.x * (width / frameCount)); 76 | } 77 | 78 | int Sprite::GetHeight() const { 79 | return round(scale.y * height); 80 | } 81 | 82 | bool Sprite::IsOpen() const { 83 | return (bool)texture; 84 | } 85 | 86 | void Sprite::SetScaleX(float scaleX, float scaleY) { 87 | Vec2 oldCenter = associated.box.Center(); 88 | 89 | if (scaleX != 0) { 90 | scale.x = scaleX; 91 | associated.box.w = GetWidth(); 92 | } 93 | if (scaleY != 0) { 94 | scale.y = scaleY; 95 | associated.box.h = GetHeight(); 96 | } 97 | 98 | associated.box = associated.box.GetCentered(oldCenter); 99 | } 100 | 101 | void Sprite::SetFrame(int frame) { 102 | currentFrame = frame; 103 | SetClip(currentFrame * width / frameCount, 0, width / frameCount, height); 104 | } 105 | 106 | void Sprite::SetFrameCount(int frameCount) { 107 | this->frameCount = frameCount; 108 | associated.box.w = GetWidth(); 109 | SetFrame(0); 110 | } 111 | 112 | void Sprite::SetFrameTime(float frameTime) { 113 | this->frameTime = frameTime; 114 | } 115 | 116 | Vec2 Sprite::GetScale() { 117 | return scale; 118 | 119 | SDL_Surface* surface = nullptr; 120 | } 121 | -------------------------------------------------------------------------------- /src/State.cpp: -------------------------------------------------------------------------------- 1 | #include "State.hpp" 2 | #include "Sprite.hpp" 3 | #include "TileSet.hpp" 4 | #include "TileMap.hpp" 5 | #include "InputManager.hpp" 6 | #include "Sound.hpp" 7 | #include "Camera.hpp" 8 | #include "CameraFollower.hpp" 9 | #include "GameObject.hpp" 10 | #include "Component.hpp" 11 | #include "Vec2.hpp" 12 | #include "Music.hpp" 13 | #include "Helpers.hpp" 14 | #include "Collision.hpp" 15 | #include "Collider.hpp" 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | State::State() : quitRequested(false), started(false), popRequested(false) {} 22 | 23 | State::~State() {} 24 | 25 | std::weak_ptr State::AddObject(GameObject* go) { 26 | std::shared_ptr goSharedPtr(go); 27 | objects.insert(goSharedPtr); 28 | 29 | if (started) { 30 | goSharedPtr->Start(); 31 | } 32 | 33 | return std::weak_ptr(goSharedPtr); 34 | } 35 | 36 | std::weak_ptr State::GetObjectPtr(GameObject* go) { 37 | auto it = std::find_if(objects.begin(), objects.end(), 38 | [&](auto& go2){ return go == go2.get(); }); 39 | 40 | if (it == objects.end()) { 41 | return std::weak_ptr(); 42 | } 43 | 44 | return std::weak_ptr(*it); 45 | } 46 | 47 | bool State::QuitRequested() const { 48 | return quitRequested; 49 | } 50 | 51 | bool State::PopRequested() const { 52 | return popRequested; 53 | } 54 | 55 | void State::StartArray() { 56 | std::for_each( 57 | objects.begin(), 58 | objects.end(), 59 | [&](auto& go) { go->Start(); } 60 | ); 61 | } 62 | 63 | void State::UpdateArray(float dt) { 64 | std::for_each( 65 | objects.begin(), 66 | objects.end(), 67 | [&](auto& go) { go->Update(dt); } 68 | ); 69 | } 70 | 71 | void State::RenderArray() { 72 | std::for_each( 73 | objects.begin(), 74 | objects.end(), 75 | [](auto& go) { go->Render(); } 76 | ); 77 | } 78 | -------------------------------------------------------------------------------- /src/Text.cpp: -------------------------------------------------------------------------------- 1 | 2 | #define INCLUDE_SDL_TTF 3 | #define INCLUDE_SDL_IMAGE 4 | #include "SDL_include.h" 5 | #include "Text.hpp" 6 | #include "Game.hpp" 7 | #include "GameData.hpp" 8 | #include "Resources.hpp" 9 | #include "Component.hpp" 10 | #include "GameObject.hpp" 11 | #include "Camera.hpp" 12 | #include 13 | #include 14 | 15 | 16 | Text::Text(GameObject& associated, const std::string& fontFile, int fontSize, TextStyle style, 17 | const std::string& text, SDL_Color color) : Component(associated), fontFile(fontFile), 18 | fontSize(fontSize), style(style), text(text), color(color), blink(false), display(true) { 19 | RemakeTexture(); 20 | } 21 | 22 | Text::~Text() {} 23 | 24 | void Text::Update(float dt) { 25 | if (blink) { 26 | blinkTimer.Update(dt); 27 | if (blinkTimer.Get() > blinkPeriod) { 28 | blinkTimer.Restart(); 29 | display = !display; 30 | } 31 | } 32 | } 33 | 34 | void Text::Render() { 35 | if (!display) { 36 | return; 37 | } 38 | 39 | Vec2 cameraPos = Camera::pos; 40 | SDL_Rect clipRect = { 0, 0, (int)associated.box.w, (int)associated.box. h }; 41 | SDL_Rect tempRect = { (int)(associated.box.x - cameraPos.x), (int)(associated.box.y - cameraPos.y), clipRect.w, clipRect.h }; 42 | int return_code = SDL_RenderCopyEx(Game::GetInstance().GetRenderer(), texture.get(), &clipRect, &tempRect, associated.angleDeg, nullptr, SDL_FLIP_NONE); 43 | if (return_code != 0) { 44 | throw std::runtime_error("Could not copy sprite to rendering target: " + std::string(IMG_GetError())); 45 | } 46 | } 47 | 48 | bool Text::Is(GameData::Types type) const { 49 | return type == this->Type; 50 | } 51 | 52 | void Text::SetText(const std::string& text) { 53 | this->text = text; 54 | RemakeTexture(); 55 | } 56 | 57 | void Text::SetColor(SDL_Color color) { 58 | this->color = color; 59 | RemakeTexture(); 60 | } 61 | 62 | void Text::SetStyle(TextStyle style) { 63 | this->style = style; 64 | RemakeTexture(); 65 | } 66 | 67 | void Text::SetFontFile(const std::string& fontFile) { 68 | this->fontFile = fontFile; 69 | RemakeTexture(); 70 | } 71 | 72 | void Text::SetFontSize(int fontSize) { 73 | this->fontSize = fontSize; 74 | RemakeTexture(); 75 | } 76 | 77 | void Text::SetBlink(bool blink, float period) { 78 | this->blink = blink; 79 | if (blink) { 80 | blinkPeriod = period; 81 | } 82 | } 83 | 84 | void Text::RemakeTexture() { 85 | font = Resources::GetFont(fontFile, fontSize); 86 | 87 | SDL_Surface* surface = nullptr; 88 | 89 | switch(style) { 90 | case TextStyle::BLENDED: 91 | surface = TTF_RenderText_Blended(font.get(), text.c_str(), color); 92 | break; 93 | case TextStyle::SOLID: 94 | surface = TTF_RenderText_Solid(font.get(), text.c_str(), color); 95 | break; 96 | case TextStyle::SHADED: 97 | surface = TTF_RenderText_Shaded(font.get(), text.c_str(), color, { 0, 0, 0, 1 }); 98 | break; 99 | } 100 | 101 | if (surface == nullptr) { 102 | throw std::runtime_error("Could not create surface from text component"); 103 | } 104 | 105 | associated.box.w = surface->w; 106 | associated.box.h = surface->h; 107 | SDL_Texture* textureFromSurface = SDL_CreateTextureFromSurface(Game::GetInstance().GetRenderer(), surface); 108 | texture = std::shared_ptr(textureFromSurface, [=](SDL_Texture* texture) { SDL_DestroyTexture(texture); }); 109 | SDL_FreeSurface(surface); 110 | } 111 | -------------------------------------------------------------------------------- /src/TileMap.cpp: -------------------------------------------------------------------------------- 1 | #include "TileMap.hpp" 2 | #include "TileSet.hpp" 3 | #include "Helpers.hpp" 4 | #include "Camera.hpp" 5 | #include "Component.hpp" 6 | #include "GameObject.hpp" 7 | #include "GameData.hpp" 8 | #include 9 | #include 10 | #include 11 | 12 | TileMap::TileMap(GameObject& associated, const std::string& file, TileSet* tileSet) : Component(associated), tileSet(tileSet) { 13 | Load(file); 14 | } 15 | 16 | void TileMap::Load(const std::string& file) { 17 | std::ifstream infile(file); 18 | std::string line; 19 | int index = 0; 20 | 21 | while (index < 3 && std::getline(infile, line, ',')) { 22 | int number = std::stoi(line); 23 | switch (index) { 24 | case 0: mapWidth = number; break; 25 | case 1: mapHeight = number; break; 26 | case 2: mapDepth = number; break; 27 | } 28 | 29 | index++; 30 | } 31 | 32 | long long times = mapWidth * mapHeight * mapDepth; 33 | 34 | for (long long i = 0; i < times; i++) { 35 | std::getline(infile, line, ','); 36 | if (Helpers::is_whitespace(line)) { 37 | i--; 38 | continue; 39 | } 40 | int number = std::stoi(line); 41 | tileMatrix.push_back(number - 1); 42 | } 43 | 44 | // default parallax factor 45 | for (int i = 0; i < mapDepth; i++) { 46 | layerParallax.push_back({0, 0}); 47 | } 48 | } 49 | 50 | void TileMap::SetTileSet(TileSet* tileSet) { 51 | this->tileSet = tileSet; 52 | } 53 | 54 | void TileMap::SetParallax(int layer, float xFactor, float yFactor) { 55 | if (layer < 0 || layer >= mapDepth) { 56 | throw std::invalid_argument("Invalid layer to add parallax factors"); 57 | } 58 | 59 | layerParallax[layer] = { xFactor, yFactor }; 60 | } 61 | 62 | int& TileMap::At(int x, int y, int z) { 63 | int index = 0; 64 | index += mapWidth * mapHeight * z; 65 | index += mapWidth * y; 66 | index += x; 67 | 68 | return tileMatrix[index]; 69 | } 70 | 71 | void TileMap::RenderLayer(int layer, int cameraX, int cameraY) { 72 | for (int i = 0; i < mapWidth; i++) { 73 | for (int j = 0; j < mapHeight; j++) { 74 | int x = i * tileSet->GetTileWidth() - cameraX - cameraX * layerParallax[layer].x; 75 | int y = j * tileSet->GetTileHeight() - cameraY - cameraY * layerParallax[layer].y; 76 | tileSet->RenderTile((unsigned)At(i, j, layer), x, y); 77 | } 78 | } 79 | } 80 | 81 | void TileMap::Render() { 82 | for (int i = 0; i < mapDepth; i++) { 83 | RenderLayer(i, Camera::pos.x, Camera::pos.y); 84 | } 85 | } 86 | 87 | int TileMap::GetWidth() { 88 | return mapWidth; 89 | } 90 | 91 | int TileMap::GetHeight() { 92 | return mapHeight; 93 | } 94 | 95 | bool TileMap::Is(GameData::Types type) const { 96 | return type == this->Type; 97 | } 98 | 99 | 100 | int TileMap::GetDepth() { 101 | return mapDepth; 102 | } 103 | 104 | void TileMap::Update(float dt) {} 105 | -------------------------------------------------------------------------------- /src/TileSet.cpp: -------------------------------------------------------------------------------- 1 | #include "TileSet.hpp" 2 | 3 | TileSet::TileSet(int tileWidth, int tileHeight, const std::string& file) 4 | : tileSet(gameObject, file), tileWidth(tileWidth), tileHeight(tileHeight) { 5 | if (tileSet.IsOpen()) { 6 | rows = tileSet.GetHeight() / tileHeight; 7 | columns = tileSet.GetWidth() / tileWidth; 8 | } 9 | } 10 | 11 | void TileSet::RenderTile(unsigned int index, float x, float y) { 12 | if (index >= (rows * columns - 1)) { 13 | return; 14 | } 15 | 16 | int clipX = (index % columns) * tileWidth; 17 | int clipY = (index / columns) * tileHeight; 18 | 19 | tileSet.SetClip(clipX, clipY, tileWidth, tileHeight); 20 | tileSet.Render(x, y); 21 | } 22 | 23 | int TileSet::GetTileHeight() { 24 | return tileHeight; 25 | } 26 | 27 | int TileSet::GetTileWidth() { 28 | return tileWidth; 29 | } 30 | -------------------------------------------------------------------------------- /src/Timer.cpp: -------------------------------------------------------------------------------- 1 | #include "Timer.hpp" 2 | 3 | Timer::Timer() : time(0) {} 4 | 5 | void Timer::Update(float dt) { 6 | this->time += dt; 7 | } 8 | 9 | void Timer::Restart() { 10 | this->time = 0; 11 | } 12 | 13 | float Timer::Get() { 14 | return this->time; 15 | } 16 | -------------------------------------------------------------------------------- /src/Vec2.cpp: -------------------------------------------------------------------------------- 1 | #include "Vec2.hpp" 2 | #include 3 | 4 | Vec2::Vec2() : x(0), y(0) {} 5 | 6 | Vec2::Vec2(float x, float y) : x(x), y(y) {} 7 | 8 | Vec2 Vec2::GetNormalized() const { 9 | float magnitude = Magnitude(); 10 | return Vec2(x / magnitude, y / magnitude); 11 | } 12 | 13 | Vec2 Vec2::GetRotated(const float& d) const { 14 | float xl = x * cos(d) - y * sin(d); 15 | float yl = y * cos(d) + x * sin(d); 16 | return Vec2(xl, yl); 17 | } 18 | 19 | float Vec2::Magnitude() const { 20 | return sqrt(x*x + y*y); 21 | } 22 | 23 | float Vec2::Distance(const Vec2& v) const { 24 | Vec2 diff = *this - v; 25 | return diff.Magnitude(); 26 | } 27 | 28 | float Vec2::XAxisInclination() const { 29 | return atan2(y, x); 30 | } 31 | 32 | Vec2& Vec2::operator+=(const Vec2& rhs) { 33 | x += rhs.x; 34 | y += rhs.y; 35 | return *this; 36 | } 37 | 38 | Vec2& Vec2::operator-=(const Vec2& rhs) { 39 | x -= rhs.x; 40 | y -= rhs.y; 41 | return *this; 42 | } 43 | 44 | Vec2& Vec2::operator*=(const float& rhs) { 45 | x *= rhs; 46 | y *= rhs; 47 | return *this; 48 | } 49 | 50 | Vec2& Vec2::operator=(const Vec2& v) { 51 | x = v.x; 52 | y = v.y; 53 | return *this; 54 | } 55 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #define INCLUDE_SDL 2 | #include "SDL_include.h" 3 | #include "Game.hpp" 4 | #include 5 | 6 | int main (int argc, char** argv) { 7 | try { 8 | Game& game = Game::GetInstance(); 9 | game.Run(); 10 | } catch (const std::exception& ex) { 11 | std::cout << "Game crashed\n\n" << std::endl; 12 | std::cout << "Exception: " << ex.what() << std::endl; 13 | std::cout << "SDL Error: " << SDL_GetError() << std::endl; 14 | } 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /templates/Component.cpp.template: -------------------------------------------------------------------------------- 1 | #include ".hpp" 2 | #include "GameObject.hpp" 3 | #include "Collider.hpp" 4 | #include "Sprite.hpp" 5 | #include 6 | 7 | ::(GameObject& associated) : Component(associated) { 8 | Collider* collider = new Collider(associated); 9 | Sprite* sprite = new Sprite(associated, ); 10 | associated.AddComponent(collider); 11 | associated.AddComponent(sprite); 12 | } 13 | 14 | ::~() {} 15 | 16 | void ::NotifyCollision(GameObject& other) {} 17 | 18 | void ::Start() {} 19 | 20 | void ::Update(float dt) {} 21 | 22 | void ::Render() {} 23 | 24 | bool ::Is(GameData::Types type) const { 25 | return type == this->Type; 26 | } 27 | -------------------------------------------------------------------------------- /templates/Component.hpp.template: -------------------------------------------------------------------------------- 1 | #ifndef _H 2 | #define _H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "Component.hpp" 9 | #include "GameObject.hpp" 10 | #include "GameData.hpp" 11 | #include 12 | 13 | class : public Component { 14 | public: 15 | (GameObject& associated); 16 | 17 | ~(); 18 | 19 | void Start() override; 20 | 21 | void NotifyCollision(GameObject& other) override; 22 | 23 | void Update(float dt) override; 24 | 25 | bool Is(GameData::Types type) const override; 26 | 27 | void Render() override; 28 | 29 | const GameData::Types Type = GameData::Types::; 30 | private: 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /templates/State.cpp.template: -------------------------------------------------------------------------------- 1 | #include ".hpp" 2 | 3 | ::() { 4 | } 5 | 6 | ::~() {} 7 | 8 | void ::Update(float dt) { 9 | if (quitRequested || popRequested) { 10 | return; 11 | } 12 | 13 | UpdateArray(dt); 14 | } 15 | 16 | void ::Start() { 17 | LoadAssets(); 18 | StartArray(); 19 | started = true; 20 | } 21 | 22 | void ::Pause() {} 23 | 24 | void ::Resume() {} 25 | 26 | void ::LoadAssets() {} 27 | 28 | void ::Render() { 29 | RenderArray(); 30 | } 31 | -------------------------------------------------------------------------------- /templates/State.hpp.template: -------------------------------------------------------------------------------- 1 | #ifndef _H 2 | #define _H 3 | 4 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5 | * INCLUDES E DEFINES 6 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 7 | 8 | #include "State.hpp" 9 | #include "Music.hpp" 10 | 11 | class : public State { 12 | public: 13 | (); 14 | 15 | ~(); 16 | 17 | void Start(); 18 | 19 | void Pause(); 20 | 21 | void Resume(); 22 | 23 | bool QuitRequested() const; 24 | 25 | void LoadAssets(); 26 | 27 | void Update(float dt); 28 | 29 | void Render(); 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /zip-files.sh: -------------------------------------------------------------------------------- 1 | id="$(git branch | grep \* | cut -d '-' -f2)" 2 | matricula="160015537" 3 | 4 | zip "$matricula""_T$id.zip" src/* obj/.gitkeep include/* Makefile README.md 5 | --------------------------------------------------------------------------------