diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46bb029 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/build/ +/gitignore/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7e170d6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,59 @@ +{ + "C_Cpp.default.includePath": [ + "/usr/include/SDL2/" + ], + "files.associations": { + "exception": "cpp", + "stdexcept": "cpp", + "string": "cpp", + "atomic": "cpp", + "*.tcc": "cpp", + "deque": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "fstream": "cpp", + "limits": "cpp", + "sstream": "cpp", + "system_error": "cpp", + "new": "cpp", + "array": "cpp", + "bit": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "mutex": "cpp", + "ostream": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp" + }, + "C_Cpp.errorSquiggles": "Enabled" +} \ No newline at end of file diff --git a/README.md b/README.md index af7016a..fafe484 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,29 @@ -# xmario +## xSDL +```yaml +package: xSDL +language: c++ +version: 0.1.0 +author: xdrm-brackets +``` + +xSDL is a simple 2D wrapper around the SDL2 to make your life easier. + +### Documentation + +The API aims to be straightforward as possible and tries to take advantage of patterns and principles to achieve its goal. + +> xSDL can be compiled as a dynamic library (_e.g. lib.so_). The include header is located in _include/xSDL.h_. It can also be compiled directly within your application. + +The class `xApplication` is a singleton representing your application, its window, renderer, etc. An `xApplication` features a `xController` that manages user input. + +The interface `xElement` represents any graphical feature to be drawn on the screen. Any number of `xElement` can be pushed to or pulled from the application at any time in order to show them or not. + +The class `xSprite` represents a sprite that can is a rectangle that can draw either as a single color, an image, or a copy of another sprite. It can be moved over time. Also, it allows you to set a custom clip if this is an image sprite. + +The class `xSpriteAnimation` derives from an image sprite, which clip can change over time. These clips are called _frames_ and have to be set beforehand, when ready you can start or stop the animation with a specified delay between frames. Every `xSpriteAnimation` is scheduled on the same dedicated animation thread. + + +The concurrency model is led simple : +- the main loop schedules rendering and user input +- the animation loop schedules animation frame shifts diff --git a/include/xSDL.h b/include/xSDL.h new file mode 100644 index 0000000..c0a8772 --- /dev/null +++ b/include/xSDL.h @@ -0,0 +1,10 @@ +#ifndef DEF_XSDL_H + #define DEF_XSDL_H + + #include "../xSDL/xApplication.h" + #include "../xSDL/xController.h" + #include "../xSDL/xElement.h" + #include "../xSDL/xSprite.h" + #include "../xSDL/xSpriteAnimation.h" + +#endif \ No newline at end of file diff --git a/main.h b/main.h new file mode 100644 index 0000000..b518827 --- /dev/null +++ b/main.h @@ -0,0 +1,19 @@ +#ifndef DEF_MAIN_H + #define DEF_MAIN_H + + #include + #include + #include + #include + + #include "include/xSDL.h" + // #include "include/xMario.h" + + using namespace std; + + #define BLOC_WIDTH 32 + #define BLOC_HEIGHT 20 + #define FPS 40 + #define BLOC_SIZE 32 + +#endif \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..490e943 --- /dev/null +++ b/makefile @@ -0,0 +1,41 @@ +.PHONY: build/exe +SDL_FLAGS=`pkg-config sdl2 SDL2_image --cflags --libs` +FLAGS=-pthread -std=c++11 $(SDL_FLAGS) -Wall -Wextra +CC=g++ $(FLAGS) +CC_STATIC=$(CC) -fPIC +CC_SHARED=g++ -shared +CUR_DIR=$(shell pwd) + +# get list of '.o' files to be generated for 'build/include/libxsdl.so' target +XSDL_O_FILES=$(shell ls -l xSDL/*.cpp | awk '{print $$NF}' | sed 's/^xSDL\/\(.\+\)\.cpp$$/build\/static\/\1.o/g') + +prepare: + test -d build/ || mkdir build/ + test -d build/static || mkdir build/static + test -d build/include || mkdir build/include + +clean: + test -d build/ && rm -r build/ || true + +run: + LD_LIBRARY_PATH="$(CUR_DIR)/build/include/:$LD_LIBRARY_PATH" ./build/exe; + +# build executable +build/exe: build/main.o build/include/libxsdl.so + $(CC) -L$(CUR_DIR)/build/include/ -o $@ $< -lxsdl + +# xSDL lib static objects +build/static/%.o: xSDL/%.cpp + $(CC_STATIC) -c $^ -o $@ + +# xSDL shared (dynamic) library +build/include/libxsdl.so: $(XSDL_O_FILES) + $(CC_SHARED) -Wl,-soname,libxsdl.so -o $@ $^ + +# xMario lib +build/xMario.o: xMario/*.cpp + $(CC) -o $@ $^ + +# build main +build/main.o: main.cpp + $(CC) -o $@ -c $^ \ No newline at end of file diff --git a/xSDL/xApplication.cpp b/xSDL/xApplication.cpp new file mode 100644 index 0000000..1d96a6b --- /dev/null +++ b/xSDL/xApplication.cpp @@ -0,0 +1,347 @@ +#include "xApplication.h" + +xApplication* xApplication::_instance = nullptr; + + +/** Static singleton builder */ +xApplication* xApplication::create(const char *t, int w, int h){ + if( xApplication::_instance != nullptr ){ + throw runtime_error("[xApplication] instance already created"); + } + + xApplication::_instance = new xApplication(t, w, h); + + if( xApplication::_instance == nullptr ){ + throw runtime_error("[xApplication] cannot instanciate"); + } + + return xApplication::_instance; +} + +/** Static singleton getter */ +xApplication* xApplication::get(){ + if( xApplication::_instance == nullptr ){ + throw runtime_error("[xApplication] instance not created yet"); + } + + return xApplication::_instance; +} + +/** Private singleton constructor */ +xApplication::xApplication(const char *t, int w, int h){ + // default values + _lasttick = SDL_GetTicks(); + _fpstime = 1000/60; + _window = NULL; + _renderer = NULL; + _texture = NULL; + + // init SDL subsystems + SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER ); + + // create window + _window = SDL_CreateWindow( + t, + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + w, + h, + SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_BORDERLESS | SDL_WINDOW_OPENGL + ); + if( _window == NULL ){ + throw runtime_error("[xApplication] cannot create SDL window"); + } + + // store window dimensions + _winrect.x = 0; _winrect.y = 0; + _winrect.w = w; _winrect.h = h; + SDL_GetWindowSize(_window, &_winrect.w, &_winrect.h); + + // create renderer + _renderer = SDL_CreateRenderer( + _window, + -1, + SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC + ); + if( _renderer == NULL ){ + throw runtime_error("[xApplication] cannot create SDL renderer"); + } +} + +/** clean resources */ +xApplication::~xApplication(){ + _sprites.clear(); + + SDL_DestroyTexture(_texture); + SDL_DestroyRenderer(_renderer); + SDL_DestroyWindow( _window ); + SDL_Quit(); +} + +/** SDL getters */ +SDL_Window* xApplication::window(){ return _window; } +SDL_Renderer* xApplication::renderer(){ return _renderer; } + +/** Set rgba background */ +void xApplication::setBackground(Uint8 r, Uint8 g, Uint8 b, Uint8 a){ + SDL_SetRenderDrawColor( _renderer, r, g, b, a ); +} + +/** Sets image background */ +bool xApplication::setImage(const char *url){ + _texture = IMG_LoadTexture( _renderer, url ); + return _texture != NULL; +} + + + + + +/* [COLLIDE] Retourne si 2 objets sont en collision +=========================================================*/ +// collisions *> +// [0] -> VRAI si collision a droite +// [1] -> VRAI si collision a gauche +// [2] -> VRAI si collision en haut +// // [3] -> VRAI si collision en bas +// bool xApplication::collide(SDL_Rect a, SDL_Rect b, vector& cols){ +// if( !this->status() ) return true; + +// // Verification de collisions +// bool outLeft = (a.x >= b.x+b.w ); // Trop a droite +// bool outRight = (a.x+a.w <= b.x ); // Trop a gauche +// bool outUp = (a.y >= b.y+b.h ); // Trop en haut +// bool outDown = (a.y+a.h <= b.y ); // Trop en bas + + +// // Calcule du bord en question +// int distLeft = abs( (a.x+a.w) - b.x ); +// int distRight = abs( a.x - (b.x+b.w) ); +// int distBottom = abs( a.y - (b.y+b.h) ); +// int distTop = abs( (a.y+a.h) - b.y ); + + +// // Valeurs de retour pointeur +// cols[0] = !outRight && distRight <= 1; // plus proche de droite +// cols[1] = !outLeft && distLeft <= 2; // plus proche de gauche +// cols[2] = !outUp && distTop <= 2; // plus proche du haut +// cols[3] = !outDown && distBottom <= 1; // plus proche du bas + + +// // On retourne si il y a collision ou pas +// return !( outRight || outLeft || outUp || outDown ); +// } + +// /* [HIT] Retourne si une texture est en collision avec une autre +// =========================================================*/ +// bool xApplication::hit(xSprite* current, int movex, int movey){ +// if( !this->status() ) return true; + + +// // Anti conflit inter-thread +// _mutex_hit.try_lock(); + +// /* (1) On recupere le SDL_Rect destination du sprite courant */ +// int xIndex = -1; +// for( int i = 0 ; i < _sprites.size() ; i++ ) +// if( _sprites[i] == current ){ +// xIndex = i; +// break; +// } + +// if( xIndex == -1 ){ +// _mutex_hit.unlock(); +// return false; +// } + + +// SDL_Rect a = *current->dst(); +// a.x += movex; +// a.y += movey; + +// // Contiendra le sens de collision +// vector collideFrom(4, false); +// // Contiendra le sens de collision +// vector collideTo(4, false); + + +// /* (2) On regarde si en dehors de la fenetre */ +// if( (a.x < _winrect.x ) // Inclus a droite +// || (a.x+a.w > _winrect.x+_winrect.w ) // Inclus a gauche +// || (a.y < _winrect.y ) // Inclus en haut +// || (a.y+a.h > _winrect.y+_winrect.h ) // Inclus en bas +// ){ +// // Si on tombe, on meurt +// if( a.y+a.h > _winrect.y+_winrect.h ) +// state = 2; + +// cerr << _indexes[xIndex] << " collide with WINDOW" << endl; +// _mutex_hit.unlock(); +// return true; +// } + + +// /* (3) On compare avec toutes les autres textures */ +// for( int i = 0 ; i < _sprites.size() ; i++ ){ + +// // Si c'est pas le sprite courant +// if( _sprites[i] != current ){ + +// // On verifie que le sprite n'entre pas en collision +// if( this->collide(a, *(_sprites[i])->dst(), collideTo) ){ + + + +// // On recupere la surface en collision inverse +// collideFrom[0] = collideTo[1]; // On inverse Droite +// collideFrom[1] = collideTo[0]; // et Gauche +// collideFrom[2] = collideTo[3]; // On inverse Haut +// collideFrom[3] = collideTo[2]; // et Bas + +// // On lance les listeners de collision +// _sprites[i]->onCollide(collideTo, current); +// current->onCollide(collideFrom, _sprites[i]); + +// _debug = *(_sprites[i])->dst(); + +// _mutex_hit.unlock(); +// return true; +// } + +// } + +// } + + + +// // On debloque la ressource +// _mutex_hit.unlock(); +// return false; +// } + +// /* [HIT] Retourne si une texture est en collision avec une autre +// =========================================================*/ +// bool xApplication::hit(string current, int movex, int movey){ +// if( !this->status() ) return true; + +// _mutex_hit.try_lock(); + +// /* (1) On recupere le SDL_Rect destination du sprite courant */ +// xSprite *sprite = NULL; +// sprite = this->get(current); + +// // Gestion erreur +// if( sprite == NULL ){ +// _mutex_hit.unlock(); +// return false; +// } + +// // Retour du resultat +// _mutex_hit.unlock(); +// return this->hit(sprite, movex, movey); +// } + + + + +/** adds new sprite to draw (with its identifier) */ +void xApplication::push(string id, xElement* sprite){ + _mutex_draw.try_lock(); + _sprites[id] = sprite; + _mutex_draw.unlock(); +} + +/** removes a sprite to draw (from its identifier) */ +void xApplication::pull(string id){ + _mutex_draw.try_lock(); + _sprites.erase(id); + _mutex_draw.unlock(); +} + + +/** removes a sprite to draw (from its address) */ +void xApplication::pull(xElement* sprite){ + _mutex_draw.try_lock(); + + map::iterator it; + for( it = _sprites.begin() ; it != _sprites.end() ; it++ ){ + if( sprite == it->second ){ + _sprites.erase(it); + break; + } + } + + _mutex_draw.unlock(); +} + +/** clears the scene */ +void xApplication::clear(){ + _mutex_draw.try_lock(); + _sprites.clear(); + _mutex_draw.unlock(); +} + +/** Update the scene */ +void xApplication::render(){ + _mutex_draw.try_lock(); + + /* 1. clear scene */ + SDL_RenderClear(_renderer); + + /* 2. apply background color */ + SDL_RenderDrawRect(_renderer, &_winrect); + + /* 3. add image texture if set */ + if( _texture != NULL) + SDL_RenderCopy(_renderer, _texture, NULL, NULL); + + + /* 4. draw every sprite */ + map::iterator it = _sprites.begin(); + for( ; it != _sprites.end() ; it++ ){ + xElement* sprite = it->second; + sprite->draw(_renderer); + } + + /* 5. commit renderer */ + SDL_RenderPresent(_renderer); + + _mutex_draw.unlock(); +} + +/** Sets FPS */ +void xApplication::setFps(const uint32_t fps){ + _fps = fps; +} + +/** schedules the main loop */ +void xApplication::schedule(){ + // poll events + SDL_Event event; + while( SDL_PollEvent(&event) != 0 ) + this->_controller.handleEvent(&event); + + // wait to satisfy FPS + this->syncFps(_fps); + + // render + this->render(); +} + +/** synchronizes according to @fps + * to be used at the end of the UI loop */ +void xApplication::syncFps(const int fps){ + // set fps + if( fps != 0 ) + _fpstime = 1000/fps; + + if( _lasttick == 0 ) + _lasttick = SDL_GetTicks()-_fpstime; + + // wait if too fast + if( SDL_GetTicks()-_lasttick < _fpstime ) + SDL_Delay( _fpstime - (SDL_GetTicks()-_lasttick) ); + + // store now + _lasttick = SDL_GetTicks(); +} diff --git a/xSDL/xApplication.h b/xSDL/xApplication.h new file mode 100644 index 0000000..942a5c9 --- /dev/null +++ b/xSDL/xApplication.h @@ -0,0 +1,89 @@ +#ifndef DEF_XENGINE_H + #define DEF_XENGINE_H + + #include "SDL.h" + #include "SDL_image.h" + #include + #include + #include + #include + #include + #include + #include "xElement.h" + #include "xController.h" + using namespace std; + + enum xAppState { + RUNNING, + STOPPING, + STOPPED + }; + + #define EventListener void(*)(SDL_Event*) + + class xApplication{ + + public: + /** Singleton */ + static xApplication* create(const char *t, int w, int h); + static xApplication* get(); + + ~xApplication(); + + SDL_Window *window(); + SDL_Renderer *renderer(); + + void setBackground(Uint8 r=0xff, Uint8 g=0xff, Uint8 b=0xff, Uint8 a=0xff); + bool setImage(const char *url); + + + // bool collide(SDL_Rect a, SDL_Rect b, vector& cols); // Collision entre 2 SDL_Rect + // bool hit(xSprite *current, int movex=0, int movey=0); // Gestion des collisions + // bool hit(string current, int movex=0, int movey=0); // Gestion des collisions + + /** scene */ + void push(string id, xElement* sprite); + void pull(string id); + void pull(xElement *sprite); + void clear(); + + void render(); + + // manage main loop and thread loops + void setFps(const uint32_t fps); + void schedule(); + + xAppState state; + private: + /** Singleton */ + xApplication(const char *t, int w, int h); + static xApplication* _instance; + + void syncFps(const int fps=0); + + // fps management + uint32_t _fps = 60; + uint32_t _lasttick; + uint32_t _fpstime; + + // event management + xController _controller; + + // sdl objects + SDL_Window *_window; + SDL_Rect _winrect; + + SDL_Renderer *_renderer; + SDL_Texture *_texture; + + // sprites + map _sprites; + + // shared threads + map _pool; + + // thread safety + mutex _mutex_draw; + }; + +#endif \ No newline at end of file diff --git a/xSDL/xController.cpp b/xSDL/xController.cpp new file mode 100644 index 0000000..c17f032 --- /dev/null +++ b/xSDL/xController.cpp @@ -0,0 +1,40 @@ +#include "xController.h" + +xController::xController(): + _handlers() +{} + +xController::~xController(){ + _handlers.clear(); +} + +// called in scheduling +void xController::handleEvent(SDL_Event* event){ + _mutex.lock(); + + map::iterator found = _handlers.find( (SDL_EventType) event->type ); + + // ignore no handler found + if( found == _handlers.end() ){ + _mutex.unlock(); + return; + } + + (*found->second)(event); + + _mutex.unlock(); +} + +// bind a new handler +void xController::attachEvent(SDL_EventType t, void(*handler)(SDL_Event*) ){ + _mutex.lock(); + _handlers[ t ] = handler; + _mutex.unlock(); +} + +// removes an existing handler +void xController::detachEvent(SDL_EventType t, void(*handler)(SDL_Event*) ){ + _mutex.lock(); + _handlers.erase(t); + _mutex.unlock(); +} \ No newline at end of file diff --git a/xSDL/xController.h b/xSDL/xController.h new file mode 100644 index 0000000..3a3d8f9 --- /dev/null +++ b/xSDL/xController.h @@ -0,0 +1,32 @@ +#ifndef DEF_XCONTROLLER_H + #define DEF_XCONTROLLER_H + + #include "SDL.h" + #include + #include + using namespace std; + + #define EventListener void(*)(SDL_Event*) + + class xController{ + + public: + xController(); + ~xController(); + + // called in scheduling + void handleEvent(SDL_Event* event); + + // manage handlers + void attachEvent(SDL_EventType t, void(*handler)(SDL_Event*) ); + void detachEvent(SDL_EventType t, void(*handler)(SDL_Event*) ); + + private: + // event handlers + map _handlers; + + // thread safety + mutex _mutex; + }; + +#endif \ No newline at end of file diff --git a/xSDL/xElement.cpp b/xSDL/xElement.cpp new file mode 100644 index 0000000..0748c12 --- /dev/null +++ b/xSDL/xElement.cpp @@ -0,0 +1 @@ +#include "xElement.h" \ No newline at end of file diff --git a/xSDL/xElement.h b/xSDL/xElement.h new file mode 100644 index 0000000..76eaadf --- /dev/null +++ b/xSDL/xElement.h @@ -0,0 +1,10 @@ +#ifndef DEF_XELEMENT_H + #define DEF_XELEMENT_H + + #include "SDL.h" + + struct xElement { + virtual void draw(SDL_Renderer* renderer) = 0; + }; + +#endif \ No newline at end of file diff --git a/xSDL/xSprite.cpp b/xSDL/xSprite.cpp new file mode 100644 index 0000000..cf2b091 --- /dev/null +++ b/xSDL/xSprite.cpp @@ -0,0 +1,212 @@ +#include "xSprite.h" + +/** clean SDL objects */ +xSprite::~xSprite(){ + _mutex.try_lock(); + SDL_FreeSurface( _surface ); + _mutex.unlock(); +} + +/** empty sprite */ +xSprite::xSprite(){ + _type = "basic"; +} + +/** color sprite */ +xSprite::xSprite(const int rgba[]){ + _type = "basic"; + this->setSurface(rgba); +} + +/** image sprite */ +xSprite::xSprite(const char *url){ + _type = "basic"; + + this->setSurface(url); +} + +/** texture copy sprite */ +xSprite::xSprite(SDL_Surface *s){ + _type = "basic"; + this->setSurface(s); +} + +/** update sprite to rhb color */ +void xSprite::setSurface(const int rgba[]){ + _mutex.try_lock(); + if( _surface != NULL ){ + SDL_FreeSurface( _surface ); + _surface = NULL; + } + + _surface = SDL_CreateRGBSurface(0, 0, 0, 32, rgba[0], rgba[1], rgba[2], rgba[3]); + + // On cree la texture a partir de la surface + // _surface = SDL_CreateTextureFromSurface(_manager->renderer(), surf); + if( _surface == NULL ) { + throw runtime_error("[xSprite] setSurface(rgba) -> NULL surface"); + } + _mutex.unlock(); +} + + + +/** update sprite to image */ +void xSprite::setSurface(const char *url){ + _mutex.try_lock(); + if( _surface != NULL ){ + SDL_FreeSurface( _surface ); + _surface = NULL; + } + + _surface = IMG_Load(url); + + if( _surface == NULL ) { + throw runtime_error("[xSprite] setSurface(url) -> NULL surface"); + } + _mutex.unlock(); +} + +/* [SETTEXTURE] Modification de la texture texture +=========================================================*/ +void xSprite::setSurface(SDL_Surface *s){ + _mutex.try_lock(); + if( _surface != NULL ){ + SDL_FreeSurface( _surface ); + _surface = NULL; + } + + _surface = s; + + if( _surface == NULL ) { + throw runtime_error("[xSprite] setSurface(surface) -> NULL surface"); + } + + _mutex.unlock(); +} + +/** move sprite with a (x,y) velocity + * - int[0]: new + * - int[1]: can move on y + */ +// bool* xSprite::move(double xSpeed, double ySpeed){ +// _mutex.try_lock(); + +// bool result[2] = {false, false}; + +// /* logic variables */ +// int incrx = x; +// int incry = y; +// bool moveFasterOnY = abs(x) <= abs(y); + +// int signofx = (x==0) ? 0 : x / abs(x); +// int signofy = (y==0) ? 0 : y / abs(y); + +// // while velocity not null +// while( incrx != 0 || incry != 0 ){ + +// result[0] = incrx; +// result[1] = incry; + +// /* (3) Si on peut aller a la destination */ +// // if( !_manager->hit(this, incrx, incry) ){ +// // _dst.x += incrx; +// // _dst.y += incry; + +// // // cerr << ">>> not locked <<<" << endl; + +// // result[0] = incrx; +// // result[1] = incry; + +// // _mutex.unlock(); +// // return result; +// // } + + + +// /* (4) Sinon, on decremente les deplacements 'alternativement' */ +// if( moveFasterOnY ){ // Si on a plus de mouvement horizontal +// if( signofx != 0 && incrx != 0 ) // si la vel. sur x n'est pas nulle +// incrx -= signofx; // on diminue la vel. sur x +// else if( signofy != 0 && incry != 0 ) // sinon si vel. sur y n'est pas nulle +// incry -= signofy; // on diminue la vel. sur y +// else // sinon, velocite nulle +// return result; // On arrete de chercher + +// }else{ // Si on a plus de mouvement vertical +// if( signofy != 0 && incry != 0 ) // si la vel. sur y n'est pas nulle +// incry -= signofy; // on diminue la vel. sur y +// else if( signofx != 0 && incrx != 0 ) // sinon si vel. sur x n'est pas nulle +// incrx -= signofx; // on diminue la vel. sur x +// else // sinon, velocite nulle +// return result; // On arrete de chercher +// } + +// // if( !_manager->hit(this, 0, 1) ){ +// // cerr << "locked from (" << _dst.x << ", " << _dst.y << ") to (" << incrx << ", " << incry << ")" << endl; +// // } +// } + +// // retour +// _mutex.unlock(); +// return result; +// } + +/** get/set sprite type */ +string xSprite::getType(){ return _type; } +void xSprite::setType(string newtype){ _type = newtype; } + +/** set default dimensions */ +void xSprite::dimensions(){ + _mutex.try_lock(); + /* extract surface lengths*/ + int w = _surface->w; + int h = _surface->h; + + _dst = (SDL_Rect){0, 0, w, h}; + _src = (SDL_Rect){0, 0, w, h}; + _mutex.unlock(); +} + +/** set surface dimensions */ +void xSprite::dimensions(SDL_Rect r){ + _mutex.try_lock(); + /* extract surface lengths */ + int w = _surface->w; + int h = _surface->h; + _src = (SDL_Rect){0, 0, w, h}; + + /* store destination dimensions */ + _dst = (SDL_Rect){r.x, r.y, r.w, r.h}; + _mutex.unlock(); +} + +/** set surface dimensions + clip */ +void xSprite::dimensions(SDL_Rect r, SDL_Rect clip){ + _mutex.try_lock(); + _src = (SDL_Rect){clip.x, clip.y, clip.w, clip.h}; + _dst = (SDL_Rect){r.x, r.y, r.w, r.h}; + _mutex.unlock(); +} + +/** returns the surface */ +SDL_Surface *xSprite::surface(){ return _surface; } + +/** return destination dimensions */ +SDL_Rect *xSprite::dst(){ return &_dst; } + +/** return source dimensions */ +SDL_Rect *xSprite::src(){ return &_src; } + + +/** draws to renderer */ +void xSprite::draw(SDL_Renderer* renderer){ + _mutex.try_lock(); + SDL_RenderCopy( + renderer, + SDL_CreateTextureFromSurface(renderer, this->surface()), + this->src(), + this->dst() + ); + _mutex.unlock(); +} \ No newline at end of file diff --git a/xSDL/xSprite.h b/xSDL/xSprite.h new file mode 100644 index 0000000..f1c8224 --- /dev/null +++ b/xSDL/xSprite.h @@ -0,0 +1,60 @@ +#ifndef DEF_XSPRITE_H + #define DEF_XSPRITE_H + + #include + #include + #include + #include "SDL.h" + #include "SDL_image.h" + #include "xElement.h" + using namespace std; + + class xSprite : public xElement { + + public: + xSprite(); // empty + xSprite(const int rgba[]); // color sprite + xSprite(const char *url); // image sprite + xSprite(SDL_Surface *s); // copy sprite + ~xSprite(); + + // replace surface + void setSurface(const int rgba[]); // color sprite + void setSurface(const char *url); // image sprite + void setSurface(SDL_Surface *t); // copy surface + + // move the sprite + // const bool* move(double xSpeed, double ySpeed); + + // action to apply on collision + // virtual void onCollide(vector from, xSprite* by); + + // get/set sprite type + string getType(); + void setType(string newtype); + + // set dimensions + void dimensions(); // defauts + void dimensions(SDL_Rect r); + void dimensions(SDL_Rect r, SDL_Rect clip); // w/ clip + + // getters + SDL_Surface *surface(); + SDL_Rect *dst(); + SDL_Rect *src(); + + // implement xElement + virtual void draw(SDL_Renderer* renderer) override; + + protected: + string _type; + + SDL_Surface* _surface = NULL; + SDL_Rect _dst; + SDL_Rect _src; + + mutex _mutex; + + }; + +#endif \ No newline at end of file diff --git a/xSDL/xSpriteAnimation.cpp b/xSDL/xSpriteAnimation.cpp new file mode 100644 index 0000000..78a0b08 --- /dev/null +++ b/xSDL/xSpriteAnimation.cpp @@ -0,0 +1,116 @@ +#include "xSpriteAnimation.h" + +/** clean SDL objects and frames */ +xSpriteAnimation::~xSpriteAnimation(){ + _mutex.try_lock(); + SDL_FreeSurface( _surface ); + _frames.erase( _frames.begin(), _frames.end() ); + _mutex.unlock(); +} + + +/** builds an animation */ +xSpriteAnimation::xSpriteAnimation(const char *url, SDL_Rect dest) +: xSprite( url ){ + + this->dimensions(dest); +} + + +/** builds an animation from an existing surface */ +xSpriteAnimation::xSpriteAnimation(SDL_Surface *s, SDL_Rect dest) +: xSprite(s) { + this->dimensions(dest); +} + + +/** adds an animation frame */ +void xSpriteAnimation::addFrame(SDL_Rect clip){ + _mutex.try_lock(); + _frames.push_back( (SDL_Rect){ + clip.x, + clip.y, + clip.w, + clip.h + } ); + _mutex.unlock(); +} + +/** clears all frames */ +void xSpriteAnimation::clearFrames(){ + _mutex.try_lock(); + _frames.erase(_frames.begin(), _frames.end()); + _mutex.unlock(); +} + +/** draw to scene */ +void xSpriteAnimation::draw(SDL_Renderer* renderer){ + _mutex.try_lock(); + SDL_RenderCopy( + renderer, + SDL_CreateTextureFromSurface(renderer, this->surface()), + this->src(), + this->dst() + ); + _mutex.unlock(); +} + +/** animation process */ +void xSpriteAnimationProcess(xSpriteAnimation *xSA, uint32_t t, int flags){ + uint32_t timer = 0; + bool way = true; + uint32_t start = 0; + + while( flags&SPRITE_ANIM_INFINITE ){ + + // for every frame (sprite) + for( uint32_t i = start ; i != xSA->_frames.size() ; i+=(way?1:-1) ){ + timer = SDL_GetTicks(); + + // update current frame clip + // xSA->_mutex.try_lock(); + xSA->_src = xSA->_frames.at(i); + // xSA->_mutex.unlock(); + + // xSA->manager()->update(); + + if( SDL_GetTicks()-timer < t ) + SDL_Delay( t - (SDL_GetTicks()-timer) ); + } + + // manage reverse flag + if( flags&SPRITE_ANIM_REVERSE ){ + way = !way; + start = (way) ? 0 : xSA->_frames.size()-1; + } + + } + + // end thread + return; +} + + +/** adds the animation to the render */ +void xSpriteAnimation::start(int t, int flags){ + _timeout = t; + _flags = flags; + + // launch animation + _animation = new thread(xSpriteAnimationProcess, this, t, flags); + + // don't wait for the thread to end + _animation->detach(); +} + + + +/** stops the animation */ +void xSpriteAnimation::stop(){ + // stop animation thread + delete _animation; + if( _animation->joinable() ) + _animation->join(); + + _animation = NULL; +} \ No newline at end of file diff --git a/xSDL/xSpriteAnimation.h b/xSDL/xSpriteAnimation.h new file mode 100644 index 0000000..c3b246f --- /dev/null +++ b/xSDL/xSpriteAnimation.h @@ -0,0 +1,47 @@ +#ifndef DEF_XSPRITEANIMATION_H + #define DEF_XSPRITEANIMATION_H + + #include "SDL.h" + #include "xSprite.h" + #include "xApplication.h" + #include "xElement.h" + #include + #include + using namespace std; + + #define SPRITE_ANIM_ONCE 0b1 + #define SPRITE_ANIM_INFINITE 0b10 + #define SPRITE_ANIM_REVERSE 0b100 + + class xSpriteAnimation : public xSprite { + + public: + // spritesheet with sprite size + xSpriteAnimation(const char *url, SDL_Rect dest); + xSpriteAnimation(SDL_Surface *s, SDL_Rect dest); + ~xSpriteAnimation(); + + void addFrame(SDL_Rect clip); + void clearFrames(); + + // animation control handles + void start(int t, int flags=SPRITE_ANIM_ONCE); + void stop(); + + // implement xElement + void draw(SDL_Renderer* renderer) override; + + protected: + vector _frames; + + // animation + int _timeout; + int _flags; + + // animation thread + thread *_animation; + friend void xSpriteAnimationProcess(xSpriteAnimation *xSA, uint32_t t, int flags); + + }; + +#endif \ No newline at end of file