Compare commits

..

14 Commits

Author SHA1 Message Date
Adrien Marquès 4ca0312479 make xEventHandler method non-const 2020-02-05 18:17:50 +01:00
Adrien Marquès 414fbf6404 create proper handler interface for xController : xEventHandler 2020-02-04 21:08:21 +01:00
Adrien Marquès 5266b5d115 add xApplication defaults 2020-02-04 20:49:10 +01:00
Adrien Marquès 2b81a72542 remove const methods | add copy constructor 2020-02-04 20:45:42 +01:00
Adrien Marquès 47429e5081 minimal refactor 2020-02-04 20:22:31 +01:00
Adrien Marquès cb1a862b6f make xSpriteGroup displace() actually update sprites with relative displace 2020-02-04 20:22:17 +01:00
Adrien Marquès 0b7e7a2948 set default xSprite clip / projection 2020-02-04 20:21:35 +01:00
Adrien Marquès d1e6a1e9dc add more xSprite SDL_Rect shorthands 2020-02-04 19:03:03 +01:00
Adrien Marquès cc276e1c2a make xSpriteGroup render all if not animated, and only the current frame in the other case 2020-02-04 18:58:09 +01:00
Adrien Marquès 49deb9e868 add xSprite constructors and SDL_Rect primitive shorthands 2020-02-04 18:56:49 +01:00
Adrien Marquès 7a6022e694 create orchestrable xSpriteGroup (no more xSprite alone) 2020-02-02 16:18:20 +01:00
Adrien Marquès ab2f170def replace xElement by xDrawable
also fix mutexes
2020-02-02 16:17:52 +01:00
Adrien Marquès 44de8cb3ea Merge SpriteAnimation into xSprite and interface improvements
- remove dead code
- a sprite with no clip uses the default surface dimensions to draw
- a sprite with a single clip uses it
- a sprite with multiple clips (i.e. frames) has the first as a default but when orchestrated iterates over clips in order
2019-11-08 21:38:16 +01:00
Adrien Marquès 12871d93dc update readme: explain orchestrable and how xSpriteAnimation implements it 2019-11-06 22:56:03 +01:00
17 changed files with 324 additions and 332 deletions

View File

@ -19,11 +19,8 @@ The class `xApplication` is a singleton representing your application, its windo
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 interface `xOrchestrable` represents any class that can be called during the application scheduling. Any number of `xOrchestrable` can be added or removed from the application at any time in order to execute 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
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. It is also an orchestrable (i.e. animates on application ticks), you can start or stop the animation with a specified time interval. Every `xSpriteAnimation` is scheduled by the `xApplication`.

View File

@ -4,8 +4,8 @@
#include "../xSDL/xOrchestrable.h"
#include "../xSDL/xApplication.h"
#include "../xSDL/xController.h"
#include "../xSDL/xElement.h"
#include "../xSDL/xDrawable.h"
#include "../xSDL/xSprite.h"
#include "../xSDL/xSpriteAnimation.h"
#include "../xSDL/xSpriteGroup.h"
#endif

View File

