game/sdlpp.hpp

606 lines
20 KiB
C++
Raw Normal View History

2020-07-26 20:37:20 +00:00
#ifndef SDLPP_HPP
#define SDLPP_HPP
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
2020-08-21 18:53:21 +00:00
#include <cmath>
2020-07-26 20:37:20 +00:00
#include <iostream>
#include <limits>
2020-07-26 20:37:20 +00:00
#include <memory>
#include <vector>
namespace SDLPP {
int hex2num(char c);
std::tuple<int, int, int> getColorsHEX(const std::string &color);
2020-07-26 20:37:20 +00:00
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<int, int> getDimensions() const {
2020-07-26 20:37:20 +00:00
int width = 0, height = 0;
SDL_GetRendererOutputSize(renderer, &width, &height);
return {width, height};
}
int getWidth() const {
return getDimensions().first;
}
int getHeight() const {
return getDimensions().second;
}
int getSmallerSide() const {
auto dimensions = getDimensions();
return dimensions.first < dimensions.second ? dimensions.first : dimensions.second;
}
int getLargerSide() const {
auto dimensions = getDimensions();
return dimensions.first > dimensions.second ? dimensions.first : dimensions.second;
}
void setBlendMode(SDL_BlendMode blendMode) {
SDL_SetRenderDrawBlendMode(renderer, blendMode);
}
void setRenderColiders(bool render) {
render_coliders = render;
}
bool getRenderColiders() {
return render_coliders;
}
2020-07-26 20:37:20 +00:00
private:
SDL_Renderer *renderer = NULL;
bool render_coliders = false;
2020-07-26 20:37:20 +00:00
};
class Texture {
public:
Texture() = delete;
Texture(std::shared_ptr<Renderer> &renderer, const std::string &img_path) : Texture(renderer, img_path, "") {}
Texture(std::shared_ptr<Renderer> &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:
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 { return infinite; }
virtual void setInfinite() { infinite = true; }
2020-07-26 20:37:20 +00:00
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;
}
virtual void render(Renderer &renderer, const std::tuple<int,int,int> &color) = 0;
2020-07-26 20:37:20 +00:00
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;
bool infinite = false;
2020-07-26 20:37:20 +00:00
};
class RenderObject {
public:
RenderObject(std::shared_ptr<Renderer> &r) : renderer(r) {}
virtual ~RenderObject() {}
virtual void render() = 0;
virtual int leftmost() = 0;
virtual int topmost() = 0;
virtual int rightmost() = 0;
virtual int bottommost() = 0;
2020-07-26 20:37:20 +00:00
virtual int collisionPushX() = 0;
virtual int collisionPushY() = 0;
virtual int collisionWidth() = 0;
virtual int collisionHeight() = 0;
2020-07-27 18:05:59 +00:00
virtual std::pair<std::pair<double,double>,std::pair<double,double>> getDoubleRect() = 0;
virtual void setPos(double x, double y) = 0;
2020-07-26 20:37:20 +00:00
bool colidesWith(const RenderObject &other) const {
2020-08-21 16:54:42 +00:00
if(!hasCollisions() || !other.hasCollisions() || getHidden() || other.getHidden()) {
2020-07-26 20:37:20 +00:00
return false;
}
for( const auto &x : collisions ) {
for( const auto &y : other.getCollisions() ) {
if(x->colidesWith(*y))
return true;
}
}
return false;
}
template<class T>
void addCollision(const T &p) {
collisions.push_back(std::make_shared<T>(p));
collisions.back()->updateCollision(collisionPushX(), collisionPushY(), collisionWidth(), collisionHeight());
}
bool hasCollisions() const {
return !collisions.empty();
}
const std::vector<std::shared_ptr<CollisionPolygon>> &getCollisions() const {
return collisions;
}
void setTexture(std::shared_ptr<Texture> &t) {
texture = t;
}
void setTexture(const std::string &img_path) {
texture = std::make_shared<Texture>(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<RenderObject> &obj) {
colidedWith.push_back(obj);
}
std::vector<std::shared_ptr<RenderObject>> &getColidedWith() {
return colidedWith;
}
void setId(uint64_t input_id) {
id = input_id;
}
uint64_t getId() {
return id;
}
void setHidden(bool hid) {
hidden = hid;
}
2020-08-21 16:54:42 +00:00
bool getHidden() const {
return hidden;
}
void setColiderColor(const std::string &color) {
colider_color = getColorsHEX(color);
}
2020-07-26 20:37:20 +00:00
virtual void move(int ticks) = 0;
virtual void updateSizeAndPosition() = 0;
virtual SDL_Rect getRect() = 0;
2020-07-26 20:37:20 +00:00
protected:
std::vector<std::shared_ptr<CollisionPolygon>> collisions;
std::shared_ptr<Texture> texture;
std::shared_ptr<Renderer> renderer;
double movementSpeed;
std::pair<int,int> movementDirection;
std::vector<std::shared_ptr<RenderObject>> colidedWith;
uint64_t id;
bool hidden = false;
std::tuple<int,int,int> colider_color = {0x00, 0xFF, 0xFF};
2020-07-26 20:37:20 +00:00
};
class Scene {
public:
Scene(std::shared_ptr<Renderer> &r) : renderer(r) {
SDL_SetRenderDrawColor(renderer->getRendererPtr(), 0xFF, 0xFF, 0xFF, 0xFF);
prev_ticks = SDL_GetTicks();
}
void addObject(const std::shared_ptr<RenderObject> &obj) {
2020-07-26 20:37:20 +00:00
renderObjects.push_back(obj);
if(obj->hasCollisions()) {
collisionObjects.push_back(obj);
}
if(renderObjects.size() == 1) {
leftmost_obj = obj;
rightmost_obj = obj;
} else {
auto rect = obj->getDoubleRect();
auto leftmost_rect = leftmost_obj->getDoubleRect();
if(rect.first.first < leftmost_rect.first.first)
leftmost_obj = obj;
auto rightmost_rect = rightmost_obj->getDoubleRect();
if(rect.first.first + rect.second.first > rightmost_rect.first.first + rightmost_rect.second.first)
rightmost_obj = obj;
}
2020-07-26 20:37:20 +00:00
}
std::shared_ptr<RenderObject> 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<std::shared_ptr<RenderObject>> getCollisions(RenderObject &r) {
2020-08-21 16:54:42 +00:00
if(r.getHidden())
return {};
2020-07-26 20:37:20 +00:00
std::vector<std::shared_ptr<RenderObject>> 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<Texture> bg) {
background = bg;
}
void setBackground(const std::string &img_path) {
background = std::make_shared<Texture>(renderer, img_path);
}
void updateSizeAndPosition() {
for( auto &x : renderObjects ) {
x->updateSizeAndPosition();
for( auto &col : x->getCollisions() ) {
col->updateCollision(x->collisionPushX(), x->collisionPushY(), x->collisionWidth(), x->collisionHeight());
}
2020-07-26 20:37:20 +00:00
}
}
void moveEverything(double x, double y) {
for( auto &obj : renderObjects ) {
auto curPos = obj->getDoubleRect();
obj->setPos( curPos.first.first + x, curPos.first.second + y );
}
}
const std::shared_ptr<RenderObject> &leftmost() {
return leftmost_obj;
}
const std::shared_ptr<RenderObject> &rightmost() {
return rightmost_obj;
}
std::pair<int, int> getDimensions() const {
return renderer->getDimensions();
}
int getWidth() const {
return renderer->getWidth();
}
int getHeight() const {
return renderer->getHeight();
}
Renderer &getRenderer() {
return *renderer;
}
2020-08-21 16:54:42 +00:00
void setPrevTicks(int ticks) {
prev_ticks = ticks;
}
2020-07-26 20:37:20 +00:00
private:
std::vector<std::shared_ptr<RenderObject>> renderObjects;
std::vector<std::shared_ptr<RenderObject>> collisionObjects;
std::shared_ptr<Renderer> renderer;
std::shared_ptr<Texture> background;
int prev_ticks = 0;
std::shared_ptr<RenderObject> leftmost_obj;
std::shared_ptr<RenderObject> rightmost_obj;
2020-07-26 20:37:20 +00:00
};
class RectangleRender : public RenderObject {
public:
RectangleRender() = delete;
virtual ~RectangleRender() {};
RectangleRender(double x, double y, double w, double h, std::shared_ptr<Renderer> &r) : RenderObject(r) {
auto dimension = renderer->getSmallerSide();
rect.x = x * dimension;
rect.y = y * dimension;
rect.w = w * dimension;
rect.h = h * dimension;
2020-07-26 20:37:20 +00:00
x_ = x;
y_ = y;
w_ = w;
h_ = h;
}
RectangleRender(int x, int y, int w, int h, std::shared_ptr<Renderer> &r, std::shared_ptr<Texture> &t) : RectangleRender(x, y, w, h, r) {
setTexture(t);
}
RectangleRender(int x, int y, int w, int h, std::shared_ptr<Renderer> &r, const std::string &img_path) : RectangleRender(x,y,w,h,r) {
auto texture = std::make_shared<Texture>(r, img_path);
setTexture(texture);
}
virtual void render() {
2020-08-21 16:54:42 +00:00
if(texture != NULL && !getHidden())
2020-07-28 16:13:38 +00:00
SDL_RenderCopy(renderer->getRendererPtr(), texture->getTexturePtr(), NULL, &rect);
2020-08-21 16:54:42 +00:00
if(hasCollisions() && renderer->getRenderColiders() && !getHidden()) {
for(const auto &col : getCollisions())
col->render(*renderer, colider_color);
}
2020-07-26 20:37:20 +00:00
}
virtual void move(int ticks) {
auto dimension = renderer->getSmallerSide();
2020-07-26 20:37:20 +00:00
auto addx = static_cast<double>(movementSpeed * movementDirection.first)*(static_cast<double>(ticks)/1000);
auto addy = static_cast<double>(movementSpeed * movementDirection.second)*(static_cast<double>(ticks)/1000);
x_ += addx;
y_ += addy;
rect.x = x_ * dimension;
rect.y = y_ * dimension;
2020-07-26 20:37:20 +00:00
for( auto &x : collisions ) {
x->updateCollision(collisionPushX(), collisionPushY(), collisionWidth(), collisionHeight());
}
}
2020-07-27 18:05:59 +00:00
virtual std::pair<std::pair<double,double>,std::pair<double,double>> getDoubleRect() {
return {{x_,y_}, {w_,h_}};
}
virtual void setPos(double x, double y) {
auto dimension = renderer->getSmallerSide();
2020-07-27 18:05:59 +00:00
x_ = x;
y_ = y;
rect.x = x_ * dimension;
rect.y = y_ * dimension;
2020-07-27 18:05:59 +00:00
}
2020-07-26 20:37:20 +00:00
virtual int leftmost() {
return rect.x;
}
virtual int topmost() {
return rect.y;
}
virtual int rightmost() {
return rect.x + rect.w;
}
virtual int bottommost() {
return rect.y + rect.h;
}
2020-07-26 20:37:20 +00:00
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 dimension = renderer->getSmallerSide();
rect.x = x_ * dimension;
rect.y = y_ * dimension;
rect.w = w_ * dimension;
rect.h = h_ * dimension;
2020-07-26 20:37:20 +00:00
}
virtual SDL_Rect getRect() {
return rect;
}
2020-07-27 18:05:59 +00:00
protected:
2020-07-26 20:37:20 +00:00
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<Renderer> &r) : RenderObject(r) {
x_ = x;
y_ = y;
rad_ = rad;
}
CircleRender(int x, int y, int rad, std::shared_ptr<Renderer> &r, std::shared_ptr<Texture> &t) : CircleRender(x,y,rad,r) {
setTexture(t);
}
CircleRender(int x, int y, int rad, std::shared_ptr<Renderer> &r, const std::string &img_path) : CircleRender(x,y,rad,r) {
auto texture = std::make_shared<Texture>(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(double x, double y, double w, double h) : CollisionPolygon(x, y) {
2020-07-26 20:37:20 +00:00
w_ = w;
h_ = h;
}
virtual ~Rect() {}
virtual bool colidesWith(const CollisionPolygon &other) const override;
virtual bool isCircle() const override { return false; }
virtual int topmost() const override {
return (!isInfinite() || original_y != -1) * getY() + isInfinite() * -1;
}
virtual int bottommost() const override {
return (!isInfinite() || h_ != -1) * (getY() + pixel_h) + isInfinite() * -1;
};
virtual int leftmost() const override {
return (!isInfinite() || original_x != -1) * getX() + isInfinite() * -1;
}
virtual int rightmost() const override {
return (!isInfinite() || w_ != -1) * (getX() + pixel_w) + isInfinite() * -1;
}
2020-07-26 20:37:20 +00:00
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;
}
virtual void render(Renderer &renderer, const std::tuple<int,int,int> &color) override {
auto rect = getRect();
// outline with desired color at 50% opacity
SDL_SetRenderDrawColor(renderer.getRendererPtr(), std::get<0>(color), std::get<1>(color), std::get<2>(color), 0x80);
SDL_RenderDrawRect(renderer.getRendererPtr(), &rect);
// fill with desired color at 25% opacity
SDL_SetRenderDrawColor(renderer.getRendererPtr(), std::get<0>(color), std::get<1>(color), std::get<2>(color), 0x40);
SDL_RenderFillRect(renderer.getRendererPtr(), &rect);
}
2020-07-26 20:37:20 +00:00
private:
SDL_Rect getRect() {
if( !isInfinite() )
return {leftmost(), topmost(), pixel_w, pixel_h};
SDL_Rect r = {0,0,0,0};
if((r.x = leftmost()) == -1)
r.x = 0;
if((r.y = topmost()) == -1)
r.y = 0;
if(rightmost() == -1)
r.w = std::numeric_limits<int>::max();
else
r.w = pixel_w;
if(bottommost() == -1)
r.h = std::numeric_limits<int>::max();
else
r.h = pixel_h;
return r;
}
2020-07-26 20:37:20 +00:00
double w_;
double h_;
int pixel_w;
int pixel_h;
};
class Circle : public CollisionPolygon {
public:
Circle(double x, double y, double rad) : CollisionPolygon(x, y) {
2020-08-21 18:53:21 +00:00
original_rad = rad;
2020-07-26 20:37:20 +00:00
}
virtual ~Circle() {}
2020-08-21 18:53:21 +00:00
virtual bool colidesWith(const CollisionPolygon &other) const override;
virtual bool isCircle() const override { return true; }
virtual int topmost() const override { return getY() - rad_; }
virtual int bottommost() const override { return getY() + rad_; };
virtual int leftmost() const override { return getX() - rad_; }
virtual int rightmost() const override { return getX() + rad_; }
virtual void updateCollision(int x, int y, int w, int h) override {
position_x = original_x * w + x;
position_y = original_y * h + y;
rad_ = original_rad * w;
}
virtual void render(Renderer &renderer, const std::tuple<int,int,int> &color) override {
std::vector<int> rect = {leftmost(), topmost(), rightmost(), bottommost()};
auto center_x = getX();
auto center_y = getY();
auto radsq = rad_ * rad_;
for(int i = rect[0]; i <= rect[2]; i++) {
auto xdiff = center_x - i;
auto xdist = xdiff * xdiff;
auto allowed_rad = sqrt(radsq - xdist);
SDL_SetRenderDrawColor(renderer.getRendererPtr(), std::get<0>(color), std::get<1>(color), std::get<2>(color), 0x40);
SDL_RenderDrawLine(renderer.getRendererPtr(), i, center_y - allowed_rad, i, center_y + allowed_rad);
SDL_SetRenderDrawColor(renderer.getRendererPtr(), std::get<0>(color), std::get<1>(color), std::get<2>(color), 0x80);
SDL_RenderDrawLine(renderer.getRendererPtr(), i, center_y - allowed_rad, i, center_y - allowed_rad + 2);
SDL_RenderDrawLine(renderer.getRendererPtr(), i, center_y + allowed_rad, i, center_y + allowed_rad - 2);
}
SDL_SetRenderDrawColor(renderer.getRendererPtr(), 0xFF, 0, 0, 0xFF);
SDL_RenderDrawLine(renderer.getRendererPtr(), center_x, center_y, center_x + rad_, center_y);
SDL_RenderDrawLine(renderer.getRendererPtr(), center_x, center_y, center_x, center_y + rad_);
SDL_RenderDrawLine(renderer.getRendererPtr(), center_x, center_y, center_x - rad_, center_y);
SDL_RenderDrawLine(renderer.getRendererPtr(), center_x, center_y, center_x, center_y - rad_);
}
2020-07-26 20:37:20 +00:00
private:
int getRadius() const {
return rad_;
}
2020-08-21 18:53:21 +00:00
double original_rad;
2020-07-26 20:37:20 +00:00
int rad_;
};
bool init();
bool init(uint32_t SDL_OPTIONS);
bool init(uint32_t SDL_OPTIONS, int IMAGE_OPTIONS);
template<class T>
void testPolymorphism(T &obj);
} // end of namespace SDLPP
#endif