350 lines
8.5 KiB
C++
350 lines
8.5 KiB
C++
#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 <vector<bool>*>
|
|
// [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<bool>& 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.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<bool> collideFrom(4, false);
|
|
// // Contiendra le sens de collision
|
|
// vector<bool> 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.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 */
|
|
void xApplication::push(xDrawable* sprite){
|
|
_mutex_draw.lock();
|
|
_sprites.insert(sprite);
|
|
_mutex_draw.unlock();
|
|
}
|
|
|
|
/** removes a sprite to draw (from its address) */
|
|
void xApplication::pull(xDrawable* sprite){
|
|
_mutex_draw.lock();
|
|
_sprites.erase(sprite);
|
|
_mutex_draw.unlock();
|
|
}
|
|
|
|
/** clears the scene */
|
|
void xApplication::clear(){
|
|
_mutex_draw.lock();
|
|
_sprites.clear();
|
|
_mutex_draw.unlock();
|
|
}
|
|
|
|
/** Update the scene */
|
|
void xApplication::render(){
|
|
_mutex_draw.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 */
|
|
for( set<xDrawable*>::iterator it = _sprites.begin() ; it != _sprites.end() ; it++ ){
|
|
xDrawable* sprite = *it;
|
|
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 )
|
|
_controller.handleEvent(&event);
|
|
|
|
const uint32_t ticks = SDL_GetTicks();
|
|
|
|
// trigger tick() on registered orchestrables
|
|
_mutex_orchestrate.lock();
|
|
set<xOrchestrable*>::iterator it;
|
|
for( it = _orchestrables.begin() ; it != _orchestrables.end() ; it++ ){
|
|
xOrchestrable* orchestrable = *it;
|
|
orchestrable->tick(ticks);
|
|
}
|
|
_mutex_orchestrate.unlock();
|
|
|
|
// wait to satisfy FPS
|
|
this->syncFps();
|
|
|
|
// render
|
|
this->render();
|
|
}
|
|
|
|
void xApplication::addOrchestrable(xOrchestrable* o){
|
|
_mutex_orchestrate.lock();
|
|
_orchestrables.insert(o);
|
|
_mutex_orchestrate.unlock();
|
|
}
|
|
|
|
void xApplication::removeOrchestrable(xOrchestrable* o){
|
|
_mutex_orchestrate.lock();
|
|
_orchestrables.erase(o);
|
|
_mutex_orchestrate.unlock();
|
|
}
|
|
|
|
/** 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();
|
|
}
|