Async sprites loading

This commit is contained in:
Thomas 2022-01-10 14:27:16 +01:00
parent 3dcdcadff3
commit 36d6b21613
No known key found for this signature in database
GPG Key ID: E538821A6CDFDAD7
21 changed files with 254 additions and 123 deletions

View File

@ -17,3 +17,4 @@ target_link_libraries(Space mingl)
target_link_libraries(Space GL) target_link_libraries(Space GL)
target_link_libraries(Space GLU) target_link_libraries(Space GLU)
target_link_libraries(Space glut) target_link_libraries(Space glut)
target_link_libraries(Space pthread)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,7 @@
# General configuration # General configuration
general: general:
maxFPS: 90 maxFPS: 90
theme: bad
# Players config # Players config
players: players:

View File

@ -25,7 +25,12 @@ typedef string configKey;
* @brief this struct stores all relevant data from the configuration file * @brief this struct stores all relevant data from the configuration file
*/ */
struct ConfigData { struct ConfigData {
/*!
* @brief theme to use. Valid values : good,bad
*/
string theme;
/*! /*!
* @brief maximum framerate at which the game will run * @brief maximum framerate at which the game will run
*/ */

View File

@ -14,7 +14,7 @@
#define GUARD_GAME_H #define GUARD_GAME_H
#include <vector> #include <vector>
#include "mingl/mingl.h" #include "mingl/mingl.h"
#include "pixelManager.h" #include "pixelManager/pixelManager.h"
#include "utils.h" #include "utils.h"
#include "playerDef.h" #include "playerDef.h"
#include "player.h" #include "player.h"
@ -41,8 +41,9 @@ private:
/*! /*!
* @brief PixelManager : Class that contains and draws all the data that will be drawn on screen * @brief PixelManager : Class that contains and draws all the data that will be drawn on screen
* This is a pointer because the object is allocated at runtime, following the configuration
*/ */
PixelManager pm; unique_ptr<PixelManager> pm;
/*! /*!
* @brief ConfigData : Struct that stores all the relevant data read from the configuration file * @brief ConfigData : Struct that stores all the relevant data read from the configuration file
@ -65,7 +66,7 @@ private:
Position basePos; Position basePos;
/*! /*!
* @brief Invader posision and type matrix * @brief Invader position and type matrix
*/ */
InvadersGrid grid; InvadersGrid grid;
@ -97,9 +98,9 @@ private:
// invaders related variables // invaders related variables
/*! /*!
* @brief cooldown between two invader shot in milliseconds * @brief cooldown until the invaders can shoot again
*/ */
unsigned fireCooldown=120; unsigned fireCooldown=0;
// basic methods // basic methods

16
headers/mySprite.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef GUARD_MYSPRITE_H
#define GUARD_MYSPRITE_H
#include <future>
#include "mingl/gui/sprite.h"
using namespace std;
class MySprite{
public:
unique_ptr<nsGui::Sprite> ptr;
future<void> asyncLoad(const string& fname);
void mirror(MySprite& msp);
};
#endif

View File

@ -0,0 +1,21 @@
#ifndef GUARD_GOODPIXELMANAGER_H
#define GUARD_GOODPIXELMANAGER_H
#include "pixelManager/pixelManager.h"
class GoodPixelManager : public PixelManager{
void loadSprites(SpriteTasks& tasks) override;
MySprite player;
MySprite invaderA;
MySprite invaderB;
MySprite invaderC;
MySprite missile;
MySprite torpedo;
public:
GoodPixelManager(MinGL& win);
};
#endif

View File

@ -24,54 +24,64 @@
#include "utils.h" #include "utils.h"
#include "playMode.h" #include "playMode.h"
#include "menu.h" #include "menu.h"
#include "mySprite.h"
using namespace std; using namespace std;
#define MIRROR(SP) mirrorData((SP).getPixelData(), (SP).getRowSize()), (SP).getRowSize()
typedef nsGui::GlutFont::GlutFonts Font; typedef nsGui::GlutFont::GlutFonts Font;
/*! /*!
* @class PixelManager * @class PixelManager
* @brief main display function, clear the window and calls sub display functions * @brief main display function, clear the window and calls sub display functions
*/ */
/*
* It's MinGL's fault. This is all I have to say
*/
// The convention seems to just add a number to the macro name
#define ADD_TASK(X) ADD_TASK2(X, X)
#define ADD_TASK2(X, Y) tasks.push_back((X).asyncLoad("assets/"#Y".si2"));
typedef vector<future<void>> SpriteTasks;
class PixelManager{ class PixelManager{
public: public:
MinGL& window; MinGL& window;
/* /*!
* Sprites are not const because for some reason the texture is associated with coordinates, * @brief loads sprites in parallel using multiple threads
* and we have no way to dissociate them... * @param[in] vec : We take his ownership, so
* So the objects are constantly updated with new coordinates as they need to be drawn * @fn void loadSprites();
* We used {} insead of () for the constructor because the () makes the compiler think we declare methods */
* virtual void loadSprites(SpriteTasks& tasks);
* (We could copy them every time, but I feel like copying image data every frame isn't great)
*/
/*! /*!
* @brief sprite of the logo of the game * @brief sprite of the logo of the game
*/ */
nsGui::Sprite logo{"assets/logo.si2"}; MySprite logo;
/*!
* @brief sprite of the background during gameplay
*/
nsGui::Sprite gameBackground{"assets/game_background.si2"};
/*! /*!
* @brief sprite of the background during menu * @brief sprite of the background during menu
*/ */
nsGui::Sprite menuBackground{"assets/menu_background.si2"}; MySprite menuBackground;
/*!
* @brief sprite of the background during gameplay
*/
MySprite gameBackground;
/*! /*!
* @brief sprite of the right hand of god * @brief sprite of the right hand of god
*/ */
nsGui::Sprite rightHand{"assets/hand_open.si2"}; MySprite rightHand;
/*! /*!
* @brief sprite of the left hand of god * @brief sprite of the left hand of god
*/ */
nsGui::Sprite leftHand{MIRROR(rightHand)}; MySprite leftHand;
/*! /*!
* @brief constructor the pixel manager class * @brief constructor the pixel manager class
@ -145,11 +155,11 @@ public:
/*! /*!
* @brief display a sprite on screen * @brief display a sprite on screen
* @param[in] sprite : sprite to draw * @param[in] msp : sprite to draw
* @param[in] pos : pixel coordinates to draw the sprite * @param[in] pos : pixel coordinates to draw the sprite
* @fn void drawSprite(const nsGui::Sprite& sprite, const Position& pos = Position(0, 0)) const; * @fn void drawSprite(const MySprite& msp, const Position& pos = Position(0, 0)) const;
*/ */
void drawSprite(const nsGui::Sprite& sprite, const Position& pos = Position(0, 0)) const; void drawSprite(const MySprite& msp, const Position& pos = Position(0, 0)) const;
/*! /*!
* @brief display a menu button on screen * @brief display a menu button on screen

View File

@ -153,6 +153,7 @@ void ConfigBuilder::readInvaderType(const configKey& baseKey, InvaderTypeDef& in
void ConfigBuilder::readConfig() { void ConfigBuilder::readConfig() {
collectedData.theme = getString("general.theme");
collectedData.maxFPS = getInt("general.maxFPS"); collectedData.maxFPS = getInt("general.maxFPS");
readGrid("grid"); readGrid("grid");

View File

@ -16,7 +16,7 @@
* The more important stuff must be drawn last * The more important stuff must be drawn last
*/ */
void Game::displayAll() const { void Game::displayAll() const {
pm.drawSprite(pm.gameBackground); pm->drawSprite(pm->gameBackground);
for (unsigned i = 0; i < this->grid.size(); ++i){ for (unsigned i = 0; i < this->grid.size(); ++i){
for (unsigned j = 0; j < this->grid[i].size(); ++j){ for (unsigned j = 0; j < this->grid[i].size(); ++j){
Position vec( Position vec(
@ -28,23 +28,23 @@ void Game::displayAll() const {
} }
for(const missile& miss : missiles){ for(const missile& miss : missiles){
pm.drawMissile(miss, confData.missilesWidth, confData.missilesColor); pm->drawMissile(miss, confData.missilesWidth, confData.missilesColor);
} }
for(const Torpedo& tor : torpedos){ for(const Torpedo& tor : torpedos){
pm.drawTorpedo(tor, confData.torpedosWidth, confData.torpedosColor); pm->drawTorpedo(tor, confData.torpedosWidth, confData.torpedosColor);
} }
displayGod(); displayGod();
DEBUG_INSTR( DEBUG_INSTR(
pm.drawText(Position(pm.getScreenWidth()-200, 20), "FPS : "+to_string(fps), nsGraphics::KWhite, Font::BITMAP_8_BY_13); pm->drawText(Position(pm->getScreenWidth()-200, 20), "FPS : "+to_string(fps), nsGraphics::KWhite, Font::BITMAP_8_BY_13);
) )
for(unsigned i=0;i<players.size();++i){ for(unsigned i=0;i<players.size();++i){
if(!players[i].isEliminated()){ if(!players[i].isEliminated()){
if(players[i].deathAnimCounter%2==0){ if(players[i].deathAnimCounter%2==0){
pm.drawPlayer(players[i].x, confData.playersWidth, confData.playerDefs[i].color); pm->drawPlayer(players[i].x, confData.playersWidth, confData.playerDefs[i].color);
} }
} }
// out of the condition, because we still need to display the falling heart // out of the condition, because we still need to display the falling heart
@ -57,15 +57,15 @@ void Game::displayHearts(playerID pID) const {
// As said before, the player loop is an illusion, 2 players max // As said before, the player loop is an illusion, 2 players max
unsigned x; unsigned x;
if(pID==PLAYER1)x = 0; if(pID==PLAYER1)x = 0;
else x = pm.getScreenWidth()-HEART_LENGTH; else x = pm->getScreenWidth()-HEART_LENGTH;
unsigned y = GOD_BENCH_SIZE+5; unsigned y = GOD_BENCH_SIZE+5;
for(unsigned i=0;i<players[pID].lives;++i){ for(unsigned i=0;i<players[pID].lives;++i){
pm.drawHeart(Position(x, y)); pm->drawHeart(Position(x, y));
y+=HEART_LENGTH+5; y+=HEART_LENGTH+5;
} }
if(players[pID].hasDeathAnimation()){ if(players[pID].hasDeathAnimation()){
pm.drawHeart(Position(x, y+players[pID].deathAnimCounter*5)); pm->drawHeart(Position(x, y+players[pID].deathAnimCounter*5));
} }
} }
@ -74,15 +74,15 @@ void Game::displayInvader(const Position& pos, InvaderType type) const {
const InvaderTypeDef& invDef = confData.invadersDef.at(type); const InvaderTypeDef& invDef = confData.invadersDef.at(type);
switch(type){ switch(type){
case InvaderType::TYPEA:{ case InvaderType::TYPEA:{
pm.drawInvaderA(pos, confData.invadersSize, invDef.color); pm->drawInvaderA(pos, confData.invadersSize, invDef.color);
return; return;
} }
case InvaderType::TYPEB:{ case InvaderType::TYPEB:{
pm.drawInvaderB(pos, confData.invadersSize, invDef.color); pm->drawInvaderB(pos, confData.invadersSize, invDef.color);
return; return;
} }
case InvaderType::TYPEC:{ case InvaderType::TYPEC:{
pm.drawInvaderC(pos, confData.invadersSize, invDef.color); pm->drawInvaderC(pos, confData.invadersSize, invDef.color);
return; return;
} }
} }
@ -97,40 +97,40 @@ void Game::displayGod() const {
case GodState::NONE: case GodState::NONE:
return; return;
case GodState::AWAKE: { case GodState::AWAKE: {
pm.drawGodBench(god.counter - GOD_BENCH_SIZE); pm->drawGodBench(god.counter - GOD_BENCH_SIZE);
Position leftHand(GOD_HAND_DISTANCE, god.counter-GOD_BENCH_SIZE); Position leftHand(GOD_HAND_DISTANCE, god.counter-GOD_BENCH_SIZE);
Position rightHand(pm.getScreenWidth()-GOD_HAND_DISTANCE-GOD_HAND_SIZE, god.counter-GOD_BENCH_SIZE); Position rightHand(pm->getScreenWidth()-GOD_HAND_DISTANCE-GOD_HAND_SIZE, god.counter-GOD_BENCH_SIZE);
pm.drawGodLeftHand(leftHand); pm->drawGodLeftHand(leftHand);
pm.drawGodRightHand(rightHand); pm->drawGodRightHand(rightHand);
pm.drawGodFace(god.counter - GOD_BENCH_SIZE); pm->drawGodFace(god.counter - GOD_BENCH_SIZE);
break; break;
} }
case GodState::WAIT:{ case GodState::WAIT:{
pm.drawGodBench(0); pm->drawGodBench(0);
Position leftHand(GOD_HAND_DISTANCE, 0); Position leftHand(GOD_HAND_DISTANCE, 0);
Position rightHand(god.getRightHandPos(pm.getScreenWidth())); Position rightHand(god.getRightHandPos(pm->getScreenWidth()));
pm.drawGodLeftHand(leftHand); pm->drawGodLeftHand(leftHand);
pm.drawGodRightHand(rightHand); pm->drawGodRightHand(rightHand);
pm.drawGodFace(0); pm->drawGodFace(0);
break; break;
} }
case GodState::RETRIEVE1: case GodState::RETRIEVE1:
case GodState::RETRIEVE2:{ case GodState::RETRIEVE2:{
// Bezier curve // Bezier curve
// counter goes [0-100] // counter goes [0-100]
pm.drawGodBench(0); pm->drawGodBench(0);
pm.drawGodLeftHand(Position(GOD_HAND_DISTANCE, 0)); pm->drawGodLeftHand(Position(GOD_HAND_DISTANCE, 0));
pm.drawGodFace(0); pm->drawGodFace(0);
Position pos(god.getRightHandPos(pm.getScreenWidth())); Position pos(god.getRightHandPos(pm->getScreenWidth()));
Position endPos = invIndexToPos(god.thrownInvPosX, god.thrownInvPosY); Position endPos = invIndexToPos(god.thrownInvPosX, god.thrownInvPosY);
applyBezier(pos, god.thrownTransition, god.counter / 100.0); applyBezier(pos, god.thrownTransition, god.counter / 100.0);
applyBezier(pos, endPos, god.counter / 100.0); applyBezier(pos, endPos, god.counter / 100.0);
// pos is now the position we need to draw our hand to // pos is now the position we need to draw our hand to
pm.drawGodRightHand(pos); pm->drawGodRightHand(pos);
if(god.thrownInvType!=InvaderType::NONE){ if(god.thrownInvType!=InvaderType::NONE){
pos+=Position(GOD_HAND_SIZE/2, GOD_HAND_SIZE/2); pos+=Position(GOD_HAND_SIZE/2, GOD_HAND_SIZE/2);
@ -140,12 +140,12 @@ void Game::displayGod() const {
break; break;
} }
case GodState::THROW:{ case GodState::THROW:{
pm.drawGodBench(0); pm->drawGodBench(0);
pm.drawGodLeftHand(Position(GOD_HAND_DISTANCE, 0)); pm->drawGodLeftHand(Position(GOD_HAND_DISTANCE, 0));
pm.drawGodFace(0); pm->drawGodFace(0);
// compute start position (not sure if we should store it or compute it each time ?) // compute start position (not sure if we should store it or compute it each time ?)
Position handPos = god.getRightHandPos(pm.getScreenWidth()); Position handPos = god.getRightHandPos(pm->getScreenWidth());
Position invaderPos = handPos; Position invaderPos = handPos;
applyTransformation(invaderPos, GOD_HAND_SIZE, confData.invadersSize); applyTransformation(invaderPos, GOD_HAND_SIZE, confData.invadersSize);
@ -160,7 +160,7 @@ void Game::displayGod() const {
else handCounter = 30-god.counter; else handCounter = 30-god.counter;
handPos = handPos + god.thrownVector * (handCounter / 100.0); handPos = handPos + god.thrownVector * (handCounter / 100.0);
} }
pm.drawGodRightHand(handPos); pm->drawGodRightHand(handPos);
break; break;
} }

View File

@ -10,17 +10,42 @@
*/ */
#include <chrono> #include <chrono>
#include <memory>
#include <thread> #include <thread>
#include "game.h" #include "game.h"
#include "playMode.h" #include "playMode.h"
#include "pixelManager/goodPixelManager.h"
#define WININIT window("SUPER Space Invader : Turbo Apocalypse DX - VS GOD", Position(1280, 720), Position(128, 128), nsGraphics::KBlack) #define WININIT window("SUPER Space Invader : Turbo Apocalypse DX - VS GOD", Position(1280, 720), Position(128, 128), nsGraphics::KBlack)
Game::Game() : WININIT, pm(window) { Game::Game() : WININIT {
if(!reloadConfig()){
if(!reloadConfig()){ // Config
throw runtime_error("Initial config loading failed. Please check the error above and fix the configuration"); throw runtime_error("Initial config loading failed. Please check the error above and fix the configuration");
} }
sm.readFile();
// Pixel Manager
if(confData.theme=="good"){
pm = std::make_unique<GoodPixelManager>(window);
}else if(confData.theme=="bad"){
pm = std::make_unique<PixelManager>(window);
}else throw runtime_error("Invalid theme value : "+confData.theme+
"\nValid values are : good,bad");
cout << "Loading sprites..." << endl;
SpriteTasks tasks;
chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now();
pm->loadSprites(tasks);
for(future<void>& f : tasks)f.wait();
// We just do that for one sprite, so I didn't bother creating an 'API' for that
pm->leftHand.mirror(pm->rightHand);
chrono::high_resolution_clock::duration elapsed = chrono::high_resolution_clock::now()-start;
cout << "Done ! Time : " << chrono::duration_cast<chrono::milliseconds>(elapsed).count() << "ms" << endl;
sm.readFile(); // Score manager
} }
bool Game::areThereInvadersLeft(){ bool Game::areThereInvadersLeft(){
@ -30,7 +55,7 @@ bool Game::areThereInvadersLeft(){
void Game::handleScoreSaving(){ void Game::handleScoreSaving(){
for(unsigned i=0;i<players.size();++i){ for(unsigned i=0;i<players.size();++i){
string pName; string pName;
pm.askPlayerNameMenu(i, players[i].score, pName); pm->askPlayerNameMenu(i, players[i].score, pName);
sm.inputScore(move(pName), players[i].score); sm.inputScore(move(pName), players[i].score);
} }
sm.writeFile(); sm.writeFile();
@ -42,13 +67,13 @@ void Game::managedGames() {
while(playMode!=PlayMode::EXIT){ while(playMode!=PlayMode::EXIT){
if(playMode==PlayMode::NONE){ if(playMode==PlayMode::NONE){
playMode = pm.showInitialMenu(); playMode = pm->showInitialMenu();
}else{ }else{
initGame(); initGame();
enterGameLoop(); // will read the playMode enterGameLoop(); // will read the playMode
handleScoreSaving(); handleScoreSaving();
cout << "END OF GAME" << endl; cout << "END OF GAME" << endl;
if(!pm.showDeathMenu())playMode = PlayMode::NONE; if(!pm->showDeathMenu())playMode = PlayMode::NONE;
} }
} }
} }
@ -69,7 +94,7 @@ void Game::initGame(){
}else{ }else{
players.resize(2); players.resize(2);
// mirror the start X Position for the other // mirror the start X Position for the other
players[1].x = pm.getScreenWidth() - confData.startXPosition - confData.playersWidth; players[1].x = pm->getScreenWidth() - confData.startXPosition - confData.playersWidth;
} }
players[0].x = confData.startXPosition; players[0].x = confData.startXPosition;
@ -103,7 +128,7 @@ WinValue Game::enterGameLoop(){ // returns when game is finished
fpsStartTime = startTime; fpsStartTime = startTime;
} }
pm.startFrame(); pm->startFrame();
managePlayers(); managePlayers();
if(manageInvaders()) { // if they went down if(manageInvaders()) { // if they went down
@ -126,7 +151,7 @@ WinValue Game::enterGameLoop(){ // returns when game is finished
displayAll(); displayAll();
pm.endFrame(); pm->endFrame();
MyTimePoint endTime = chrono::high_resolution_clock::now(); MyTimePoint endTime = chrono::high_resolution_clock::now();

View File

@ -20,7 +20,7 @@ void Game::manageOnePlayer(playerID pID){
else p.x -= confData.playersSpeed; else p.x -= confData.playersSpeed;
} }
if (ISPRESSED(pID, right)){ if (ISPRESSED(pID, right)){
if(p.x + confData.playersWidth + confData.playersSpeed >= pm.getScreenWidth()) p.x = pm.getScreenWidth() - confData.playersWidth - 1; if(p.x + confData.playersWidth + confData.playersSpeed >= pm->getScreenWidth()) p.x = pm->getScreenWidth() - confData.playersWidth - 1;
else p.x += confData.playersSpeed; else p.x += confData.playersSpeed;
} }
@ -35,7 +35,7 @@ void Game::manageOnePlayer(playerID pID){
if(p.fireCooldown==0) { if(p.fireCooldown==0) {
if (ISPRESSED(pID, shoot)) { if (ISPRESSED(pID, shoot)) {
torpedos.emplace_back(p.x + confData.playersWidth / 2, pm.getScreenHeight() - PLAYER_HEIGHT, pID); torpedos.emplace_back(p.x + confData.playersWidth / 2, pm->getScreenHeight() - PLAYER_HEIGHT, pID);
p.fireCooldown = confData.playersFireCooldown; p.fireCooldown = confData.playersFireCooldown;
} }
}else --p.fireCooldown; }else --p.fireCooldown;
@ -69,7 +69,7 @@ bool Game::manageInvaders(){
// you got the end position of the invader crowd ! // you got the end position of the invader crowd !
if(end + confData.invadersSpeed < pm.getScreenWidth()){ if(end + confData.invadersSpeed < pm->getScreenWidth()){
basePos.setX(basePos.getX() + confData.invadersSpeed); basePos.setX(basePos.getX() + confData.invadersSpeed);
}else{ }else{
basePos.setY(basePos.getY() + confData.invadersSize + confData.invadersDistance); basePos.setY(basePos.getY() + confData.invadersSize + confData.invadersDistance);
@ -121,7 +121,7 @@ void Game::remCollidingProjectiles(){
void Game::moveMissiles() { void Game::moveMissiles() {
auto miss = missiles.begin(); auto miss = missiles.begin();
while (miss != missiles.end()) { while (miss != missiles.end()) {
if (miss->getY() >= pm.getScreenHeight())missiles.erase(miss); if (miss->getY() >= pm->getScreenHeight())missiles.erase(miss);
else { else {
miss->setY(miss->getY()+confData.missilesSpeed); miss->setY(miss->getY()+confData.missilesSpeed);
++miss; ++miss;
@ -144,7 +144,7 @@ void Game::checkMissilesAndPlayers() {
auto miss_ite = missiles.begin(); auto miss_ite = missiles.begin();
while(miss_ite!=missiles.end()){ while(miss_ite!=missiles.end()){
bool wasColliding = false; bool wasColliding = false;
if(miss_ite->getY()>=pm.getScreenHeight()-PLAYER_HEIGHT){ // check collision on Y if(miss_ite->getY()>=pm->getScreenHeight()-PLAYER_HEIGHT){ // check collision on Y
// now check collision on X (with both players) // now check collision on X (with both players)
for(Player& p : players){ for(Player& p : players){
if(p.isPlaying()){ if(p.isPlaying()){
@ -206,6 +206,6 @@ bool Game::invadersTouchPlayer() const {
unsigned outter = line.getOutterInvader(); unsigned outter = line.getOutterInvader();
return this->basePos.getY()+confData.invadersSize*(outter+1) return this->basePos.getY()+confData.invadersSize*(outter+1)
+confData.invadersDistance*outter +confData.invadersDistance*outter
>= pm.getScreenHeight() - PLAYER_HEIGHT; >= pm->getScreenHeight() - PLAYER_HEIGHT;
}); });
} }

View File

@ -48,7 +48,7 @@ bool Game::manageGod() {
god.thrownInvPosY = grid[god.thrownInvPosX].randomValidInv(); god.thrownInvPosY = grid[god.thrownInvPosX].randomValidInv();
god.thrownInvType = InvaderType::NONE; god.thrownInvType = InvaderType::NONE;
god.thrownTransition.setX(pm.getScreenWidth() - GOD_HAND_DISTANCE - GOD_HAND_SIZE); god.thrownTransition.setX(pm->getScreenWidth() - GOD_HAND_DISTANCE - GOD_HAND_SIZE);
god.thrownTransition.setY(basePos.getY() + INV_GET_POS(god.thrownInvPosY)); god.thrownTransition.setY(basePos.getY() + INV_GET_POS(god.thrownInvPosY));
return false; return false;
} }
@ -79,7 +79,7 @@ bool Game::manageGod() {
// compute the launch vector // compute the launch vector
Position invaderMiddlePos(pm.getScreenWidth() - GOD_HAND_DISTANCE - GOD_HAND_SIZE / 2, Position invaderMiddlePos(pm->getScreenWidth() - GOD_HAND_DISTANCE - GOD_HAND_SIZE / 2,
GOD_HAND_SIZE / 2); GOD_HAND_SIZE / 2);
@ -90,7 +90,7 @@ bool Game::manageGod() {
* honestly at this point I want to re-code the whole game engine to allow a better handling of cases like this...*/ * honestly at this point I want to re-code the whole game engine to allow a better handling of cases like this...*/
Position playerMiddlePos(players[target].x + confData.playersWidth / 2, Position playerMiddlePos(players[target].x + confData.playersWidth / 2,
pm.getScreenHeight() - PLAYER_HEIGHT / 2); pm->getScreenHeight() - PLAYER_HEIGHT / 2);
god.thrownVector = playerMiddlePos - invaderMiddlePos; god.thrownVector = playerMiddlePos - invaderMiddlePos;
god.thrownVector = god.thrownVector / (god.thrownVector.computeMagnitude() / 1000.0); god.thrownVector = god.thrownVector / (god.thrownVector.computeMagnitude() / 1000.0);
@ -101,7 +101,7 @@ bool Game::manageGod() {
case GodState::THROW: { case GodState::THROW: {
++god.counter; ++god.counter;
Position invaderPos = god.getRightHandPos(pm.getScreenWidth()); Position invaderPos = god.getRightHandPos(pm->getScreenWidth());
applyTransformation(invaderPos, GOD_HAND_SIZE, confData.invadersSize); applyTransformation(invaderPos, GOD_HAND_SIZE, confData.invadersSize);
Position a = god.thrownVector * (god.counter / 100.0); Position a = god.thrownVector * (god.counter / 100.0);
invaderPos = invaderPos + a; invaderPos = invaderPos + a;
@ -109,8 +109,8 @@ bool Game::manageGod() {
bool touched = false; bool touched = false;
// check if OOB (Out Of Bounds) // check if OOB (Out Of Bounds)
if (invaderPos.getY() + confData.invadersSize >= pm.getScreenWidth() || if (invaderPos.getY() + confData.invadersSize >= pm->getScreenWidth() ||
(invaderPos.getX() < 0 || invaderPos.getX() + confData.invadersSize >= pm.getScreenWidth())) { (invaderPos.getX() < 0 || invaderPos.getX() + confData.invadersSize >= pm->getScreenWidth())) {
touched = true; touched = true;
/* there are no invaders in the grid anymore, and the one thrown just went out of bound /* there are no invaders in the grid anymore, and the one thrown just went out of bound
@ -118,7 +118,7 @@ bool Game::manageGod() {
if(!areThereInvadersLeft())return true; if(!areThereInvadersLeft())return true;
// check player collision // check player collision
} else if (invaderPos.getY() + confData.invadersSize >= pm.getScreenHeight() - PLAYER_HEIGHT) { } else if (invaderPos.getY() + confData.invadersSize >= pm->getScreenHeight() - PLAYER_HEIGHT) {
for (Player &p: players) { for (Player &p: players) {
if(p.isPlaying()){ if(p.isPlaying()){
if (areLinesColliding( if (areLinesColliding(

26
src/mySprite.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "utils.h"
#include "mySprite.h"
future<void> MySprite::asyncLoad(const string& fname){
return std::async(std::launch::async, [fname, this]() -> void {
ptr = std::make_unique<nsGui::Sprite>(fname);
});
}
void MySprite::mirror(MySprite& msp) {
const vector<RGBAcolor>& inPixels = msp.ptr->getPixelData();
unsigned rowSize = msp.ptr->getRowSize();
vector<RGBAcolor> outPixels;
// we reserve size so the vector doesn't dynamically grows
outPixels.reserve(inPixels.size());
//for each line of pixel
for(unsigned rowOffset=0; rowOffset < inPixels.size(); rowOffset+=rowSize){
// for each pixel of that line
for(unsigned j=0;j<rowSize;++j) {
// push back the pixel opposed to this one (still in the same line)
outPixels.push_back(inPixels[rowOffset + rowSize - j - 1]);
}
}
ptr = std::make_unique<nsGui::Sprite>(outPixels, rowSize);
}

View File

@ -11,7 +11,7 @@
*/ */
#include "mingl/gui/text.h" #include "mingl/gui/text.h"
#include "pixelManager.h" #include "pixelManager/pixelManager.h"
#include "utils.h" #include "utils.h"
#include "god.h" #include "god.h"
@ -76,26 +76,10 @@ void PixelManager::drawTorpedo(const Position& baseVector, unsigned width, const
window << Rectangle(baseVector, baseVector + Position(width, width * PROJ_LENGTH_FACTOR), color); window << Rectangle(baseVector, baseVector + Position(width, width * PROJ_LENGTH_FACTOR), color);
} }
void PixelManager::drawSprite(const Sprite& sprite, const Position& pos) const { void PixelManager::drawSprite(const MySprite& msp, const Position& pos) const {
// see pixelManager.h for the explanation of this hack // The sprite is associated with a Vec2D for whatever reason, so yeah, we modify it each time we draw it
const_cast<Sprite&>(sprite).setPosition(pos); msp.ptr->setPosition(pos);
sprite.draw(window); msp.ptr->draw(window);
}
unsigned PixelManager::getScreenHeight() const {
return window.getWindowSize().getY();
}
unsigned PixelManager::getScreenWidth() const {
return window.getWindowSize().getX();
}
void PixelManager::startFrame() const {
window.clearScreen();
}
void PixelManager::endFrame() const {
window.finishFrame();
} }
void PixelManager::drawGodBench(int y) const { void PixelManager::drawGodBench(int y) const {
@ -128,19 +112,3 @@ void PixelManager::drawGodFace(int y, bool angry) const {
void PixelManager::drawText(const Position& pos, const string& text, const nsGraphics::RGBAcolor& color, Font font) const { void PixelManager::drawText(const Position& pos, const string& text, const nsGraphics::RGBAcolor& color, Font font) const {
window << Text(pos, text, color, font); window << Text(pos, text, color, font);
} }
vector<RGBAcolor>
PixelManager::mirrorData(const vector<nsGraphics::RGBAcolor>& inPixels, unsigned rowSize) {
vector<RGBAcolor> outPixels;
// we reserve size so the vector doesn't dynamically grows
outPixels.reserve(inPixels.size());
//for each line of pixel
for(unsigned rowOffset=0; rowOffset < inPixels.size(); rowOffset+=rowSize){
// for each pixel of that line
for(unsigned j=0;j<rowSize;++j) {
// push back the pixel opposed to this one (still in the same line)
outPixels.push_back(inPixels[rowOffset + rowSize - j - 1]);
}
}
return std::move(outPixels);
}

View File

@ -13,13 +13,13 @@
#include <thread> #include <thread>
#include "mingl/shape/rectangle.h" #include "mingl/shape/rectangle.h"
#include "playMode.h" #include "playMode.h"
#include "pixelManager.h" #include "pixelManager/pixelManager.h"
#include "utils.h" #include "utils.h"
using namespace nsShape; using namespace nsShape;
using namespace nsGraphics; using namespace nsGraphics;
void PixelManager::displayButton(const Position& baseVector,const string& text,nsGraphics::RGBAcolor& color){ void PixelManager::displayButton(const Position& baseVector, const string& text, nsGraphics::RGBAcolor& color){
window << Rectangle(baseVector, Position(180, 40)+baseVector, KGray); window << Rectangle(baseVector, Position(180, 40)+baseVector, KGray);
window << Rectangle(baseVector+Position(2,2), Position(178, 38)+baseVector, KBlack); window << Rectangle(baseVector+Position(2,2), Position(178, 38)+baseVector, KBlack);
window << nsGui::Text(baseVector+Position(10,22), text, color); window << nsGui::Text(baseVector+Position(10,22), text, color);

View File

@ -0,0 +1,15 @@
#include "pixelManager/goodPixelManager.h"
void GoodPixelManager::loadSprites(SpriteTasks& tasks) {
PixelManager::loadSprites(tasks);
ADD_TASK(player)
ADD_TASK(invaderA)
ADD_TASK(invaderB)
ADD_TASK(invaderC)
ADD_TASK(missile)
ADD_TASK(torpedo)
}
GoodPixelManager::GoodPixelManager(MinGL& win) : PixelManager(win) {
}

View File

@ -0,0 +1,41 @@
#include "pixelManager/pixelManager.h"
void PixelManager::loadSprites(SpriteTasks& tasks){
ADD_TASK(logo)
ADD_TASK(menuBackground)
ADD_TASK(gameBackground)
ADD_TASK2(rightHand, hand)
}
void PixelManager::startFrame() const {
window.clearScreen();
}
void PixelManager::endFrame() const {
window.finishFrame();
}
unsigned PixelManager::getScreenHeight() const {
return window.getWindowSize().getY();
}
unsigned PixelManager::getScreenWidth() const {
return window.getWindowSize().getX();
}
vector<RGBAcolor>
PixelManager::mirrorData(const vector<nsGraphics::RGBAcolor>& inPixels, unsigned rowSize) {
vector<RGBAcolor> outPixels;
// we reserve size so the vector doesn't dynamically grows
outPixels.reserve(inPixels.size());
//for each line of pixel
for(unsigned rowOffset=0; rowOffset < inPixels.size(); rowOffset+=rowSize){
// for each pixel of that line
for(unsigned j=0;j<rowSize;++j) {
// push back the pixel opposed to this one (still in the same line)
outPixels.push_back(inPixels[rowOffset + rowSize - j - 1]);
}
}
return std::move(outPixels);
}