From 3f61cd59ba51495253d720acde7fdea38f7038e3 Mon Sep 17 00:00:00 2001 From: zvon Date: Sun, 26 Jul 2020 22:37:20 +0200 Subject: [PATCH] Working, terrible code, but working --- main.cpp | 156 ++++++++++++++++++ sdlpp.cpp | 107 +++++++++++++ sdlpp.hpp | 461 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 724 insertions(+) create mode 100644 main.cpp create mode 100644 sdlpp.cpp create mode 100644 sdlpp.hpp diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..736130f --- /dev/null +++ b/main.cpp @@ -0,0 +1,156 @@ +#include "sdlpp.hpp" +#include +#include +#include + +#define PLAYER_ID 0x00000001 +#define FINALLY_ID 0x00000002 + +std::shared_ptr player; +std::vector> bgtextures; +bool quit = false; + +void addStuff(SDLPP::Scene &scene, std::shared_ptr &r) { + auto y = std::make_shared(0.4,0.2,0.25,0.4, r); + y->addCollision(SDLPP::Rect(0,0,1,1)); + y->setTexture("6.png"); + y->setId(FINALLY_ID); + scene.addObject(y); + auto x = std::make_shared(0,0,0.2,0.2, r); + x->addCollision(SDLPP::Rect(0,0,1,1)); + x->setTexture("5.png"); + x->setId(PLAYER_ID); + scene.addObject(x); + player = x; +} + +void quitGame() { + std::cout << "Quitting!" << std::endl; + quit = true; +} + +void handleKeyDown(SDL_Keycode key, SDLPP::Scene &scene) { + switch(key) { + case SDLK_UP: + scene.setBackground(bgtextures[0]); + break; + case SDLK_DOWN: + scene.setBackground(bgtextures[1]); + break; + case SDLK_RIGHT: + scene.setBackground(bgtextures[2]); + break; + case SDLK_LEFT: + scene.setBackground(bgtextures[3]); + break; + case SDLK_ESCAPE: + quitGame(); + break; + case SDLK_a: + player->addMovement(-1,0); + break; + case SDLK_d: + player->addMovement(1,0); + break; + case SDLK_w: + player->addMovement(0,-1); + break; + case SDLK_s: + player->addMovement(0,1); + break; + default: + scene.setBackground(bgtextures[4]); + break; + } +} + +void handleKeyUp(SDL_Keycode key) { + switch(key) { + case SDLK_a: + player->addMovement(1,0); + break; + case SDLK_d: + player->addMovement(-1,0); + break; + case SDLK_w: + player->addMovement(0,1); + break; + case SDLK_s: + player->addMovement(0,-1); + default: + break; + } +} + +void pollEvents(SDLPP::Scene &scene) { + SDL_Event event; + while( SDL_PollEvent( &event ) != 0 ) { + switch(event.type) { + case SDL_QUIT: + quitGame(); + break; + case SDL_KEYDOWN: + if( !event.key.repeat ) + handleKeyDown(event.key.keysym.sym, scene); + break; + case SDL_KEYUP: + handleKeyUp(event.key.keysym.sym); + break; + case SDL_WINDOWEVENT: + if(event.window.event == SDL_WINDOWEVENT_RESIZED) + scene.updateSizeAndPosition(); + default: + break; + } + } +} + +void doInput(std::shared_ptr scene) { + FPSmanager gFPS; + SDL_initFramerate(&gFPS); + SDL_setFramerate(&gFPS, 200); + int base = SDL_GetTicks(); + int frames = 0; + while(!quit) { + SDL_framerateDelay(&gFPS); + pollEvents(*scene); + scene->movement(); + for( auto &x : scene->getCollisions(*player) ) { + if( x->getId() == FINALLY_ID ) { + std::cout << "Finally!" << std::endl; + } + } + frames++; + if(SDL_GetTicks() - base >= 1000) { + base = SDL_GetTicks(); + printf("FPS: %d\n", frames); + frames = 0; + } + } +} + +int main() { + SDLPP::init(); + SDLPP::Window w("Oh yeah, boi!"); + auto renderer = std::make_shared(w); + auto main_scene = std::make_shared(renderer); + bgtextures.push_back(std::make_shared(renderer, "1.bmp")); + bgtextures.push_back(std::make_shared(renderer, "2.bmp")); + bgtextures.push_back(std::make_shared(renderer, "3.png")); + bgtextures.push_back(std::make_shared(renderer, "4.png")); + bgtextures.push_back(std::make_shared(renderer, "test.bmp")); + main_scene->setBackground(bgtextures[0]); + addStuff(*main_scene, renderer); + player->setMovementSpeed(0.3); + + FPSmanager gFPS; + SDL_initFramerate(&gFPS); + SDL_setFramerate(&gFPS, 60); + std::thread inputThread(doInput, main_scene); + while( !quit ) { + SDL_framerateDelay(&gFPS); + main_scene->renderScene(); + main_scene->presentScene(); + } + inputThread.join(); +} diff --git a/sdlpp.cpp b/sdlpp.cpp new file mode 100644 index 0000000..938b296 --- /dev/null +++ b/sdlpp.cpp @@ -0,0 +1,107 @@ +#include "sdlpp.hpp" + +#include +#include + +bool SDLPP::init() { + if( SDL_Init(SDL_INIT_VIDEO) < 0 ) { + std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; + return false; + } + if( IMG_Init( IMG_INIT_PNG ) != IMG_INIT_PNG ) { + std::cerr << "SDL_image could not initialize! SDL_image Error: " << IMG_GetError() << std::endl; + return false; + } + return true; +} + +bool SDLPP::init(uint32_t SDL_OPTIONS) { + if( SDL_Init(SDL_OPTIONS) < 0 ) { + std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; + return false; + } + if( IMG_Init( IMG_INIT_PNG ) != IMG_INIT_PNG ) { + std::cerr << "SDL_image could not initialize! SDL_image Error: " << IMG_GetError() << std::endl; + return false; + } + return true; +} + +bool SDLPP::init(uint32_t SDL_OPTIONS, int IMAGE_OPTIONS) { + if( SDL_Init(SDL_OPTIONS) < 0 ) { + std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; + return false; + } + if( IMG_Init( IMAGE_OPTIONS ) != IMAGE_OPTIONS ) { + std::cerr << "SDL_image could not initialize! SDL_image Error: " << IMG_GetError() << std::endl; + return false; + } + return true; +} + +void SDLPP::CircleRender::render() { + std::cout << "I'm a circle, look at me go!" << std::endl << "My dimensions are: [" << x_ << ", " << y_ << "], radius: " << rad_ << std::endl; +} + +// only rectangles for now +bool intersects(const SDLPP::CollisionPolygon &p1, const SDLPP::CollisionPolygon &p2) { + return !(p1.rightmost() < p2.leftmost() || p2.rightmost() < p1.leftmost() || + p1.topmost() > p2.bottommost() || p2.topmost() > p1.bottommost()); +} + +bool infinityIntersection(const SDLPP::CollisionPolygon &infinite, const SDLPP::CollisionPolygon &other) { + int ileft = infinite.leftmost(); + int iright = infinite.rightmost(); + int itop = infinite.topmost(); + int ibottom = infinite.bottommost(); + bool ret = ileft != -1 && ileft <= other.rightmost(); + ret |= iright != -1 && iright >= other.leftmost(); + ret |= itop != -1 && itop <= other.bottommost(); + ret |= ibottom != -1 && ibottom >= other.topmost(); + ret |= ileft == -1 && iright == -1 && itop == -1 && ibottom == -1; + return ret; +} + +bool SDLPP::Rect::colidesWith(const SDLPP::CollisionPolygon &other) const { + if(other.isCircle()) { + return other.colidesWith(*this); + } + if(other.isInfinite()) { + return infinityIntersection(other, *this); + } + return intersects(*this, other); +} + +bool SDLPP::Circle::colidesWith(const SDLPP::CollisionPolygon &other) const { + if(other.isCircle()) { + int otherRad = (other.rightmost() - other.leftmost())/2; + int thisRad = getRadius(); + int totalDist = otherRad + thisRad; + int xdiff = other.leftmost() + otherRad - (leftmost() + thisRad); + int ydiff = other.topmost() + otherRad - (topmost() + thisRad); + return (xdiff*xdiff + ydiff*ydiff) <= totalDist * totalDist; + } else if (other.isInfinite()) { + return infinityIntersection(other, *this); + } + int rad = rad_; + int centerx = getX(); + int centery = getY(); + if(other.topmost() <= centery && other.bottommost() >= centery) { + return other.leftmost() <= rightmost() && other.rightmost() >= leftmost(); + } else if (other.leftmost() <= centerx && other.rightmost() >= centerx) { + return other.topmost() <= bottommost() && other.bottommost() >= topmost(); + } + int pointx=0, pointy=0; + if(centerx > other.rightmost()) { + pointx = other.rightmost(); + } else { + pointx = other.leftmost(); + } + if(centery < other.topmost()) { + pointy = other.topmost(); + } else { + pointy = other.bottommost(); + } + int distancesquared = (pointx - centerx)*(pointx - centerx) + (pointy - centery)*(pointy-centery); + return distancesquared <= rad*rad; +} diff --git a/sdlpp.hpp b/sdlpp.hpp new file mode 100644 index 0000000..629ca89 --- /dev/null +++ b/sdlpp.hpp @@ -0,0 +1,461 @@ +#ifndef SDLPP_HPP +#define SDLPP_HPP + +#include +#include +#include +#include +#include + +namespace SDLPP { + +class Window { +public: + Window() : Window("SDL Window", 640, 480, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED) { + } + Window(const std::string &window_name) : Window(window_name, 640, 480, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED) { + } + Window(const std::string &window_name, uint32_t width, uint32_t height) : Window(window_name, width, height, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED) {} + Window(const std::string &window_name, uint32_t width, uint32_t height, + uint32_t posx, uint32_t posy) { + window = SDL_CreateWindow(window_name.c_str(), posx, posy, width, height, SDL_WINDOW_SHOWN); + if( window == NULL ) { + std::cerr << "SDL could not create a window! SDL_Error: " << SDL_GetError() << std::endl; + throw "Couldn't create window"; + } + } + ~Window() { + SDL_DestroyWindow(window); + } + SDL_Window *getWindowPtr() { + return window; + } +private: + SDL_Window *window = NULL; +}; + +class Renderer { +public: + Renderer() = delete; + Renderer(Window &window) { + renderer = SDL_CreateRenderer(window.getWindowPtr(), -1, SDL_RENDERER_ACCELERATED); + if(renderer == NULL ) { + std::cerr << "SDL could not create a renderer! SDL_Error: " << SDL_GetError(); + throw "Couldn't create renderer"; + } + SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); + } + ~Renderer() { + SDL_DestroyRenderer(renderer); + } + SDL_Renderer *getRendererPtr() { + return renderer; + } + std::pair getDimensions() { + int width = 0, height = 0; + SDL_GetRendererOutputSize(renderer, &width, &height); + return {width, height}; + } +private: + SDL_Renderer *renderer = NULL; +}; + +class Texture { +public: + Texture() = delete; + Texture(std::shared_ptr &renderer, const std::string &img_path) : Texture(renderer, img_path, "") {} + Texture(std::shared_ptr &renderer, const std::string &img_path, const std::string &color_key) { + SDL_Surface *surface = IMG_Load(img_path.c_str()); + if( surface == NULL ) { + std::cerr << "Unable to load image '" << img_path << "': IMG Error: " << IMG_GetError() << std::endl; + throw "IMG_Load error"; + } + if( !color_key.empty() ) { + auto colors = getColorsHEX(color_key); + SDL_SetColorKey(surface, SDL_TRUE, SDL_MapRGB(surface->format, std::get<0>(colors), std::get<1>(colors), std::get<2>(colors))); + } + texture = SDL_CreateTextureFromSurface(renderer->getRendererPtr(), surface); + if( texture == NULL ) { + std::cerr << "Unable to create texture from '" << img_path << "'! SDL Error: " << SDL_GetError() << std::endl; + throw "Texture error"; + } + SDL_FreeSurface(surface); + } + ~Texture() { + SDL_DestroyTexture(texture); + } + SDL_Texture *getTexturePtr() { + return texture; + } +private: + int hex2num(char c) { + if(c <= '9') + return c - '0'; + switch(c) { + case 'a': + case 'A': + return 10; + case 'b': + case 'B': + return 11; + case 'c': + case 'C': + return 12; + case 'd': + case 'D': + return 13; + case 'e': + case 'E': + return 14; + default: + return 15; + } + } + + std::tuple getColorsHEX(const std::string &color) { + int red = 0, green = 0, blue = 0; + const char *color_ptr = color.c_str(); + if(color_ptr[0] == '#') + color_ptr++; + red = hex2num(color_ptr[0])*16 + hex2num(color_ptr[1]); + green = hex2num(color_ptr[2])*16 + hex2num(color_ptr[3]); + blue = hex2num(color_ptr[4])*16 + hex2num(color_ptr[5]); + return {red, green, blue}; + } + + SDL_Texture *texture = NULL; +}; + +class CollisionPolygon { +public: + CollisionPolygon(double x, double y) { + original_x = x; + original_y = y; + position_x = 0; + position_y = 0; + } + virtual ~CollisionPolygon() {} + virtual bool colidesWith(const CollisionPolygon &other) const = 0; + virtual bool isCircle() const = 0; + virtual bool isInfinite() const = 0; + virtual int topmost() const = 0; + virtual int bottommost() const = 0; + virtual int leftmost() const = 0; + virtual int rightmost() const = 0; + virtual void updateCollision(int x, int y, int w, int h) { + position_x = original_x * w + x; + position_y = original_y * h + y; + } + int getX() const { + return position_x; + } + int getY() const { + return position_y; + } +protected: + double original_x; + double original_y; + int position_x; + int position_y; +}; + +class RenderObject { +public: + RenderObject(std::shared_ptr &r) : renderer(r) {} + virtual ~RenderObject() {} + virtual void render() = 0; + virtual int leftmost() = 0; + virtual int topmost() = 0; + virtual int collisionPushX() = 0; + virtual int collisionPushY() = 0; + virtual int collisionWidth() = 0; + virtual int collisionHeight() = 0; + bool colidesWith(const RenderObject &other) const { + if(!hasCollisions() || !other.hasCollisions()) { + return false; + } + for( const auto &x : collisions ) { + for( const auto &y : other.getCollisions() ) { + if(x->colidesWith(*y)) + return true; + } + } + return false; + } + template + void addCollision(const T &p) { + collisions.push_back(std::make_shared(p)); + collisions.back()->updateCollision(collisionPushX(), collisionPushY(), collisionWidth(), collisionHeight()); + } + bool hasCollisions() const { + return !collisions.empty(); + } + const std::vector> &getCollisions() const { + return collisions; + } + void setTexture(std::shared_ptr &t) { + texture = t; + } + void setTexture(const std::string &img_path) { + texture = std::make_shared(renderer, img_path); + } + // per second, relative to window width + void setMovementSpeed(double speed) { + movementSpeed = speed; + } + void addMovement(int x, int y) { + movementDirection.first += x; + movementDirection.second += y; + } + void clearColided() { + colidedWith.clear(); + } + void addColided(std::shared_ptr &obj) { + colidedWith.push_back(obj); + } + std::vector> &getColidedWith() { + return colidedWith; + } + void setId(uint64_t input_id) { + id = input_id; + } + uint64_t getId() { + return id; + } + virtual void move(int ticks) = 0; + virtual void updateSizeAndPosition() = 0; +protected: + std::vector> collisions; + std::shared_ptr texture; + std::shared_ptr renderer; + double movementSpeed; + std::pair movementDirection; + std::vector> colidedWith; + uint64_t id; +}; + +class Scene { +public: + Scene(std::shared_ptr &r) : renderer(r) { + SDL_SetRenderDrawColor(renderer->getRendererPtr(), 0xFF, 0xFF, 0xFF, 0xFF); + prev_ticks = SDL_GetTicks(); + } + template + void addObject(std::shared_ptr &obj) { + renderObjects.push_back(obj); + if(obj->hasCollisions()) { + collisionObjects.push_back(obj); + } + } + std::shared_ptr getObject(int index) { + return renderObjects[index]; + } + void movement() { + int now_ticks = SDL_GetTicks(); + for( const auto &x : renderObjects ) { + x->move(now_ticks - prev_ticks); + } + prev_ticks = now_ticks; + } + std::vector> getCollisions(RenderObject &r) { + std::vector> ret{}; + for(const auto &x : collisionObjects) { + if(x->colidesWith(r)) { + ret.push_back(x); + } + } + return ret; + } + void renderScene() { + SDL_RenderClear(renderer->getRendererPtr()); + SDL_RenderCopy(renderer->getRendererPtr(), background->getTexturePtr(), NULL, NULL); + for( const auto &x : renderObjects ) { + x->render(); + } + } + void presentScene() { + SDL_RenderPresent(renderer->getRendererPtr()); + } + void setBackground(std::shared_ptr bg) { + background = bg; + } + void setBackground(const std::string &img_path) { + background = std::make_shared(renderer, img_path); + } + void updateSizeAndPosition() { + for( auto &x : renderObjects ) { + x->updateSizeAndPosition(); + } + } +private: + std::vector> renderObjects; + std::vector> collisionObjects; + std::shared_ptr renderer; + std::shared_ptr background; + int prev_ticks = 0; +}; + +class RectangleRender : public RenderObject { +public: + RectangleRender() = delete; + virtual ~RectangleRender() {}; + RectangleRender(double x, double y, double w, double h, std::shared_ptr &r) : RenderObject(r) { + auto dimensions = renderer->getDimensions(); + auto smaller_dimension = dimensions.first < dimensions.second ? dimensions.first : dimensions.second; + rect.x = x * dimensions.first; + rect.y = y * dimensions.second; + rect.w = w * smaller_dimension; + rect.h = h * smaller_dimension; + x_ = x; + y_ = y; + w_ = w; + h_ = h; + } + RectangleRender(int x, int y, int w, int h, std::shared_ptr &r, std::shared_ptr &t) : RectangleRender(x, y, w, h, r) { + setTexture(t); + } + RectangleRender(int x, int y, int w, int h, std::shared_ptr &r, const std::string &img_path) : RectangleRender(x,y,w,h,r) { + auto texture = std::make_shared(r, img_path); + setTexture(texture); + } + virtual void render() { + SDL_RenderCopy(renderer->getRendererPtr(), texture->getTexturePtr(), NULL, &rect); + } + virtual void move(int ticks) { + auto dimensions = renderer->getDimensions(); + auto addx = static_cast(movementSpeed * movementDirection.first)*(static_cast(ticks)/1000); + auto addy = static_cast(movementSpeed * movementDirection.second)*(static_cast(ticks)/1000); + x_ += addx; + y_ += addy; + rect.x = x_ * dimensions.first; + rect.y = y_ * dimensions.second; + for( auto &x : collisions ) { + x->updateCollision(collisionPushX(), collisionPushY(), collisionWidth(), collisionHeight()); + } + } + virtual int leftmost() { + return rect.x; + } + virtual int topmost() { + return rect.y; + } + virtual int collisionPushX() { + return rect.x; + } + virtual int collisionPushY() { + return rect.y; + } + virtual int collisionWidth() { + return rect.w; + } + virtual int collisionHeight() { + return rect.h; + } + virtual void updateSizeAndPosition() { + auto dimensions = renderer->getDimensions(); + auto smaller_dimension = dimensions.first < dimensions.second ? dimensions.first : dimensions.second; + rect.x = x_ * dimensions.first; + rect.y = y_ * dimensions.second; + rect.w = w_ * smaller_dimension; + rect.h = h_ * smaller_dimension; + } +private: + double x_; + double y_; + double w_; + double h_; + SDL_Rect rect; +}; + +class CircleRender : public RenderObject { +public: + CircleRender() = delete; + virtual ~CircleRender() {}; + CircleRender(int x, int y, int rad, std::shared_ptr &r) : RenderObject(r) { + x_ = x; + y_ = y; + rad_ = rad; + } + CircleRender(int x, int y, int rad, std::shared_ptr &r, std::shared_ptr &t) : CircleRender(x,y,rad,r) { + setTexture(t); + } + CircleRender(int x, int y, int rad, std::shared_ptr &r, const std::string &img_path) : CircleRender(x,y,rad,r) { + auto texture = std::make_shared(r, img_path); + setTexture(texture); + } + virtual void render(); + virtual int leftmost() { + return x_-rad_; + } + virtual int topmost() { + return y_-rad_; + } + virtual int collisionPushX() { + return x_; + } + virtual int collisionPushY() { + return y_; + } +private: + int x_; + int y_; + int rad_; +}; + +class Rect : public CollisionPolygon { +public: + Rect(int x, int y, int w, int h) : CollisionPolygon(x, y) { + w_ = w; + h_ = h; + } + virtual ~Rect() {} + virtual bool colidesWith(const CollisionPolygon &other) const override; + virtual bool isCircle() const override { return false; } + virtual bool isInfinite() const override { return false; } + virtual int topmost() const override { return getY(); } + virtual int bottommost() const override { return getY() + pixel_h; }; + virtual int leftmost() const override { return getX(); } + virtual int rightmost() const override { return getX() + pixel_w; } + virtual void updateCollision(int x, int y, int w, int h) override { + position_x = original_x * w + x; + position_y = original_y * h + y; + pixel_w = w_ * w; + pixel_h = h_ * h; + } +private: + double w_; + double h_; + int pixel_w; + int pixel_h; +}; + +class Circle : public CollisionPolygon { +public: + Circle(int x, int y, int rad) : CollisionPolygon(x, y) { + rad_ = rad; + } + virtual ~Circle() {} + virtual bool colidesWith(const CollisionPolygon &other) const; + virtual bool isCircle() const { return true; } + virtual bool isInfinite() const { return false; } + virtual int topmost() const { return getY() - rad_; } + virtual int bottommost() const { return getY() + rad_; }; + virtual int leftmost() const { return getX() - rad_; } + virtual int rightmost() const { return getX() + rad_; } +private: + int getRadius() const { + return rad_; + } + int rad_; +}; + +bool init(); +bool init(uint32_t SDL_OPTIONS); +bool init(uint32_t SDL_OPTIONS, int IMAGE_OPTIONS); + +template +void testPolymorphism(T &obj); + +} // end of namespace SDLPP + +#endif