#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){ _fpstime = 1000.0/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(); // render this->render(); } /** synchronizes according to FPS * to be used at the end of the UI loop */ void xApplication::syncFps(){ 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(); }