diff --git a/.gitignore b/.gitignore index 45dba51..6a8bc10 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ .vscode -build -main.x86_64 \ No newline at end of file +build \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index c3abcb0..0000000 --- a/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) 2024 acetheking987 - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 73cd15b..0000000 --- a/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# bootleg-game-engine - -A bootleg "game engine" in c++, using SDL2 for rendering and input handling. - -## Dependencies -- SDL2 -- SDL2_ttf -- SDL2_mixer - -## Building -```bash -$ make -``` \ No newline at end of file diff --git a/assets/bird.png b/assets/bird.png new file mode 100644 index 0000000..9b38e24 Binary files /dev/null and b/assets/bird.png differ diff --git a/example_assets/C64.ttf b/example_assets/C64.ttf deleted file mode 100644 index 02ff5fc..0000000 Binary files a/example_assets/C64.ttf and /dev/null differ diff --git a/example_assets/crystal_zone.mp3 b/example_assets/crystal_zone.mp3 deleted file mode 100644 index 2dca5b0..0000000 Binary files a/example_assets/crystal_zone.mp3 and /dev/null differ diff --git a/example_assets/sprites.bmp b/example_assets/sprites.bmp deleted file mode 100644 index 0fbd794..0000000 Binary files a/example_assets/sprites.bmp and /dev/null differ diff --git a/include/assets.hpp b/include/assets.hpp new file mode 100644 index 0000000..b5bac87 --- /dev/null +++ b/include/assets.hpp @@ -0,0 +1,59 @@ +#ifndef ASSETS_HPP +#define ASSETS_HPP + +#include +#include +#include +#include + +// Structs +struct Rect { + int x; + int y; + int width; + int height; + + Rect(int x = 0, int y = 0, int width = 0, int height = 0) + : x(x), y(y), width(width), height(height) {} + Rect(const SDL_Rect& rect) + : x(rect.x), y(rect.y), width(rect.w), height(rect.h) {} +}; + +struct Tile { + Rect srcRect; + std::string assetName; +}; + +// Classes +class AssetManager { +public: + AssetManager(); + ~AssetManager(); + + void load(const std::string& name, const std::string& filePath); + SDL_Surface* getAsset(const std::string& name) const; + void unloadAsset(const std::string& name); + void clearAssets(); + +private: + std::unordered_map assets; +}; + +struct TileMap { +public: + TileMap(AssetManager& assetManager); + ~TileMap(); + + void addTile(const std::string& name, const Tile& tile); + Tile getTile(const std::string& name) const; + SDL_Surface* getTileAsset(const std::string& name) const; + Rect getTileRect(const std::string& name) const; + void removeTile(const std::string& name); + void clear(); + +private: + std::unordered_map tiles; + AssetManager& assetManager; +}; + +#endif \ No newline at end of file diff --git a/include/engine.h b/include/engine.h deleted file mode 100644 index 6edf820..0000000 --- a/include/engine.h +++ /dev/null @@ -1,30 +0,0 @@ -#include "input.h" -#include "text.h" -#include "sprite.h" -#include "vector.h" -#include -#include -#include -#include "sound.h" - -class Engine -{ - private: - const char* ctitle; - int frameStart = 0; - bool soundInitialized = false; - - public: - Input input; - int targetFrameRate = 60; - SDL_Window *window; - SDL_Renderer *renderer; - - Engine(std::string title, int width, int height); - void initSound(); - void clear(SDL_Color color); - void startFrame(); - void render(); - void update(); - ~Engine(); -}; \ No newline at end of file diff --git a/include/engine.hpp b/include/engine.hpp new file mode 100644 index 0000000..cfebb07 --- /dev/null +++ b/include/engine.hpp @@ -0,0 +1,50 @@ +#ifndef RENDER_HPP +#define RENDER_HPP + +// Include necessary headers +#include "assets.hpp" +#include "input.hpp" +#include + +// Structs +struct Color { + Uint8 r; + Uint8 g; + Uint8 b; + Uint8 a; + + Color(Uint8 r = 0, Uint8 g = 0, Uint8 b = 0, Uint8 a = 255) : r(r), g(g), b(b), a(a) {} +}; + +// Classes +class Engine { +public: + Engine(const char* windowTitle, int windowWidth, int windowHeight, int windowFlags); + Engine(const char* windowTitle, int windowWidth, int windowHeight) + : Engine(windowTitle, windowWidth, windowHeight, 0) {} + ~Engine(); + + void targetFPS(int fps); + void startFrame(); + void clearScreen(const Color& color); + void draw(SDL_Surface* surface, Rect destRect); + void draw(SDL_Surface* surface, Rect srcRect, Rect destRect); + void draw(Tile tile, Rect destRect); + void draw(SDL_Surface* surface, Vector2 pos) { draw(surface, Rect(pos.x, pos.y, surface->w, surface->h)); } + void draw(SDL_Surface* surface, Rect srcRect, Vector2 pos) { draw(surface, srcRect, Rect(pos.x, pos.y, srcRect.width, srcRect.height)); } + void draw(Tile tile, Vector2 pos) { draw(tile, Rect(pos.x, pos.y, tile.srcRect.width, tile.srcRect.height)); } + void present(); + + Input input; // Input handler + AssetManager assets; // Asset manager + +private: + SDL_Window* window; + SDL_Renderer* renderer; + int targetFPSValue = 60; // Target frames per second + Uint64 frameStartTime = 0; // Start time of the current frame + Uint64 frameEndTime = 0; // End time of the current frame + Uint64 frameDuration = 0; // Duration of the current frame in milliseconds +}; + +#endif \ No newline at end of file diff --git a/include/input.h b/include/input.h deleted file mode 100644 index cc37d74..0000000 --- a/include/input.h +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include -#include - -enum CBUTTONS -{ - DPAD_UP = SDL_CONTROLLER_BUTTON_DPAD_UP, - DPAD_DOWN = SDL_CONTROLLER_BUTTON_DPAD_DOWN, - DPAD_LEFT = SDL_CONTROLLER_BUTTON_DPAD_LEFT, - DPAD_RIGHT = SDL_CONTROLLER_BUTTON_DPAD_RIGHT, - A = SDL_CONTROLLER_BUTTON_A, - B = SDL_CONTROLLER_BUTTON_B, - X = SDL_CONTROLLER_BUTTON_X, - Y = SDL_CONTROLLER_BUTTON_Y, - BACK = SDL_CONTROLLER_BUTTON_BACK, - GUIDE = SDL_CONTROLLER_BUTTON_GUIDE, - START = SDL_CONTROLLER_BUTTON_START, - LEFTSTICK = SDL_CONTROLLER_BUTTON_LEFTSTICK, - RIGHTSTICK = SDL_CONTROLLER_BUTTON_RIGHTSTICK, - LEFTSHOULDER = SDL_CONTROLLER_BUTTON_LEFTSHOULDER, - RIGHTSHOULDER = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER -}; - -struct Controller -{ - SDL_GameController* controller; - std::string name; - std::map buttons; - Sint16 leftStickX = 0; - Sint16 leftStickY = 0; - Sint16 rightStickX = 0; - Sint16 rightStickY = 0; - Sint16 leftTrigger = 0; - Sint16 rightTrigger = 0; -}; - -struct Mouse -{ - int x; - int y; - int wheel; - std::map buttons; -}; - -class Input -{ - private: - SDL_Event event; - - public: - bool exit; - std::map controllers; - std::map activeKeys; - Mouse mouse; - - Input(); - void update(); - bool keysPressed(std::vector keys); - bool controllerPressed(std::vector buttons, int controller = 0); -}; \ No newline at end of file diff --git a/include/input.hpp b/include/input.hpp new file mode 100644 index 0000000..5b59ffd --- /dev/null +++ b/include/input.hpp @@ -0,0 +1,45 @@ +#ifndef INPUT_HPP +#define INPUT_HPP + +#include +#include +#include + +// Structs +struct Vector2 { + int x; + int y; + Vector2(int x = 0, int y = 0) : x(x), y(y) {} +}; + +// Classes +class Input { +public: + Input(); + ~Input(); + + void handleEvents(); + bool quitRequested() const; + bool isKeyPressed(SDL_Scancode key); + bool isKeyPressed(std::vector keys); + bool hasKeyBeenPressed(SDL_Scancode key); + bool hasKeyBeenPressed(std::vector keys); + bool isMouseButtonPressed(Uint8 button); + bool isMouseButtonPressed(std::vector buttons); + bool hasMouseButtonBeenPressed(Uint8 button); + bool hasMouseButtonBeenPressed(std::vector buttons); + Vector2 getMousePosition() const; + + +private: + SDL_Event event; + std::unordered_map keyStates; + std::unordered_map keyFallingEdgeStates; + std::unordered_map mouseButtonStates; + std::unordered_map mouseButtonFallingEdgeStates; + Vector2 mousePosition; + bool quitRequestedFlag = false; +}; + +#endif + diff --git a/include/sound.h b/include/sound.h deleted file mode 100644 index 05e8cf7..0000000 --- a/include/sound.h +++ /dev/null @@ -1,26 +0,0 @@ -#include - -class Music -{ - private: - Mix_Music* music; - public: - Music(const char* path); - void play(int loops = -1); - void pause(); - void stop(); - void resume(); - void setVolume(int volume); - ~Music(); -}; - -class Effect -{ - private: - Mix_Chunk* effect; - public: - Effect(const char* path); - void setVolume(int volume); - void play(int loops = 0, int channel = -1); - ~Effect(); -}; \ No newline at end of file diff --git a/include/sprite.h b/include/sprite.h deleted file mode 100644 index 4b3474e..0000000 --- a/include/sprite.h +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include -#include -#include -#include "vector.h" -#pragma once - -class Sprite -{ - private: - std::map frames; - SDL_Surface* surface; - SDL_Texture* Texture; - int_vec2 size; - - public: - SDL_Rect dstrect; - int_vec2 scale = { 1, 1 }; - double angle = 0; - Sprite(SDL_Renderer* renderer, SDL_Surface* surface); - void draw(SDL_Renderer* renderer, std::string frame, bool autoRect = true, bool flip = false); - void addFrame(std::string name, SDL_Rect rect); - void move(int_vec2 position); - void setPos(int_vec2 position); - int_vec2 getSize(std::string frame); - void autorect(std::string frame); - ~Sprite(); -}; - -class Animation -{ - private: - std::vector frames; - Sprite* sprite; - int frameDuration = 0; - int frameCounter = 0; - std::string currentFrameName; - - public: - bool end = false; - int frameDirection = 1; - int frameIndex = 0; - bool loop = true; - bool pingpong = false; - Animation(std::map frames, Sprite* sprite, int frameDuration); - void update(); - void draw(SDL_Renderer* renderer, bool autoRect = true, bool flip = false); - void reset(); -}; - -class AnimationManager -{ - private: - std::map animations; - std::map flip; - std::map autoRect; - std::string currentAnimation; - bool playing = false; - Sprite* sprite; - - public: - AnimationManager(Sprite* sprite); - void addAnimation(std::string name, std::map animation, int frameDuration = 10, bool flip = false, bool autoRect = true, bool loop = true, bool pingpong = false); - void setAnimation(std::string name); - void playAnimation(std::string name); - void update(); - void draw(SDL_Renderer* renderer); -}; \ No newline at end of file diff --git a/include/text.h b/include/text.h deleted file mode 100644 index 815c1b7..0000000 --- a/include/text.h +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include -#include - -class Text -{ -private: - SDL_Surface* surface; - SDL_Texture* texture; - TTF_Font* font; - SDL_Color color; - SDL_Rect rect; - -public: - Text(TTF_Font* font, SDL_Color color); - void draw(SDL_Renderer* renderer, std::string text, int x, int y); -}; \ No newline at end of file diff --git a/include/vector.h b/include/vector.h deleted file mode 100644 index 76c938d..0000000 --- a/include/vector.h +++ /dev/null @@ -1,58 +0,0 @@ -// prevent multiple inclusion -#pragma once - -// class for 2D float vector -class float_vec2 -{ - // public members - public: - // x and y coordinates - float x, y; - // operator functions - float_vec2 operator+(float_vec2 vec) - { - return { x + vec.x, y + vec.y }; - } - float_vec2 operator*(float v) - { - return { x * v, y * v }; - } - float_vec2 operator*(float_vec2 vec) - { - return { x * vec.x, y * vec.y }; - } - float_vec2 operator-(float_vec2 vec) - { - return { x - vec.x, y - vec.y }; - } - float_vec2 operator-(float v) - { - return { x - v, y - v }; - } -}; - -// class for 2D integer vector -class int_vec2 -{ - // public members - public: - // x and y coordinates - int x, y; - // operator functions - int_vec2 operator+(int_vec2 vec) - { - return { x + vec.x, y + vec.y }; - } - int_vec2 operator*(int v) - { - return { x * v, y * v }; - } - int_vec2 operator*(int_vec2 vec) - { - return { x * vec.x, y * vec.y }; - } - int_vec2 operator-(int_vec2 vec) - { - return { x - vec.x, y - vec.y }; - } -}; \ No newline at end of file diff --git a/makefile b/makefile index c270f46..2b175ea 100644 --- a/makefile +++ b/makefile @@ -1,20 +1,39 @@ +# Compiler and linker settings CXX = g++ -LDFLAGS = `sdl2-config --libs` -l SDL2_ttf -l SDL2_mixer -CXXFLAGS = `sdl2-config --cflags` -pedantic -O2 -std=c++17 -lX11 -lstdc++fs -I include +LDFLAGS = -lSDL3 -lSDL3_image +CXXFLAGS = -pedantic -std=c++17 -I include -I /usr/include/SDL3_image -I /usr/include/SDL3 + +# Source and object files SRC = $(wildcard src/*.cpp) OBJ = $(addprefix build/, $(notdir $(SRC:.cpp=.o))) + +# Target executable TARGET = build/x86_64/main.x86_64 -all: dirs $(TARGET) +# Default target +all: clean dirs $(TARGET) +# development target with debugging +dev: CXXFLAGS += -g +dev: all + +# Release target +release: CXXFLAGS += -O3 +release: all + +# Create build directory if it doesn't exist dirs: @mkdir -p build/x86_64 +# Build target $(TARGET): $(OBJ) - @$(CXX) -o $@ $^ $(LDFLAGS) + $(CXX) -o $@ $^ $(LDFLAGS) +# Build object files build/%.o: src/%.cpp - @$(CXX) -c -o $@ $< $(CXXFLAGS) + $(CXX) -c -o $@ $< $(CXXFLAGS) +# Clean up build files clean: - @rm -f $(OBJ) $(TARGET) \ No newline at end of file + @rm -f $(OBJ) $(TARGET) + @echo "Cleaned up build files." \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..e417bfc --- /dev/null +++ b/readme.md @@ -0,0 +1,4 @@ +# Bootleg Game Engine V2 + +## Overview +This is a lightweight "game engine", designed to be simple and easy to use for small projects. It provides basic functionality for rendering, input handling, and game loop management. \ No newline at end of file diff --git a/src/assets.cpp b/src/assets.cpp new file mode 100644 index 0000000..f4b81df --- /dev/null +++ b/src/assets.cpp @@ -0,0 +1,93 @@ +#include "assets.hpp" + +AssetManager::AssetManager() { +} + +AssetManager::~AssetManager() { + clearAssets(); +} + +void AssetManager::load(const std::string& name, const std::string& filePath) { + SDL_Surface* surface = IMG_Load(filePath.c_str()); + if (!surface) { + SDL_Log("Failed to load image %s: %s", filePath.c_str(), SDL_GetError()); + } + assets[name] = surface; +} + +SDL_Surface* AssetManager::getAsset(const std::string& name) const { + auto it = assets.find(name); + if (it != assets.end()) { + return it->second; + } + SDL_Log("Asset %s not found", name.c_str()); + return nullptr; +} + +void AssetManager::unloadAsset(const std::string& name) { + auto it = assets.find(name); + if (it != assets.end()) { + SDL_DestroySurface(it->second); + assets.erase(it); + } + SDL_Log("Asset %s not found", name.c_str()); +} + +void AssetManager::clearAssets() { + for (auto& pair : assets) { + SDL_DestroySurface(pair.second); + } + assets.clear(); +} + +TileMap::TileMap(AssetManager& assetManager) + : assetManager(assetManager) { +} + +TileMap::~TileMap() { + clear(); +} + +void TileMap::addTile(const std::string& name, const Tile& tile) { + tiles[name] = tile; +} + +Tile TileMap::getTile(const std::string& name) const { + auto it = tiles.find(name); + if (it != tiles.end()) { + return it->second; + } + SDL_Log("Tile %s not found", name.c_str()); + return Tile(); // Return an empty Tile +} + +SDL_Surface* TileMap::getTileAsset(const std::string& name) const { + auto it = tiles.find(name); + if (it != tiles.end()) { + return assetManager.getAsset(it->second.assetName); + } + SDL_Log("Tile %s not found", name.c_str()); + return nullptr; +} + +Rect TileMap::getTileRect(const std::string& name) const { + auto it = tiles.find(name); + if (it != tiles.end()) { + return Rect(it->second.srcRect); + } + SDL_Log("Tile %s not found", name.c_str()); + return Rect(); // Return an empty Rect +} + +void TileMap::removeTile(const std::string& name) { + auto it = tiles.find(name); + if (it != tiles.end()) { + tiles.erase(it); + } else { + SDL_Log("Tile %s not found", name.c_str()); + } +} + +void TileMap::clear() { + tiles.clear(); +} \ No newline at end of file diff --git a/src/engine.cpp b/src/engine.cpp index 4fcb556..5c2069b 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -1,78 +1,138 @@ -#include "engine.h" +#include "engine.hpp" -Engine::Engine(std::string title, int width, int height) -{ - // convert title to char* - ctitle = title.c_str(); +Engine::Engine(const char* windowTitle, int windowWidth, int windowHeight, int windowFlags) { + // Initialize SDL + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + SDL_Log("SDL could not initialize! SDL_Error: %s", SDL_GetError()); + return; + } - // Initialize SDL and TFF; - TTF_Init(); - SDL_Init(SDL_INIT_EVERYTHING); + // Create the window + window = SDL_CreateWindow(windowTitle, windowWidth, windowHeight, windowFlags); + if (!window) { + SDL_Log("Window could not be created! SDL_Error: %s", SDL_GetError()); + return; + } - // Create window and renderer - window = SDL_CreateWindow( - ctitle, - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - width, - height, - SDL_WINDOW_SHOWN - ); - renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + // Create the renderer + renderer = SDL_CreateRenderer(window, nullptr); + if (!renderer) { + SDL_Log("Renderer could not be created! SDL_Error: %s", SDL_GetError()); + return; + } } -void Engine::initSound() -{ - // Initialize sound - Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048); - soundInitialized = true; +Engine::~Engine() { + // Destroy the asset manager + assets.clearAssets(); + + // Destroy the renderer + if (renderer) { + SDL_DestroyRenderer(renderer); + } + + // Destroy the window + if (window) { + SDL_DestroyWindow(window); + } + + // Quit SDL subsystems + SDL_Quit(); } -void Engine::clear(SDL_Color color) -{ - // Set color +void Engine::clearScreen(const Color& color) { + // Set the draw color SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); - - // Clear screen + + // Clear the screen SDL_RenderClear(renderer); } -void Engine::startFrame() -{ - // Start frame - frameStart = SDL_GetTicks(); -} - -void Engine::render() -{ - // Render screen +void Engine::present() { + // Present the renderer SDL_RenderPresent(renderer); - - if (1000 / targetFrameRate > SDL_GetTicks() - frameStart) - { - SDL_Delay(1000 / targetFrameRate - (SDL_GetTicks() - frameStart)); + + // Calculate frame duration + frameEndTime = SDL_GetTicks(); + frameDuration = frameEndTime - frameStartTime; + // Calculate delay to maintain target FPS + if (targetFPSValue > 0) { + Uint32 targetFrameTime = 1000 / targetFPSValue; + if (frameDuration < targetFrameTime) { + SDL_Delay(targetFrameTime - frameDuration); + } } } -void Engine::update() -{ - // Update input - input.update(); -} - -Engine::~Engine() -{ - // Free renderer and window - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - - // If sound is initialized, quit Mix - if (soundInitialized) - { - Mix_Quit(); +void Engine::draw(SDL_Surface* surface, Rect destRect) { + // Draw a surface to the renderer at the specified destination rectangle + if (!surface) { + SDL_Log("Cannot draw null surface"); + return; } - // Quit SDL and TTF - SDL_Quit(); - TTF_Quit(); + SDL_FRect sdlDestRect = { + static_cast(destRect.x), + static_cast(destRect.y), + static_cast(destRect.width), + static_cast(destRect.height) + }; + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); + if (!texture) { + SDL_Log("Failed to create texture from surface: %s", SDL_GetError()); + return; + } + + SDL_RenderTexture(renderer, texture, nullptr, &sdlDestRect); + SDL_DestroyTexture(texture); } + +void Engine::draw(SDL_Surface* surface, Rect srcRect, Rect destRect) { + // Draw a surface to the renderer with specified source and destination rectangles + if (!surface) { + SDL_Log("Cannot draw null surface"); + return; + } + + SDL_FRect sdlSrcRect = { + static_cast(srcRect.x), + static_cast(srcRect.y), + static_cast(srcRect.width), + static_cast(srcRect.height) + }; + SDL_FRect sdlDestRect = { + static_cast(destRect.x), + static_cast(destRect.y), + static_cast(destRect.width), + static_cast(destRect.height) + }; + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); + if (!texture) { + SDL_Log("Failed to create texture from surface: %s", SDL_GetError()); + return; + } + + SDL_RenderTexture(renderer, texture, &sdlSrcRect, &sdlDestRect); + SDL_DestroyTexture(texture); +} + +void Engine::draw(Tile tile, Rect destRect) { + // Draw a tile to the renderer at the specified destination rectangle + SDL_Surface* surface = assets.getAsset(tile.assetName); + if (!surface) { + SDL_Log("Tile asset %s not found", tile.assetName.c_str()); + return; + } + + draw(surface, tile.srcRect, destRect); +} + +void Engine::startFrame() { + // Start the frame timer + frameStartTime = SDL_GetTicks(); +} + +void Engine::targetFPS(int fps) { + // Set the target frames per second + targetFPSValue = fps; +} \ No newline at end of file diff --git a/src/example-game.cpp b/src/example-game.cpp deleted file mode 100644 index 19fc64c..0000000 --- a/src/example-game.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// Includes -#include "engine.h" - -// Variables -SDL_Surface* assets; -Music* music; -Engine *engine; - -// Functions -// Quit function -void quit() -{ - // Free assets - SDL_FreeSurface(assets); - delete engine; - // Exit program - exit(0); -} - -// Main function -int main(int argc, char* argv[]) -{ - engine = new Engine("Game", 800, 600); - engine->initSound(); - engine->targetFrameRate = 60; - - // Load assets - assets = SDL_LoadBMP("example_assets/sprites.bmp"); - music = new Music("example_assets/crystal_zone.mp3"); - - // frame map - std::map idle = { - { "idle", { 76, 456, 68, 128 } }, - }; - - std::map front = { - { "front", { 4, 456, 68, 125 } }, - { "front2", { 76, 456, 68, 128 } }, - { "front3", { 148, 456, 68, 125 } } - }; - - std::map back = { - { "back", { 662, 456, 68, 125 } }, - { "back2", { 734, 456, 68, 128 } }, - { "back3", { 806, 456, 68, 125 } } - }; - - std::map walk = { - { "walk", { 220, 456, 68, 124 } }, - { "walk2", { 292, 456, 73, 127 } }, - { "walk3", { 369, 456, 68, 124 } } - }; - - std::map test = { - { "test", { 4, 2095, 68, 59 } }, - { "test2", { 76, 2095, 68, 59 } } - }; - - // Create sprite and animation - Sprite* sprite = new Sprite(engine->renderer, assets); - AnimationManager* animations = new AnimationManager(sprite); - animations->addAnimation("idle", idle, 10, false, true, false, false); - animations->addAnimation("walk_left", walk, 10, false, true, true, false); - animations->addAnimation("walk_right", walk, 10, true, true, true, false); - animations->addAnimation("walk_back", back, 10, false, true, true, false); - animations->addAnimation("walk_front", front, 10, false, true, true, true); - animations->addAnimation("test", test, 20, false, true, false, false); - animations->setAnimation("idle"); - - // Play music - music->play(); - - // Main loop - while (true) - { - // Start time - engine->startFrame(); - - // Update input - engine->update(); - - // Exit if window s closed - if (engine->input.exit || engine->input.activeKeys[SDLK_ESCAPE]) - { - quit(); - } - - if (engine->input.activeKeys[SDLK_d] || engine->input.controllers[0].buttons[CBUTTONS::DPAD_RIGHT] || engine->input.controllers[0].leftStickX > 8000) - { - animations->setAnimation("walk_right"); - sprite->move({ 1, 0 }); - } - if (engine->input.activeKeys[SDLK_a] || engine->input.controllers[0].buttons[CBUTTONS::DPAD_LEFT] || engine->input.controllers[0].leftStickX < -8000) - - { - animations->setAnimation("walk_left"); - sprite->move({ -1, 0 }); - } - if (engine->input.activeKeys[SDLK_w] || engine->input.controllers[0].buttons[CBUTTONS::DPAD_UP] || engine->input.controllers[0].leftStickY < -8000) - { - animations->setAnimation("walk_back"); - sprite->move({ 0, -1 }); - } - if (engine->input.activeKeys[SDLK_s] || engine->input.controllers[0].buttons[CBUTTONS::DPAD_DOWN] || engine->input.controllers[0].leftStickY > 8000) - { - animations->setAnimation("walk_front"); - sprite->move({ 0, 1 }); - } - if (engine->input.activeKeys[SDLK_SPACE] || engine->input.controllers[0].buttons[CBUTTONS::A]) - { - animations->playAnimation("test"); - } - if (!engine->input.keysPressed({ SDLK_w, SDLK_a, SDLK_s, SDLK_d }) && engine->input.controllers[0].leftStickX < 8000 && engine->input.controllers[0].leftStickX > -8000 && - engine->input.controllers[0].leftStickY < 8000 && engine->input.controllers[0].leftStickY > -8000 && - !engine->input.controllerPressed({ CBUTTONS::DPAD_UP, CBUTTONS::DPAD_DOWN, CBUTTONS::DPAD_LEFT, CBUTTONS::DPAD_RIGHT })) - { - animations->setAnimation("idle"); - } - - // Update animation - animations->update(); - - // Clear screen - engine->clear({ 100, 150, 255, 255 }); - - // Draw sprite - animations->draw(engine->renderer); - - // Update screen - engine->render(); - } - return 0; -} \ No newline at end of file diff --git a/src/input.cpp b/src/input.cpp index 662a97c..435458d 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1,78 +1,112 @@ -#include "input.h" +#include "input.hpp" + +Input::Input() { -Input::Input() -{ - SDL_Init(SDL_INIT_JOYSTICK); - exit = false; } -void Input::update() -{ - while (SDL_PollEvent(&event)) - { - switch (event.type) - { - case SDL_CONTROLLERBUTTONDOWN: - controllers[event.cbutton.which].buttons[event.cbutton.button] = true; - break; - case SDL_CONTROLLERBUTTONUP: - controllers[event.cbutton.which].buttons[event.cbutton.button] = false; - break; - case SDL_MOUSEBUTTONDOWN: - mouse.buttons[event.button.button] = true; - break; - case SDL_MOUSEBUTTONUP: - mouse.buttons[event.button.button] = false; - break; - case SDL_KEYDOWN: - activeKeys[event.key.keysym.sym] = true; - break; - case SDL_KEYUP: - activeKeys.erase(event.key.keysym.sym); - break; - case SDL_CONTROLLERDEVICEADDED: - controllers[SDL_NumJoysticks() - 1] = { - SDL_GameControllerOpen(SDL_NumJoysticks() - 1), - SDL_GameControllerName(SDL_GameControllerOpen(SDL_NumJoysticks() - 1)) - }; - printf("Controller added: %s\n", controllers[SDL_NumJoysticks() - 1].name.c_str()); - break; - case SDL_QUIT: - exit = true; - break; +Input::~Input() { + +} + +void Input::handleEvents() { + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_QUIT: + quitRequestedFlag = true; + break; + case SDL_EVENT_KEY_DOWN: + if (keyStates[event.key.scancode] == false) { + keyFallingEdgeStates[event.key.scancode] = true; + } + keyStates[event.key.scancode] = true; + break; + case SDL_EVENT_KEY_UP: + keyStates[event.key.scancode] = false; + keyFallingEdgeStates[event.key.scancode] = false; + break; + case SDL_EVENT_MOUSE_BUTTON_DOWN: + if (mouseButtonStates[event.button.button] == false) { + mouseButtonFallingEdgeStates[event.button.button] = true; + } + mouseButtonStates[event.button.button] = true; + break; + case SDL_EVENT_MOUSE_BUTTON_UP: + mouseButtonStates[event.button.button] = false; + mouseButtonFallingEdgeStates[event.button.button] = false; + break; + case SDL_EVENT_MOUSE_MOTION: + mousePosition.x = static_cast(event.motion.x); + mousePosition.y = static_cast(event.motion.y); + break; } - for (auto& controller : controllers) - { - controller.second.leftStickX = SDL_GameControllerGetAxis(controller.second.controller, SDL_CONTROLLER_AXIS_LEFTX); - controller.second.leftStickY = SDL_GameControllerGetAxis(controller.second.controller, SDL_CONTROLLER_AXIS_LEFTY); - controller.second.rightStickX = SDL_GameControllerGetAxis(controller.second.controller, SDL_CONTROLLER_AXIS_RIGHTX); - controller.second.rightStickY = SDL_GameControllerGetAxis(controller.second.controller, SDL_CONTROLLER_AXIS_RIGHTY); - controller.second.leftTrigger = SDL_GameControllerGetAxis(controller.second.controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT); - controller.second.rightTrigger = SDL_GameControllerGetAxis(controller.second.controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); - } - SDL_GetMouseState(&mouse.x, &mouse.y); - mouse.wheel = event.wheel.y; } } -bool Input::keysPressed(std::vector keys) -{ - for (auto key : keys) - { - if (activeKeys[key]) - { +bool Input::quitRequested() const { + return quitRequestedFlag; +} + +bool Input::isKeyPressed(SDL_Scancode key) { + auto it = keyStates.find(key); + return it != keyStates.end() && it->second; +} + +bool Input::isKeyPressed(std::vector keys) { + for (const auto& key : keys) { + if (isKeyPressed(key)) { return true; } } return false; } -bool Input::controllerPressed(std::vector buttons, int controller) -{ - for (auto button : buttons) - { - if (controllers[controller].buttons[button]) - { +bool Input::isMouseButtonPressed(Uint8 button) { + auto it = mouseButtonStates.find(button); + return it != mouseButtonStates.end() && it->second; +} + +bool Input::isMouseButtonPressed(std::vector buttons) { + for (const auto& button : buttons) { + if (isMouseButtonPressed(button)) { + return true; + } + } + return false; +} + +Vector2 Input::getMousePosition() const { + return mousePosition; +} + +bool Input::hasKeyBeenPressed(SDL_Scancode key) { + auto it = keyFallingEdgeStates.find(key); + if (it != keyFallingEdgeStates.end() && it->second) { + keyFallingEdgeStates[key] = false; + return true; + } + return false; +} + +bool Input::hasKeyBeenPressed(std::vector keys) { + for (const auto& key : keys) { + if (hasKeyBeenPressed(key)) { + return true; + } + } + return false; +} + +bool Input::hasMouseButtonBeenPressed(Uint8 button) { + auto it = mouseButtonFallingEdgeStates.find(button); + if (it != mouseButtonFallingEdgeStates.end() && it->second) { + mouseButtonFallingEdgeStates[button] = false; + return true; + } + return false; +} +bool Input::hasMouseButtonBeenPressed(std::vector buttons) { + for (const auto& button : buttons) { + if (hasMouseButtonBeenPressed(button)) { return true; } } diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..854360e --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,57 @@ +#include "engine.hpp" + +// Main function +int main(int argc, char* argv[]) { + // Create the engine with a window title and dimensions + Engine engine("SDL3 Game Engine", 800, 600); + TileMap tileMap(engine.assets); + + // Load bird asset + engine.assets.load("bird", "assets/bird.png"); + tileMap.addTile("bird0", Tile{{0, 0, 34, 24}, "bird"}); + tileMap.addTile("bird1", Tile{{34, 0, 34, 24}, "bird"}); + tileMap.addTile("bird2", Tile{{68, 0, 34, 24}, "bird"}); + + int currentBirdIndex = 0; + + // bird position + Vector2 birdPosition(0, 0); + + while (!engine.input.quitRequested()) { + // Start a new frame + engine.startFrame(); + + // Handle input events + engine.input.handleEvents(); + + // check for movement keys + if (engine.input.isKeyPressed(SDL_SCANCODE_D)) { + birdPosition.x += 1; // Move right + } + if (engine.input.isKeyPressed(SDL_SCANCODE_A)) { + birdPosition.x -= 1; // Move left + } + if (engine.input.isKeyPressed(SDL_SCANCODE_W)) { + birdPosition.y -= 1; // Move up + } + if (engine.input.isKeyPressed(SDL_SCANCODE_S)) { + birdPosition.y += 1; // Move down + } + + // Change bird sprite on space key press + if (engine.input.hasKeyBeenPressed(SDL_SCANCODE_SPACE)) { + currentBirdIndex = (currentBirdIndex + 1) % 3; // Cycle through bird sprites + } + + // Clear the screen with a color + engine.clearScreen({0, 0, 0}); // Black color + + // Draw the bird asset at a specific position + engine.draw(tileMap.getTile("bird" + std::to_string(currentBirdIndex)), birdPosition); + + // Present the rendered frame + engine.present(); + } + + return 0; +} \ No newline at end of file diff --git a/src/sound.cpp b/src/sound.cpp deleted file mode 100644 index 6232c1c..0000000 --- a/src/sound.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "sound.h" - -Music::Music(const char* path) -{ - music = Mix_LoadMUS(path); -} - -void Music::play(int loops) -{ - Mix_PlayMusic(music, loops); -} - -void Music::pause() -{ - Mix_PauseMusic(); -} - -void Music::stop() -{ - Mix_HaltMusic(); -} - -void Music::resume() -{ - Mix_ResumeMusic(); -} - -void Music::setVolume(int volume) -{ - Mix_VolumeMusic(volume); -} - -Music::~Music() -{ - Mix_FreeMusic(music); -} - -Effect::Effect(const char* path) -{ - effect = Mix_LoadWAV(path); -} - -void Effect::play(int loops, int channel) -{ - Mix_PlayChannel(channel, effect, loops); -} - -void Effect::setVolume(int volume) -{ - Mix_VolumeChunk(effect, volume); -} - -Effect::~Effect() -{ - Mix_FreeChunk(effect); -} \ No newline at end of file diff --git a/src/sprite.cpp b/src/sprite.cpp deleted file mode 100644 index 2030fb1..0000000 --- a/src/sprite.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "sprite.h" - -Sprite::Sprite(SDL_Renderer *renderer, SDL_Surface *surface) -{ - Texture = SDL_CreateTextureFromSurface(renderer, surface); - dstrect = { 0, 0, surface->w, surface->h }; -} - -Sprite::~Sprite() -{ - SDL_DestroyTexture(Texture); -} - -void Sprite::draw(SDL_Renderer* renderer, std::string frame, bool autoRect, bool flip) - { - if (frames.find(frame) == frames.end()) - { - printf("Frame not found\n"); - return; - } - if (autoRect) - { - dstrect = { dstrect.x, dstrect.y, frames[frame].w, frames[frame].h }; - } - SDL_SetTextureAlphaMod(Texture, 255); - dstrect.w *= scale.x; - dstrect.h *= scale.y; - SDL_RenderCopyEx(renderer, Texture, &frames[frame], &dstrect, angle, NULL, flip ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE); -} - -void Sprite::addFrame(std::string name, SDL_Rect rect) -{ - frames[name] = rect; -} - -void Sprite::setPos(int_vec2 position) -{ - dstrect.x = position.x; - dstrect.y = position.y; -} - -void Sprite::move(int_vec2 position) -{ - dstrect.x += position.x; - dstrect.y += position.y; -} - -int_vec2 Sprite::getSize(std::string frame) -{ - size = { frames[frame].w, frames[frame].h }; - return size; -} - -void Sprite::autorect(std::string frame) -{ - dstrect = { dstrect.x, dstrect.y, frames[frame].w, frames[frame].h }; -} - - -Animation::Animation(std::map frames, Sprite* sprite, int frameDuration) -{ - this->sprite = sprite; - this->frameDuration = frameDuration; - - for (auto const& x : frames) - { - this->frames.push_back(x.first); - this->sprite->addFrame(x.first, x.second); - } - currentFrameName = this->frames[0]; -} - -void Animation::reset() -{ - frameIndex = 0; - frameDirection = 1; - currentFrameName = frames[0]; - end = false; -} - -void Animation::update() -{ - frameCounter++; - end = false; - if (frameCounter >= frameDuration) - { - frameCounter = 0; - if (pingpong) - { - frameIndex += frameDirection; - if (frameIndex >= frames.size() - 1 || frameIndex <= 0) - { - if (!loop) - { - frameDirection = 0; - end = true; - } else { - frameDirection *= -1; - } - } - } - else - { - frameIndex++; - if (frameIndex >= frames.size()) - { - if (!loop) - { - frameIndex = frames.size() - 1; - end = true; - } else { - frameIndex = 0; - } - } - } - - - currentFrameName = frames[frameIndex]; - } -} - -void Animation::draw(SDL_Renderer* renderer, bool autoRect, bool flip) -{ - sprite->draw(renderer, currentFrameName, autoRect, flip); -} - -AnimationManager::AnimationManager(Sprite* sprite) -{ - this->sprite = sprite; -} - -void AnimationManager::addAnimation(std::string name, std::map frames, int frameDuration, bool flip, bool autoRect, bool loop, bool pingpong) -{ - animations[name] = new Animation(frames, sprite, frameDuration); - this->flip[name] = flip; - this->autoRect[name] = autoRect; - animations[name]->loop = loop; - animations[name]->pingpong = pingpong; -} - -void AnimationManager::setAnimation(std::string name) -{ - if (!playing) { - currentAnimation = name; - } -} - -void AnimationManager::update() -{ - animations[currentAnimation]->update(); - if (playing && animations[currentAnimation]->end) - { - playing = false; - } -} - -void AnimationManager::draw(SDL_Renderer* renderer) -{ - animations[currentAnimation]->draw(renderer, autoRect[currentAnimation], flip[currentAnimation]); -} - -void AnimationManager::playAnimation(std::string name) -{ - if (!playing) { - animations[name]->loop = false; - animations[name]->reset(); - setAnimation(name); - playing = true; - } -} \ No newline at end of file diff --git a/src/text.cpp b/src/text.cpp deleted file mode 100644 index 71d8cdd..0000000 --- a/src/text.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "text.h" - -Text::Text(TTF_Font* font, SDL_Color color) -{ - this->font = font; - this->color = color; -} - -void Text::draw(SDL_Renderer* renderer, std::string text, int x, int y) -{ - surface = TTF_RenderText_Solid(font, text.c_str(), color); - texture = SDL_CreateTextureFromSurface(renderer, surface); - rect = { x, y, surface->w, surface->h }; - SDL_RenderCopy(renderer, texture, NULL, &rect); - SDL_FreeSurface(surface); - SDL_DestroyTexture(texture); -} \ No newline at end of file