@ -139,7 +139,7 @@ bool xApplication::setImage(const char *url){
// // Anti conflit inter-thread
// _mutex_hit.try_lock();
// _mutex_hit.lock();
// /* (1) On recupere le SDL_Rect destination du sprite courant */
// int xIndex = -1;
@ -224,7 +224,7 @@ bool xApplication::setImage(const char *url){
// bool xApplication::hit(string current, int movex, int movey){
// if( !this->status() ) return true;
// _mutex_hit.try_lock();
// _mutex_hit.lock();
// /* (1) On recupere le SDL_Rect destination du sprite courant */
// xSprite *sprite = NULL;
@ -245,29 +245,29 @@ bool xApplication::setImage(const char *url){
/** adds new sprite to draw */
void xApplication::push(xElement* sprite){
_mutex_draw.try_lock();
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(xElement* sprite){
_mutex_draw.try_lock();
void xApplication::pull(xDrawable* sprite){
_mutex_draw.lock();
_sprites.erase(sprite);
_mutex_draw.unlock();
}
/** clears the scene */
void xApplication::clear(){
_mutex_draw.try_lock();
_mutex_draw.lock();
_sprites.clear();
_mutex_draw.unlock();
}
/** Update the scene */
void xApplication::render(){
_mutex_draw.try_lock();
_mutex_draw.lock();
/* 1. clear scene */
SDL_RenderClear(_renderer);
@ -281,9 +281,8 @@ void xApplication::render(){
/* 4. draw every sprite */
set<xElement*>::iterator it;
for( it = _sprites.begin() ; it != _sprites.end() ; it++ ){
xElement* sprite = *it;
for( set<xDrawable*>::iterator it = _sprites.begin() ; it != _sprites.end() ; it++ ){
xDrawable* sprite = *it;
sprite->draw(_renderer);
}
@ -308,7 +307,7 @@ void xApplication::schedule(){
const uint32_t ticks = SDL_GetTicks();
// trigger tick() on registered orchestrables
_mutex_orchestrate.try_lock();
_mutex_orchestrate.lock();
set<xOrchestrable*>::iterator it;
for( it = _orchestrables.begin() ; it != _orchestrables.end() ; it++ ){
xOrchestrable* orchestrable = *it;
@ -324,13 +323,13 @@ void xApplication::schedule(){
}
void xApplication::addOrchestrable(xOrchestrable* o){
_mutex_orchestrate.try_lock();
_mutex_orchestrate.lock();
_orchestrables.insert(o);
_mutex_orchestrate.unlock();
}
void xApplication::removeOrchestrable(xOrchestrable* o){
_mutex_orchestrate.try_lock();
_mutex_orchestrate.lock();
_orchestrables.erase(o);
_mutex_orchestrate.unlock();
}

View File

@ -9,7 +9,7 @@
#include <mutex>
#include <thread>
#include <stdexcept>
#include "xElement.h"
#include "xDrawable.h"
#include "xController.h"
#include "xOrchestrable.h"
using namespace std;
@ -41,8 +41,8 @@
// bool hit(string current, int movex=0, int movey=0); // Gestion des collisions
/** scene */
void push(xElement* sprite);
void pull(xElement *sprite);
void push(xDrawable* sprite);
void pull(xDrawable *sprite);
void clear();
void render();
@ -64,24 +64,24 @@
void syncFps();
// fps management
uint32_t _lasttick;
uint32_t _fpstime;
uint32_t _lasttick { 0 };
uint32_t _fpstime { (int) 1000.0/60 };
// event management
xController _controller;
// sprites
set<xElement*> _sprites;
set<xDrawable*> _sprites;
// execution pool
set<xOrchestrable*> _orchestrables;
// sdl objects
SDL_Window *_window;
SDL_Rect _winrect;
SDL_Window *_window { NULL };
SDL_Rect _winrect { 0, 0, 0, 0 };
SDL_Renderer *_renderer;
SDL_Texture *_texture;
SDL_Renderer *_renderer { NULL };
SDL_Texture *_texture { NULL };
// thread safety
mutex _mutex_draw;

View File

@ -12,29 +12,24 @@ xController::~xController(){
void xController::handleEvent(SDL_Event* event){
_mutex.lock();
map<SDL_EventType, EventHandler>::iterator found = _handlers.find( (SDL_EventType) event->type );
// ignore no handler found
if( found == _handlers.end() ){
_mutex.unlock();
return;
for( set<xEventHandler*>::iterator it = _handlers.begin() ; it != _handlers.end() ; it++ ){
xEventHandler* handler = (*it);
handler->handle(event);
}
(*found->second)(event);
_mutex.unlock();
}
// bind a new handler
void xController::attachEvent(SDL_EventType t, EventHandlerArg){
void xController::attachEvent(xEventHandler* handler){
_mutex.lock();
_handlers[ t ] = handler;
_handlers.insert(handler);
_mutex.unlock();
}
// removes an existing handler
void xController::detachEvent(SDL_EventType t){
void xController::detachEvent(xEventHandler* handler){
_mutex.lock();
_handlers.erase(t);
_handlers.erase(handler);
_mutex.unlock();
}

View File

@ -3,11 +3,10 @@
#include "SDL.h"
#include <mutex>
#include <map>
using namespace std;
#include <set>
#include "xEventHandler.h"
#define EventHandlerArg void(* handler)(SDL_Event*)
#define EventHandler void(*)(SDL_Event*)
using namespace std;
class xController{
@ -19,12 +18,12 @@
void handleEvent(SDL_Event* event);
// manage handlers
void attachEvent(SDL_EventType t, EventHandlerArg);
void detachEvent(SDL_EventType t);
void attachEvent(xEventHandler* handler);
void detachEvent(xEventHandler* handler);
private:
// event handlers
map<SDL_EventType, EventHandler> _handlers;
set<xEventHandler*> _handlers;
// thread safety
mutex _mutex;

1
xSDL/xDrawable.cpp Normal file
View File

@ -0,0 +1 @@
#include "xDrawable.h"

View File

@ -3,7 +3,7 @@
#include "SDL.h"
struct xElement {
struct xDrawable {
virtual void draw(SDL_Renderer* renderer) = 0;
};

View File

@ -1 +0,0 @@
#include "xElement.h"

1
xSDL/xEventHandler.cpp Normal file
View File

@ -0,0 +1 @@
#include "xEventHandler.h"

16
xSDL/xEventHandler.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef DEF_XEVENT_HANDLER_H
#define DEF_XEVENT_HANDLER_H
#include "SDL.h"
using namespace std;
class xEventHandler {
public:
// handles the event
virtual void handle(SDL_Event* event) = 0;
};
#endif

View File

@ -2,38 +2,56 @@
/** clean SDL objects */
xSprite::~xSprite(){
_mutex.try_lock();
_mutex.lock();
SDL_FreeSurface( _surface );
_mutex.unlock();
}
/** empty sprite */
xSprite::xSprite(){
_type = "basic";
/** copy constructor */
xSprite::xSprite(const xSprite& other){
// copy surface
if( other._surface != NULL ){
this->setSurface( SDL_ConvertSurface(other._surface, other._surface->format, SDL_SWSURFACE) );
}
this->setClip(other._clip);
this->project(other._projection);
}
/** 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);
/** image sprite with clip */
xSprite::xSprite(const char *url, SDL_Rect clip){
this->setSurface(url);
this->setClip(clip);
}
xSprite::xSprite(const char *url, int clip_x, int clip_y, int clip_w, int clip_h){
this->setSurface(url);
this->setClip(clip_x, clip_y, clip_w, clip_h);
}
/** image sprite with clip and projection */
xSprite::xSprite(const char *url, SDL_Rect clip, SDL_Rect projection){
this->setSurface(url);
this->setClip(clip);
this->project(projection);
}
xSprite::xSprite(const char *url, int clip_x, int clip_y, int clip_w, int clip_h, int project_x, int project_y, int project_w, int project_h){
this->setSurface(url);
this->setClip(clip_x, clip_y, clip_w, clip_h);
this->project(project_x, project_y, project_w, project_h);
}
/** update sprite to rhb color */
void xSprite::setSurface(const int rgba[]){
_mutex.try_lock();
_mutex.lock();
if( _surface != NULL ){
SDL_FreeSurface( _surface );
_surface = NULL;
@ -41,8 +59,6 @@ void xSprite::setSurface(const int rgba[]){
_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");
}
@ -53,7 +69,7 @@ void xSprite::setSurface(const int rgba[]){
/** update sprite to image */
void xSprite::setSurface(const char *url){
_mutex.try_lock();
_mutex.lock();
if( _surface != NULL ){
SDL_FreeSurface( _surface );
_surface = NULL;
@ -67,10 +83,9 @@ void xSprite::setSurface(const char *url){
_mutex.unlock();
}
/* [SETTEXTURE] Modification de la texture texture
=========================================================*/
/** copies an existing surface */
void xSprite::setSurface(SDL_Surface *s){
_mutex.try_lock();
_mutex.lock();
if( _surface != NULL ){
SDL_FreeSurface( _surface );
_surface = NULL;
@ -84,129 +99,57 @@ void xSprite::setSurface(SDL_Surface *s){
_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};
const SDL_Surface* xSprite::surface(){
_mutex.lock();
SDL_Surface* surface = _surface;
_mutex.unlock();
return surface;
}
/** 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};
/** set sprite clip */
void xSprite::setClip(SDL_Rect clip){
_mutex.lock();
_clip = clip;
_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();
void xSprite::setClip(int x, int y, int w, int h){
setClip( (SDL_Rect){x, y, w, h} );
}
/** 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; }
/** gets sprite clip */
const SDL_Rect xSprite::clip(){
_mutex.lock();
const SDL_Rect clip = _clip;
_mutex.unlock();
return clip;
}
/** set sprite projection */
void xSprite::project(SDL_Rect dest){
_mutex.lock();
_projection = dest;
_mutex.unlock();
}
void xSprite::project(int x, int y, int w, int h){
project( (SDL_Rect){x, y, w, h} );
}
/** gets sprite projection */
const SDL_Rect xSprite::projection(){
_mutex.lock();
const SDL_Rect projection = _projection;
_mutex.unlock();
return projection;
}
/** draws to renderer */
void xSprite::draw(SDL_Renderer* renderer){
_mutex.try_lock();
_mutex.lock();
SDL_RenderCopy(
renderer,
SDL_CreateTextureFromSurface(renderer, this->surface()),
this->src(),
this->dst()
SDL_CreateTextureFromSurface(renderer, _surface),
&_clip,
&_projection
);
_mutex.unlock();
}

View File

@ -6,54 +6,54 @@
#include <mutex>
#include "SDL.h"
#include "SDL_image.h"
#include "xElement.h"
#include "xDrawable.h"
#include "xApplication.h"
using namespace std;
class xSprite : public xElement {
class xSprite : public xDrawable{
public:
xSprite(); // empty
xSprite(const int rgba[]); // color sprite
xSprite(const char *url); // image sprite
xSprite(SDL_Surface *s); // copy sprite
~xSprite();
// copy constructor
xSprite(const xSprite& other);
// color sprite
xSprite(const int rgba[]);
// image sprite
xSprite(const char *url);
// image with default clip
xSprite(const char *url, SDL_Rect clip);
// image with default clip
xSprite(const char *url, int clip_x, int clip_y, int clip_w, int clip_h);
// image with default clip and projection
xSprite(const char *url, SDL_Rect clip, SDL_Rect projection);
// image with default clip and projection
xSprite(const char *url, int clip_x, int clip_y, int clip_w, int clip_h, int project_x, int project_y, int project_w, int project_h);
virtual ~xSprite();
// replace surface
void setSurface(const int rgba[]); // color sprite
void setSurface(const char *url); // image sprite
void setSurface(SDL_Surface *t); // copy surface
const SDL_Surface* surface();
// move the sprite
// const bool* move(double xSpeed, double ySpeed);
// sets the sprite clip
void setClip(SDL_Rect clip);
void setClip(int x, int y, int w, int h);
const SDL_Rect clip();
// action to apply on collision
// virtual void onCollide(vector<bool> from, xSprite* by);
// set projection into scene
void project(SDL_Rect dest);
void project(int x, int y, int w, int h);
const SDL_Rect projection();
// 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
// implement xDrawable
virtual void draw(SDL_Renderer* renderer) override;
private:
SDL_Surface* _surface = NULL;
SDL_Rect _clip { 0, 0, 0, 0 };
SDL_Rect _projection { 0, 0, 0, 0 };
protected:
string _type;
SDL_Surface* _surface = NULL;
SDL_Rect _dst;
SDL_Rect _src;
mutex _mutex;
mutex _mutex;
};

View File

@ -1,76 +0,0 @@
#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_frames.try_lock();
_frames.push_back( (SDL_Rect){
clip.x,
clip.y,
clip.w,
clip.h
} );
_mutex_frames.unlock();
}
/** clears all frames */
void xSpriteAnimation::clearFrames(){
_mutex_frames.try_lock();
_frames.erase(_frames.begin(), _frames.end());
_mutex_frames.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 xSpriteAnimation::tick(const uint32_t ticks){
_mutex_frames.try_lock();
uint32_t time_index = ticks / _interval;
size_t frame_index = time_index % _frames.size();
// update current frame clip
_src = _frames.at(frame_index);
_mutex_frames.unlock();
}
/** adds the animation to the render */
void xSpriteAnimation::start(uint32_t interval){
_interval = interval;
xApplication::get()->addOrchestrable(this);
}
/** stops the animation */
void xSpriteAnimation::stop(){
xApplication::get()->removeOrchestrable(this);
}

View File

@ -1,41 +0,0 @@
#ifndef DEF_XSPRITEANIMATION_H
#define DEF_XSPRITEANIMATION_H
#include "SDL.h"
#include <vector>
#include "xSprite.h"
#include "xApplication.h"
#include "xElement.h"
#include "xOrchestrable.h"
using namespace std;
class xSpriteAnimation : public xSprite, public xOrchestrable {
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(uint32_t delay);
void stop();
// implement xElement
void draw(SDL_Renderer* renderer) override;
// implement xOrchestrable
void tick(const uint32_t ticks);
protected:
vector<SDL_Rect> _frames;
mutex _mutex_frames;
// animation
uint32_t _interval;
};
#endif

106
xSDL/xSpriteGroup.cpp Normal file
View File

@ -0,0 +1,106 @@
#include "xSpriteGroup.h"
/** clean SDL objects */
xSpriteGroup::~xSpriteGroup(){
_mutex.lock();
_sprites.clear();
_mutex.unlock();
}
/** default constructor */
xSpriteGroup::xSpriteGroup(){
}
/** adds a sprite to the group */
void xSpriteGroup::add(xSprite* sprite){
_mutex.lock();
_sprites.insert(sprite);
_mutex.unlock();
}
/** removes a sprite from the group */
void xSpriteGroup::remove(xSprite* sprite){
_mutex.lock();
_sprites.erase(sprite);
_mutex.unlock();
}
/** apply relative displace for each sprite */
void xSpriteGroup::displace(int x, int y, int w, int h){
displace( (SDL_Rect){x,y,w,h} );
}
void xSpriteGroup::displace(SDL_Rect relative_displace){
_mutex.lock();
for( set<xSprite*>::iterator it = _sprites.begin() ; it != _sprites.end() ; it++ ) {
xSprite* sprite = (*it);
const SDL_Rect actual_projection = sprite->projection();
sprite->project(
actual_projection.x + relative_displace.x,
actual_projection.y + relative_displace.y,
actual_projection.w + relative_displace.w,
actual_projection.h + relative_displace.h
);
}
_mutex.unlock();
}
/** draws to renderer */
void xSpriteGroup::draw(SDL_Renderer* renderer){
_mutex.lock();
// only draw active sprite if animated
if( _animated ){
// invalid active index
if( _active_sprite >= _sprites.size() ) {
_active_sprite = 0;
_mutex.unlock();
return;
}
set<xSprite*>::iterator at_index = _sprites.begin();
advance(at_index, _active_sprite);
if( at_index == _sprites.end() ){
_mutex.unlock();
return;
}
(*at_index)->draw(renderer);
_mutex.unlock();
return;
}
// else draw every sprite
set<xSprite*>::iterator it;
for( it = _sprites.begin() ; it != _sprites.end() ; it++ ){
(*it)->draw(renderer);
}
_mutex.unlock();
}
/** animation process */
void xSpriteGroup::tick(const uint32_t ticks){
_mutex.lock();
const uint32_t time_index = ticks / _animation_interval;
_active_sprite = time_index % _sprites.size();
_mutex.unlock();
}
/** orchestrate the animation */
void xSpriteGroup::animate(uint32_t interval){
_mutex.lock();
_animated = true;
_animation_interval = interval;
_active_sprite = 0;
xApplication::get()->addOrchestrable(this);
_mutex.unlock();
}
/** stops orchestrating the animation */
void xSpriteGroup::freeze(){
_mutex.lock();
xApplication::get()->removeOrchestrable(this);
_mutex.unlock();
}

53
xSDL/xSpriteGroup.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef DEF_XSPRITEGROUP_H
#define DEF_XSPRITEGROUP_H
#include <string>
#include <vector>
#include <mutex>
#include "SDL.h"
#include "SDL_image.h"
#include "xDrawable.h"
#include "xOrchestrable.h"
#include "xApplication.h"
#include "xSprite.h"
using namespace std;
class xSpriteGroup : public xDrawable, public xOrchestrable {
public:
xSpriteGroup();
virtual ~xSpriteGroup();
// manage sprite list
void add(xSprite* sprite);
void remove(xSprite* sprite);
void clear();
// apply relative displace for each sprite
void displace(SDL_Rect relative_displace);
void displace(int x, int y, int w, int h);
// implement xDrawable
virtual void draw(SDL_Renderer* renderer) override;
// implement xOrchestrable
void tick(const uint32_t ticks);
// animation handles
void animate(uint32_t interval);
void freeze();
protected:
set<xSprite*> _sprites;
// if not animated, draw all sprites, else draw one at a time
bool _animated = false;
// active sprite for animation
size_t _active_sprite = 0;
uint32_t _animation_interval = 0;
mutex _mutex;
};
#endif