Reestructurados los ficheros en carpetas

This commit is contained in:
2022-10-20 16:31:22 +02:00
parent ce509c6aa6
commit 764463fa56
40 changed files with 50 additions and 50 deletions

31
source/background.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include "background.h"
//Constructor
Background::Background()
{
init(0, 0, 0, 0, NULL);
}
//Inicializador
void Background::init(int x, int y, int w, int h, LTexture *texture)
{
//Establece el alto y el ancho del sprite del fondo
mSprite.setWidth(w);
mSprite.setHeight(h);
//Establece la posición X,Y del sprite del fondo
mSprite.setPosX(x);
mSprite.setPosY(y);
//Establece la textura donde están los gráficos para el sprite del fondo
mSprite.setTexture(*texture);
//Establece el rectangulo de donde coger la imagen
mSprite.setSpriteClip(0, 0, mSprite.getWidth(), mSprite.getHeight());
}
//Pinta el fondo en la pantalla
void Background::render()
{
mSprite.render();
}

26
source/background.h Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include "sprite.h"
#ifndef BACKGROUND_H
#define BACKGROUND_H
//Clase para el fondo de pantalla del juego
class Background
{
public:
//Constructor
Background();
//Inicializador
void init(int x, int y, int w, int h, LTexture *texture);
//Pinta el fondo en la pantalla
void render();
private:
//Variables
Sprite mSprite;
};
#endif

467
source/balloon.cpp Normal file
View File

@@ -0,0 +1,467 @@
#include "balloon.h"
//Constructor
Balloon::Balloon()
{
init(0, 0, NO_KIND, BALLON_VELX_POSITIVE, 0);
}
//Inicializador
void Balloon::init(int x, int y, Uint8 kind, float velx, Uint16 creationtimer)
{
switch (kind)
{
case BALLOON_1:
//Posición inicial
mPosX = x;
mPosY = y;
//Alto y ancho del objeto
mWidth = 8;
mHeight = mWidth;
//Inicializa los valores de velocidad y gravedad
mVelX = velx;
mVelY = 0;
mMaxVelY = 3;
mGravity = 0.09;
mDefaultVelY = 3;
//Puntos que da el globo al ser destruido
mScore = 50;
//Rectangulo con la imagen del sprite
mSprite.setSpriteClip(37 + 21 + 13, 0, mWidth, mHeight);
break;
case BALLOON_2:
//Posición inicial
mPosX = x;
mPosY = y;
//Alto y ancho del objeto
mWidth = 13;
mHeight = mWidth;
//Inicializa los valores de velocidad y gravedad
mVelX = velx;
mVelY = 0;
mMaxVelY = 3;
mGravity = 0.10;
mDefaultVelY = 4;
//Puntos que da el globo al ser destruido
mScore = 100;
//Rectangulo con la imagen del sprite
mSprite.setSpriteClip(37 + 21, 0, mWidth, mHeight);
break;
case BALLOON_3:
//Posición inicial
mPosX = x;
mPosY = y;
//Alto y ancho del objeto
mWidth = 21;
mHeight = mWidth;
//Inicializa los valores de velocidad y gravedad
mVelX = velx;
mVelY = 0;
mMaxVelY = 3;
mGravity = 0.10;
mDefaultVelY = 4.5;
//Puntos que da el globo al ser destruido
mScore = 200;
//Rectangulo con la imagen del sprite
mSprite.setSpriteClip(37, 0, mWidth, mHeight);
break;
case BALLOON_4:
//Posición inicial
mPosX = x;
mPosY = y;
//Alto y ancho del objeto
mWidth = 37;
mHeight = mWidth;
//Inicializa los valores de velocidad y gravedad
mVelX = velx;
mVelY = 0;
mMaxVelY = 3;
mGravity = 0.10;
mDefaultVelY = 5;
//Puntos que da el globo al ser destruido
mScore = 400;
//Rectangulo con la imagen del sprite
mSprite.setSpriteClip(0, 0, mWidth, mHeight);
break;
default:
//Posición inicial
mPosX = x;
mPosY = y;
//Alto y ancho del objeto
mWidth = 0;
mHeight = mWidth;
//Inicializa los valores de velocidad y gravedad
mVelX = velx;
mVelY = 0;
mMaxVelY = 0;
mGravity = 0;
mDefaultVelY = 0;
//Puntos que da el globo al ser destruido
mScore = 0;
//Rectangulo con la imagen del sprite
mSprite.setSpriteClip(0, 0, mWidth, mHeight);
break;
}
//Textura con los gráficos del sprite
mSprite.setTexture(gBalloonTexture);
//Alto y ancho del sprite
mSprite.setWidth(mWidth);
mSprite.setHeight(mHeight);
//Posición X,Y del sprite
mSprite.setPosX(mPosX);
mSprite.setPosY(mPosY);
//Tamaño del circulo de colisión
mCollider.r = mWidth / 2;
//Alinea el circulo de colisión con el objeto
shiftColliders();
//Inicializa variables
mStopped = true;
mStoppedTimer = 0;
mBlinking = false;
mVisible = true;
mInvulnerable = false;
mBeingCreated = true;
mCreationTimer = creationtimer;
//Tipo
mKind = kind;
}
//Centra el globo en la posición X
void Balloon::allignTo(int x)
{
mPosX = x - (mWidth / 2);
if (mPosX < PLAY_AREA_LEFT)
{
mPosX = PLAY_AREA_LEFT + 1;
}
else if ((mPosX + mWidth) > PLAY_AREA_RIGHT)
{
mPosX = PLAY_AREA_RIGHT - mWidth - 1;
}
//Posición X,Y del sprite
mSprite.setPosX(getPosX());
mSprite.setPosY(getPosY());
//Alinea el circulo de colisión con el objeto
shiftColliders();
}
//Pinta el globo en la pantalla
void Balloon::render()
{
if (mVisible)
{
mSprite.render();
}
}
//Actualiza la posición y estados del globo
void Balloon::move()
{
//Comprobamos si se puede mover
if (isStopped() == false)
{
//Lo movemos a izquierda o derecha
mPosX += mVelX;
//Si queda fuera de pantalla, corregimos su posición y cambiamos su sentido
if ((mPosX < PLAY_AREA_LEFT) || (mPosX + mWidth > PLAY_AREA_RIGHT))
{
//Corregir posición
mPosX -= mVelX;
//Invertir sentido
mVelX = -mVelX;
}
//Mueve el globo hacia arriba o hacia abajo
mPosY += int(mVelY);
//Si se sale por arriba
if (mPosY < PLAY_AREA_TOP)
{
//Corregimos
mPosY -= int(mVelY);
//Invertimos sentido
mVelY = -mVelY;
}
//Si el globo se sale por la parte inferior
if (mPosY + mHeight > PLAY_AREA_BOTTOM)
{
//Corregimos
mPosY -= int(mVelY);
//Invertimos colocando una velocidad por defecto
mVelY = -mDefaultVelY;
}
//Aplica gravedad al objeto, sin pasarse de un limite establecido
if (int(mVelY) > mMaxVelY)
{
mVelY = mMaxVelY;
}
else
{
mVelY += mGravity;
}
//Actualiza la posición del sprite
mSprite.setPosX(getPosX());
mSprite.setPosY(mPosY);
//Actualiza la posición del circulo de colisión
shiftColliders();
}
//Si no se puede mover:
//Comprobar si se está creando
else if (isBeingCreated() == true)
{
//Actualiza el valor de las variables
setStop(true);
setInvulnerable(true);
//Todavia tiene tiempo en el contador
if (mCreationTimer > 0)
{
//Desplaza lentamente el globo hacia abajo y hacia un lado
if (mCreationTimer % 10 == 0)
{
++mPosY;
mPosX += mVelX;
//Actualiza la posición del sprite
mSprite.setPosX(getPosX());
mSprite.setPosY(mPosY);
//Actualiza la posición del circulo de colisión
shiftColliders();
}
//Hace visible el globo de forma intermitente
if (mCreationTimer > 100)
{
setVisible(mCreationTimer / 10 % 2 == 0);
}
else
{
setVisible(mCreationTimer / 5 % 2 == 0);
}
--mCreationTimer;
}
//El contador ha llegado a cero
else
{
setBeingCreated(false);
setStop(false);
setVisible(true);
setInvulnerable(false);
}
}
//Comprobar si está detenido
else if (isStopped() == true)
{
//Si todavía está detenido, reduce el contador
if (mStoppedTimer > 0)
{
--mStoppedTimer;
} //Si el contador ha llegado a cero, ya no está detenido
else
{
setStop(false);
}
}
}
//Pone a cero todos los valores del globo
void Balloon::erase()
{
init(0, 0, NO_KIND, 0, 0);
}
//Comprueba si el globo tiene algun tipo asignado
bool Balloon::isActive()
{
if (mKind == NO_KIND)
{
return false;
}
else
{
return true;
}
}
//Obtiene del valor de la variable
int Balloon::getPosX()
{
return int(mPosX);
}
//Obtiene del valor de la variable
int Balloon::getPosY()
{
return mPosY;
}
//Obtiene del valor de la variable
float Balloon::getVelY()
{
return mVelY;
}
//Obtiene del valor de la variable
int Balloon::getWidth()
{
return mWidth;
}
//Obtiene del valor de la variable
int Balloon::getHeight()
{
return mHeight;
}
//Establece el valor de la variable
void Balloon::setVelY(float velY)
{
mVelY = velY;
}
//Obtiene del valor de la variable
int Balloon::getKind()
{
return mKind;
}
//Establece el valor de la variable
void Balloon::setStop(bool state)
{
mStopped = state;
}
//Obtiene del valor de la variable
bool Balloon::isStopped()
{
return mStopped;
}
//Establece el valor de la variable
void Balloon::setBlink(bool state)
{
mBlinking = state;
}
//Obtiene del valor de la variable
bool Balloon::isBlinking()
{
return mBlinking;
}
//Establece el valor de la variable
void Balloon::setVisible(bool state)
{
mVisible = state;
}
//Obtiene del valor de la variable
bool Balloon::isVisible()
{
return mVisible;
}
//Establece el valor de la variable
void Balloon::setInvulnerable(bool state)
{
mInvulnerable = state;
}
//Obtiene del valor de la variable
bool Balloon::isInvulnerable()
{
return mInvulnerable;
}
//Establece el valor de la variable
void Balloon::setBeingCreated(bool state)
{
mBeingCreated = state;
}
//Obtiene del valor de la variable
bool Balloon::isBeingCreated()
{
return mBeingCreated;
}
//Establece el valor de la variable
void Balloon::setStoppedTimer(Uint16 time)
{
mStoppedTimer = time;
}
//Obtiene del valor de la variable
Uint16 Balloon::getStoppedTimer()
{
return mStoppedTimer;
}
//Obtiene del valor de la variable
Uint16 Balloon::getScore()
{
return mScore;
}
//Obtiene el circulo de colisión
Circle &Balloon::getCollider()
{
return mCollider;
}
//Alinea el circulo de colisión con la posición del objeto globo
void Balloon::shiftColliders()
{
//Align collider to center of balloon
mCollider.x = mPosX + mCollider.r;
mCollider.y = mPosY + mCollider.r;
}

152
source/balloon.h Normal file
View File

@@ -0,0 +1,152 @@
#pragma once
#include "struct.h"
#include "sprite.h"
#include "const.h"
#include "globals.h"
#ifndef BALLOON_H
#define BALLOON_H
//Clase globo
class Balloon
{
public:
//Constructor
Balloon();
//Inicializador
void init(int x, int y, Uint8 kind, float velx, Uint16 creationtimer);
//Centra el globo en la posición X
void allignTo(int x);
//Pinta el globo en la pantalla
void render();
//Actualiza la posición y estados del globo
void move();
//Pone a cero todos los valores del globo
void erase();
//Comprueba si el globo tiene algun tipo asignado
bool isActive();
//Obtiene del valor de la variable
int getPosX();
//Obtiene del valor de la variable
int getPosY();
//Obtiene del valor de la variable
float getVelY();
//Obtiene del valor de la variable
int getWidth();
//Obtiene del valor de la variable
int getHeight();
//Establece el valor de la variable
void setVelY(float velY);
//Obtiene del valor de la variable
int getKind();
//Establece el valor de la variable
void setStop(bool state);
//Obtiene del valor de la variable
bool isStopped();
//Establece el valor de la variable
void setBlink(bool state);
//Obtiene del valor de la variable
bool isBlinking();
//Establece el valor de la variable
void setVisible(bool state);
//Obtiene del valor de la variable
bool isVisible();
//Establece el valor de la variable
void setInvulnerable(bool state);
//Obtiene del valor de la variable
bool isInvulnerable();
//Establece el valor de la variable
void setBeingCreated(bool state);
//Obtiene del valor de la variable
bool isBeingCreated();
//Establece el valor de la variable
void setStoppedTimer(Uint16 time);
//Obtiene del valor de la variable
Uint16 getStoppedTimer();
//Obtiene del valor de la variable
Uint16 getScore();
//Obtiene el circulo de colisión
Circle &getCollider();
private:
//Posición X,Y del objeto globo
float mPosX;
int mPosY;
//Alto y ancho del objeto globo
Uint8 mWidth;
Uint8 mHeight;
//Variables para controlar la velocidad del globo
float mVelX;
float mVelY;
float mGravity;
float mDefaultVelY;
int mMaxVelY;
//Puntos que da el globo al ser destruido
Uint16 mScore;
//Indica si el globo está parado
bool mStopped;
//Temporizador para controlar el estado "parado"
Uint16 mStoppedTimer;
//Indica si el globo está intermitente
bool mBlinking;
//Indica si el globo es visible
bool mVisible;
//Indica si el globo es invulnerable
bool mInvulnerable;
//Indica si el globo se está creando
bool mBeingCreated;
//Temporizador para controlar el estado "creandose"
Uint16 mCreationTimer;
//Tipo de globo
Uint8 mKind;
//Sprite del objeto globo
Sprite mSprite;
//Circulo de colisión del objeto
Circle mCollider;
//Alinea el circulo de colisión con la posición del objeto globo
void shiftColliders();
};
#endif

190
source/bullet.cpp Normal file
View File

@@ -0,0 +1,190 @@
#include "bullet.h"
//Constructor
Bullet::Bullet()
{
init(0, 0, NO_KIND);
}
//Iniciador
void Bullet::init(int x, int y, int kind)
{
//Posición inicial del objeto
mPosX = x;
mPosY = y;
//Alto y ancho del objeto
mWidth = 8;
mHeight = mWidth;
//Velocidad inicial en el eje Y
mVelY = -3;
//Tipo de bala
mKind = kind;
//Textura con los gráficos del objeto
mSprite.setTexture(gBulletTexture);
//Alto y ancho del sprite
mSprite.setWidth(mWidth);
mSprite.setHeight(mHeight);
//Posición inicial del sprite
mSprite.setPosX(mPosX);
mSprite.setPosY(mPosY);
//Valores especificos según el tipo
switch (kind)
{
case BULLET_UP:
//Establece la velocidad inicial
mVelX = 0;
//Rectangulo con los gráficos del objeto
mSprite.setSpriteClip(0 * mWidth, 0, mSprite.getWidth(), mSprite.getHeight());
break;
case BULLET_LEFT:
//Establece la velocidad inicial
mVelX = -2;
//Rectangulo con los gráficos del objeto
mSprite.setSpriteClip(1 * mWidth, 0, mSprite.getWidth(), mSprite.getHeight());
break;
case BULLET_RIGHT:
//Establece la velocidad inicial
mVelX = 2;
//Rectangulo con los gráficos del objeto
mSprite.setSpriteClip(2 * mWidth, 0, mSprite.getWidth(), mSprite.getHeight());
break;
default:
break;
}
//Establece el tamaño del circulo de colisión
mCollider.r = mWidth / 2;
//Alinea el circulo de colisión con el objeto
shiftColliders();
}
//Pinta el objeto en pantalla
void Bullet::render()
{
mSprite.render();
}
//Actualiza la posición y estado del objeto en horizontal
void Bullet::move()
{
//Mueve el objeto a su nueva posición
mPosX += mVelX;
//Si el objeto se sale del area de juego por los laterales
if ((mPosX < PLAY_AREA_LEFT) || (mPosX + mWidth > PLAY_AREA_RIGHT))
{
//Se deshabilita
mKind = NO_KIND;
}
//Mueve el objeto a su nueva posición en vertical
mPosY += int(mVelY);
//Si el objeto se sale del area de juego por la parte superior o inferior
if ((mPosY < PLAY_AREA_TOP) || (mPosY + mHeight > PLAY_AREA_BOTTOM))
{
//Se deshabilita
mKind = NO_KIND;
}
//Actualiza la posición del sprite
mSprite.setPosX(mPosX);
mSprite.setPosY(mPosY);
//Alinea el circulo de colisión con el objeto
shiftColliders();
}
#ifdef TEST
void Bullet::testMove()
{
//Update sprite position
mSprite.setPosX(mPosX);
mSprite.setPosY(mPosY);
//Update circle colliders
shiftColliders();
}
#endif
//Deshabilita el objeto
void Bullet::erase()
{
mKind = NO_KIND;
}
//Comprueba si el objeto está activo
bool Bullet::isActive()
{
if (mKind == NO_KIND)
{
return false;
}
else
{
return true;
}
}
//Obtiene el valor de la variable
int Bullet::getPosX()
{
return mPosX;
}
//Obtiene el valor de la variable
int Bullet::getPosY()
{
return mPosY;
}
//Establece el valor de la variable
void Bullet::setPosX(int x)
{
mPosX = x;
}
//Establece el valor de la variable
void Bullet::setPosY(int y)
{
mPosY = y;
}
//Obtiene el valor de la variable
float Bullet::getVelY()
{
return mVelY;
}
//Obtiene el valor de la variable
int Bullet::getKind()
{
return mKind;
}
//Obtiene el circulo de colisión
Circle &Bullet::getCollider()
{
return mCollider;
}
//Alinea el circulo de colisión con el objeto
void Bullet::shiftColliders()
{
mCollider.x = mPosX + mCollider.r;
mCollider.y = mPosY + mCollider.r;
}

82
source/bullet.h Normal file
View File

@@ -0,0 +1,82 @@
#pragma once
#include "struct.h"
#include "sprite.h"
#include "const.h"
#include "globals.h"
#ifndef BULLET_H
#define BULLET_H
//Clase bala
class Bullet
{
public:
//Constructor
Bullet();
//Iniciador
void init(int x, int y, int kind);
//Pinta el objeto en pantalla
void render();
//Actualiza la posición y estado del objeto
void move();
#ifdef TEST
void testMove();
#endif
//Deshabilita el objeto
void erase();
//Comprueba si el objeto está activo
bool isActive();
//Obtiene el valor de la variable
int getPosX();
//Obtiene el valor de la variable
int getPosY();
//Establece el valor de la variable
void setPosX(int x);
//Establece el valor de la variable
void setPosY(int y);
//Obtiene el valor de la variable
float getVelY();
//Obtiene el valor de la variable
int getKind();
//Obtiene el circulo de colisión
Circle &getCollider();
private:
//Posición X/Y del objeto
int mPosX;
int mPosY;
//Alto y ancho el objeto
Uint8 mWidth;
Uint8 mHeight;
//Velocidad del objeto
int mVelX;
int mVelY;
//Tipo de objeto
int mKind;
//Sprite con los graficos y métodos de pintado
Sprite mSprite;
//Balloon's collision circle
Circle mCollider;
//Alinea el circulo de colisión con el objeto
void shiftColliders();
};
#endif

111
source/const.h Normal file
View File

@@ -0,0 +1,111 @@
#pragma once
#include <SDL2/SDL.h>
#ifndef CONST_H
#define CONST_H
//Tamaño de bloque
const int BLOCK = 8;
//Tamaño de la pantalla real
const int SCREEN_WIDTH = 256;
const int SCREEN_HEIGHT = SCREEN_WIDTH * 3 / 4;
//Tamaño de la pantalla que se muestra
const int VIEW_WIDTH = SCREEN_WIDTH * 3;
const int VIEW_HEIGHT = SCREEN_HEIGHT * 3;
//Zona de juego
const int PLAY_AREA_TOP = (0 * BLOCK);
const int PLAY_AREA_BOTTOM = SCREEN_HEIGHT - (4 * BLOCK);
const int PLAY_AREA_LEFT = (0 * BLOCK);
const int PLAY_AREA_RIGHT = SCREEN_WIDTH - (0 * BLOCK);
const int PLAY_AREA_WIDTH = PLAY_AREA_RIGHT - PLAY_AREA_LEFT;
const int PLAY_AREA_HEIGHT = PLAY_AREA_BOTTOM - PLAY_AREA_TOP;
//Color transparente para los sprites
const int COLOR_KEY_R = 0xff;
const int COLOR_KEY_G = 0x00;
const int COLOR_KEY_B = 0xff;
//Opciones de menu
const int MENU_NO_OPTION = -1;
const int MENU_OPTION_START = 0;
const int MENU_OPTION_QUIT = 1;
const int MENU_OPTION_TOTAL = 2;
//Selector de menu
const int MENU_SELECTOR_BLACK = (BLOCK * 0);
const int MENU_SELECTOR_WHITE = (BLOCK * 1);
//Tipos de fondos para el menu
const int MENU_BACKGROUND_TRANSPARENT = 0;
const int MENU_BACKGROUND_SOLID = 1;
//Estados del jugador
const int PLAYER_STATE_STOPPED = 0;
const int PLAYER_STATE_WALKING_LEFT = 1;
const int PLAYER_STATE_WALKING_RIGHT = 2;
const int PLAYER_STATE_TOTAL = 3;
//Estados del juego
const int GAME_STATE_TITLE = 0;
const int GAME_STATE_PLAYING = 1;
const int GAME_STATE_PAUSED = 2;
const int GAME_STATE_QUIT = 3;
const int GAME_STATE_TOTAL = 4;
//Anclajes para el marcador de puntos
const int SCORE_WORD_X = (SCREEN_WIDTH / 4) - ((5 * BLOCK) / 2);
const int SCORE_WORD_Y = SCREEN_HEIGHT - (3 * BLOCK) + 4;
const int SCORE_NUMBER_X = (SCREEN_WIDTH / 4) - (3 * BLOCK);
const int SCORE_NUMBER_Y = SCREEN_HEIGHT - (2 * BLOCK) + 4;
const int HISCORE_WORD_X = ((SCREEN_WIDTH / 4) * 3) - ((8 * BLOCK) / 2);
const int HISCORE_WORD_Y = SCREEN_HEIGHT - (3 * BLOCK) + 4;
const int HISCORE_NUMBER_X = ((SCREEN_WIDTH / 4) * 3) - (3 * BLOCK);
const int HISCORE_NUMBER_Y = SCREEN_HEIGHT - (2 * BLOCK) + 4;
//Ningun tipo
const int NO_KIND = 0;
//Tipos de globo
const int BALLOON_1 = 1;
const int BALLOON_2 = 2;
const int BALLOON_3 = 3;
const int BALLOON_4 = 4;
//Velocidad del globo
const float BALLON_VELX_POSITIVE = 0.7;
const float BALLON_VELX_NEGATIVE = -0.7;
//Tipos de bala
const int BULLET_UP = 1;
const int BULLET_LEFT = 2;
const int BULLET_RIGHT = 3;
//Estados de entrada
const int NO_INPUT = 0;
const int INPUT_UP = 1;
const int INPUT_DOWN = 2;
const int INPUT_LEFT = 3;
const int INPUT_RIGHT = 4;
const int INPUT_FIRE = 5;
//Zona muerta del mando analógico
const int JOYSTICK_DEAD_ZONE = 8000;
//Mapeo de bottones (8bitdo)
const int BUTTON_A = 0;
const int BUTTON_B = 1;
const int BUTTON_X = 3;
const int BUTTON_Y = 4;
const int BUTTON_SELECT = 10;
const int BUTTON_START = 11;
const int BUTTON_L = 6;
const int BUTTON_R = 7;
//Estado del teclado SDL
//const Uint8 *keystates;
#endif

970
source/gamedirector.cpp Normal file
View File

@@ -0,0 +1,970 @@
#include "gamedirector.h"
//Calcula el cuadrado de la distancia entre dos puntos
double distanceSquared(int x1, int y1, int x2, int y2)
{
int deltaX = x2 - x1;
int deltaY = y2 - y1;
return deltaX * deltaX + deltaY * deltaY;
}
//Detector de colisiones entre dos circulos
bool checkCollision(Circle &a, Circle &b)
{
//Calcula el radio total al cuadrado
int totalRadiusSquared = a.r + b.r;
totalRadiusSquared = totalRadiusSquared * totalRadiusSquared;
//Si la distancia entre el centro de los circulos es inferior a la suma de sus radios
if (distanceSquared(a.x, a.y, b.x, b.y) < (totalRadiusSquared))
{
//Los circulos han colisionado
return true;
}
//En caso contrario
return false;
}
//Constructor
GameDirector::GameDirector()
{
init();
}
//Iniciador
void GameDirector::init()
{
//Variables
mGameStatus = GAME_STATE_TITLE;
mOldTicks = 0;
mMaxBalloons = 50;
mMaxBullets = 50;
mGameSpeed = 15;
mMenaceLevel = 0;
mMenaceLevelThreshold = 7;
mScore = 0;
mHiScore = 0;
mScoreText = std::to_string(mScore);
mHiScoreText = std::to_string(mHiScore);
mGetReady = true;
//Objeto jugador
player.init();
//Establece a cero todos los valores del vector de objetos globo
resetBalloons();
//Crea dos objetos globo y los centra en el area de juego
//balloon[0].init(0, BLOCK, BALLOON_4, BALLON_VELX_POSITIVE, 0);
//balloon[0].allignTo(PLAY_AREA_WIDTH / 2);
//balloon[1].init(0, BLOCK, BALLOON_4, BALLON_VELX_NEGATIVE, 0);
//balloon[1].allignTo(PLAY_AREA_WIDTH / 2);
//Con los globos creados, calcula el nivel de amenaza
calculateMenaceLevel();
//Establece a cero todos los valores del vector de objetos bala
resetBullets();
#ifdef TEST
balloonTest.init(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, BALLOON_4, 0);
balloonTest.stop();
bulletTest.init(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 2, BULLET_UP);
#endif
//Los fondos
gameBackground.init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - (0 * BLOCK), &gGameBackgroundTexture);
titleBackground.init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, &gTitleBackgroundTexture);
//Objetos texto, uno de cada color
whiteText.init(&gWhiteFontTexture);
blackText.init(&gBlackFontTexture);
//Inicializa el objeto con el menu del titulo
menuTitle.init(0, 16 * BLOCK, MENU_SELECTOR_WHITE, MENU_BACKGROUND_TRANSPARENT);
menuTitle.addItem("START");
menuTitle.addItem("EXIT");
menuTitle.setBackgroundColor(0, 0, 0, 255);
menuTitle.centerMenuOnScreen();
//Inicializa el objeto con el menu de pausa
menuPause.init(0, 12 * BLOCK, MENU_SELECTOR_WHITE, MENU_BACKGROUND_SOLID);
menuPause.addItem("CONTINUE");
menuPause.addItem("EXIT TO TITLE");
menuPause.setBackgroundColor(0x73, 0x27, 0x5c, 255);
menuPause.centerMenuOnScreen();
}
//Hace una pausa de milisegundos
void GameDirector::sleep(Uint16 time)
{
Uint32 ticks = SDL_GetTicks();
while (SDL_GetTicks() - ticks < time)
{
/* code */
}
}
//Establece el valor de la variable
void GameDirector::setScore(Uint32 score)
{
mScore = score;
}
//Establece el valor de la variable
void GameDirector::setHiScore(Uint32 score)
{
mHiScore = score;
}
//Actualiza el valor de HiScore en caso necesario
void GameDirector::updateHiScore()
{
if (mScore > mHiScore)
{
mHiScore = mScore;
}
}
//Transforma un valor numérico en una cadena de 6 cifras
std::string GameDirector::updateScoreText(Uint32 num)
{
switch (num)
{
case 0 ... 9:
return ("00000" + std::to_string(num));
break;
case 10 ... 99:
return ("0000" + std::to_string(num));
break;
case 100 ... 999:
return ("000" + std::to_string(num));
break;
case 1000 ... 9999:
return ("00" + std::to_string(num));
break;
case 10000 ... 99999:
return ("0" + std::to_string(num));
break;
case 100000 ... 999999:
return (std::to_string(num));
break;
default:
return (std::to_string(num));
break;
}
}
//Pinta el marcador en pantalla usando un objeto texto
void GameDirector::renderScoreBoard(Text &text)
{
mScoreText = updateScoreText(mScore);
mHiScoreText = updateScoreText(mHiScore);
text.write(SCORE_WORD_X, SCORE_WORD_Y, "SCORE");
text.write(SCORE_NUMBER_X, SCORE_NUMBER_Y, mScoreText);
text.write(HISCORE_WORD_X, HISCORE_WORD_Y, "HI-SCORE");
text.write(HISCORE_NUMBER_X, HISCORE_NUMBER_Y, mHiScoreText);
}
//Mueve todos los globos activos
void GameDirector::moveBalloons()
{
for (Uint8 i = 0; i < mMaxBalloons; i++)
{
if (balloon[i].isActive())
{
balloon[i].move();
}
}
}
//Pinta en pantalla todos los globos activos
void GameDirector::renderBalloons()
{
for (Uint8 i = 0; i < mMaxBalloons; i++)
{
if (balloon[i].isActive())
{
balloon[i].render();
}
}
}
//Devuelve el primer indice no activo del vector de globos
Uint8 GameDirector::getBallonFreeIndex()
{
int index = 0;
for (Uint8 i = 0; i < mMaxBalloons; i++)
{
if (balloon[i].isActive() == false)
{
index = i;
break;
}
}
return index;
}
//Crea un globo nuevo en el vector de globos
Uint8 GameDirector::createNewBalloon(int x, int y, Uint8 kind, float velx, Uint16 creationtimer)
{
Uint8 index = getBallonFreeIndex();
balloon[index].init(x, y, kind, velx, creationtimer);
return index;
}
//Establece a cero todos los valores del vector de objetos globo
void GameDirector::resetBalloons()
{
for (Uint8 i = 0; i < mMaxBalloons; i++)
{
balloon[i].erase();
}
}
//Explosiona un globo. Lo destruye y crea otros dos si es el caso
void GameDirector::popBalloon(Uint8 index)
{
if (balloon[index].isActive())
{
Uint8 kind = balloon[index].getKind();
Uint8 freeIndex = 0;
switch (kind)
{
//Si es del tipo más pequeño, simplemente elimina el globo
case BALLOON_1:
balloon[index].erase();
break;
//En cualquier otro caso, crea dos globos de un tipo inferior
default:
freeIndex = getBallonFreeIndex();
balloon[freeIndex].init(0, balloon[index].getPosY(), balloon[index].getKind() - 1, BALLON_VELX_NEGATIVE, 0);
balloon[freeIndex].allignTo(balloon[index].getPosX() + (balloon[index].getWidth() / 2));
balloon[freeIndex].setVelY(-2.5);
freeIndex = getBallonFreeIndex();
balloon[freeIndex].init(0, balloon[index].getPosY(), balloon[index].getKind() - 1, BALLON_VELX_POSITIVE, 0);
balloon[freeIndex].allignTo(balloon[index].getPosX() + (balloon[index].getWidth() / 2));
balloon[freeIndex].setVelY(-2.5);
//Elimina el globo
balloon[index].erase();
break;
}
}
}
//Detiene todos los globos
void GameDirector::stopAllBalloons()
{
for (Uint8 i = 0; i < mMaxBalloons; i++)
{
if (balloon[i].isActive())
{
balloon[i].setStop(true);
}
}
}
//Pone en marcha todos los globos
void GameDirector::startAllBalloons()
{
for (Uint8 i = 0; i < mMaxBalloons; i++)
{
if (balloon[i].isActive())
{
balloon[i].setStop(false);
}
}
}
//Obtiene el numero de globos activos
Uint8 GameDirector::countBalloons()
{
Uint8 num = 0;
for (Uint8 i = 0; i < mMaxBalloons; i++)
{
if (balloon[i].isActive())
{
++num;
}
}
return num;
}
//Comprueba la colisión entre el jugador y los globos activos
bool GameDirector::checkPlayerBallonCollision()
{
bool result = false;
for (Uint8 i = 0; i < mMaxBalloons; i++)
{
if (balloon[i].isActive())
{
if (checkCollision(player.getCollider(), balloon[i].getCollider()))
{
result = true;
break;
}
}
}
return result;
}
//Comprueba y procesa la colisión entre las balas y los globos
void GameDirector::processBulletBallonCollision()
{
for (Uint8 i = 0; i < mMaxBalloons; i++)
{
for (Uint8 j = 0; j < mMaxBullets; j++)
{
if (balloon[i].isActive() && !(balloon[i].isInvulnerable()) && bullet[j].isActive())
{
if (checkCollision(balloon[i].getCollider(), bullet[j].getCollider()))
{
player.addScore(balloon[i].getScore());
setScore(player.getScore());
updateHiScore();
popBalloon(i);
Mix_PlayChannel(-1, gPopBalloonFX, 0);
bullet[j].erase();
calculateMenaceLevel();
break;
}
}
}
}
}
//Mueve las balas activas
void GameDirector::moveBullets()
{
for (Uint8 i = 0; i < mMaxBullets; i++)
{
if (bullet[i].isActive())
{
bullet[i].move();
}
}
}
//Pinta las balas activas
void GameDirector::renderBullets()
{
for (Uint8 i = 0; i < mMaxBullets; i++)
{
if (bullet[i].isActive())
{
bullet[i].render();
}
}
}
//Devuelve el primer indice no activo del vector de balas
Uint8 GameDirector::getBulletFreeIndex()
{
Uint8 index = 0;
for (int i = 0; i < mMaxBullets; i++)
{
if (bullet[i].isActive() == false)
{
index = i;
break;
}
}
return index;
}
//Establece a cero todos los valores del vector de objetos bala
void GameDirector::resetBullets()
{
for (Uint8 i = 0; i < mMaxBullets; i++)
{
bullet[i].init(0, 0, NO_KIND);
}
}
//Crea un objeto bala
void GameDirector::createBullet(int x, int y, Uint8 kind)
{
bullet[getBulletFreeIndex()].init(x, y, kind);
}
//Calcula y establece el valor de amenaza en funcion de los globos activos
void GameDirector::calculateMenaceLevel()
{
mMenaceLevel = 0;
for (Uint8 i = 0; i < mMaxBalloons; i++)
{
switch (balloon[i].getKind())
{
case BALLOON_1:
mMenaceLevel += 1;
break;
case BALLOON_2:
mMenaceLevel += 2;
break;
case BALLOON_3:
mMenaceLevel += 4;
break;
case BALLOON_4:
mMenaceLevel += 8;
break;
default:
mMenaceLevel += 0;
break;
}
}
}
//Obtiene el valor de la variable
Uint8 GameDirector::getMenaceLevel()
{
return mMenaceLevel;
}
//Gestiona el nivel de amenaza
void GameDirector::checkMenaceLevel()
{
//Aumenta el nivel de amenaza en función de la puntuación
mMenaceLevelThreshold = 7 + (4 * (mScore / 10000));
//Si el nivel de amenza es inferior al umbral
if (mMenaceLevel < mMenaceLevelThreshold)
{
Uint8 index = 0;
//Obtiene el centro del jugador en el eje X
int x = player.getPosX() + (player.getWidth() / 2);
//Crea un globo sobre el jugador en dirección hacia el centro
if (x < (PLAY_AREA_WIDTH / 2))
{
index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_POSITIVE, 400);
}
else
{
index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_NEGATIVE, 400);
}
balloon[index].allignTo(x);
//Recalcula el nivel de amenaza con el nuevo globo
calculateMenaceLevel();
}
}
//Gestiona la entrada de teclado y mando durante el juego
void GameDirector::checkGameInput()
{
//Obtiene el estado de las teclas pulsadas del teclado
const Uint8 *keystates = SDL_GetKeyboardState(NULL);
//Si está pulsada la tecla izquierda o el mando hacia la izquierda
if ((keystates[SDL_SCANCODE_LEFT] != 0) || (SDL_JoystickGetAxis(gGameController, 0) < -JOYSTICK_DEAD_ZONE))
{
player.checkInput(INPUT_LEFT);
}
//Si está pulsada la tecla derecha o el mando hacia la derecha
else if ((keystates[SDL_SCANCODE_RIGHT] != 0) || (SDL_JoystickGetAxis(gGameController, 0) > JOYSTICK_DEAD_ZONE))
{
player.checkInput(INPUT_RIGHT);
}
//Ninguna de las dos direcciones pulsadas
else
{
player.checkInput(NO_INPUT);
}
//Comprobamos la tecla o el botón de disparo central
if ((SDL_JoystickGetButton(gGameController, BUTTON_X)) || (keystates[SDL_SCANCODE_W] != 0))
{
if (player.canFire())
{
createBullet(player.getPosX() + (player.getWidth() / 2) - 4, player.getPosY(), BULLET_UP);
player.setFireCooldown(10);
//Reproduce el sonido de disparo
Mix_PlayChannel(-1, gBulletFX, 0);
}
}
//Comprobamos la tecla o el botón de disparo izquierdo
if ((SDL_JoystickGetButton(gGameController, BUTTON_Y)) || (keystates[SDL_SCANCODE_Q] != 0))
{
if (player.canFire())
{
createBullet(player.getPosX() + (player.getWidth() / 2) - 4, player.getPosY(), BULLET_LEFT);
player.setFireCooldown(10);
//Reproduce el sonido de disparo
Mix_PlayChannel(-1, gBulletFX, 0);
}
}
//Comprobamos la tecla o el botón de disparo derecho
if ((SDL_JoystickGetButton(gGameController, BUTTON_A)) || (keystates[SDL_SCANCODE_E] != 0))
{
if (player.canFire())
{
createBullet(player.getPosX() + (player.getWidth() / 2) - 4, player.getPosY(), BULLET_RIGHT);
player.setFireCooldown(10);
//Reproduce el sonido de disparo
Mix_PlayChannel(-1, gBulletFX, 0);
}
}
//Comprobamos la tecla o el botón de pausa/menu
if ((SDL_JoystickGetButton(gGameController, BUTTON_START)) || (keystates[SDL_SCANCODE_ESCAPE] != 0))
{
setGameStatus(GAME_STATE_PAUSED);
//Detiene la música
Mix_HaltMusic();
}
}
//Gestiona la entrada de teclado y mando durante el menu
void GameDirector::checkMenuInput(Menu *menu)
{
//Obtiene el estado de las teclas pulsadas del teclado
const Uint8 *keystates = SDL_GetKeyboardState(NULL);
//Si está pulsada la tecla izquierda o el mando hacia la izquierda
if ((keystates[SDL_SCANCODE_UP] != 0) || (SDL_JoystickGetAxis(gGameController, 1) < -JOYSTICK_DEAD_ZONE))
{
menu->checkInput(INPUT_UP);
}
//Si está pulsada la tecla derecha o el mando hacia la derecha
else if ((keystates[SDL_SCANCODE_DOWN] != 0) || (SDL_JoystickGetAxis(gGameController, 1) > JOYSTICK_DEAD_ZONE))
{
menu->checkInput(INPUT_DOWN);
}
//Comprobamos la tecla o el botón de menu/pausa
else if (keystates[SDL_SCANCODE_RETURN] != 0 || (SDL_JoystickGetButton(gGameController, BUTTON_A)))
{
menu->checkInput(INPUT_FIRE);
}
#ifdef TEST
if (SDL_JoystickGetButton(gGameController, 1))
{
std::cout << "button1\n";
}
if (SDL_JoystickGetButton(gGameController, 1))
{
std::cout << "button1\n";
}
if (SDL_JoystickGetButton(gGameController, 2))
{
std::cout << "button2\n";
}
if (SDL_JoystickGetButton(gGameController, 3))
{
std::cout << "button3\n";
}
if (SDL_JoystickGetButton(gGameController, 4))
{
std::cout << "button4\n";
}
if (SDL_JoystickGetButton(gGameController, 5))
{
std::cout << "button5\n";
}
if (SDL_JoystickGetButton(gGameController, 6))
{
std::cout << "button6\n";
}
if (SDL_JoystickGetButton(gGameController, 7))
{
std::cout << "button7\n";
}
if (SDL_JoystickGetButton(gGameController, 8))
{
std::cout << "button8\n";
}
if (SDL_JoystickGetButton(gGameController, 9))
{
std::cout << "button9\n";
}
if (SDL_JoystickGetButton(gGameController, 10))
{
std::cout << "button10\n";
}
if (SDL_JoystickGetButton(gGameController, 11))
{
std::cout << "button11\n";
}
if (SDL_JoystickGetButton(gGameController, 12))
{
std::cout << "button12\n";
}
if (SDL_JoystickGetButton(gGameController, 13))
{
std::cout << "button13\n";
}
if (SDL_JoystickGetButton(gGameController, 14))
{
std::cout << "button14\n";
}
if (SDL_JoystickGetButton(gGameController, 15))
{
std::cout << "button15\n";
}
#endif
}
//Obtiene el valor de la variable
Uint8 GameDirector::getGameStatus()
{
return mGameStatus;
}
//Establece el valor de la variable
void GameDirector::setGameStatus(Uint8 status)
{
mGameStatus = status;
}
//Pinta una transición en pantalla
void GameDirector::renderTransition(Uint8 index)
{
switch (index)
{
case 0:
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = SCREEN_WIDTH;
rect.h = 0;
SDL_RenderPresent(gRenderer);
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0);
for (Uint16 i = 0; i < SCREEN_HEIGHT; i = i + 2)
{
rect.h = i;
SDL_RenderFillRect(gRenderer, &rect);
SDL_RenderPresent(gRenderer);
}
break;
case 1:
SDL_Rect rect1;
rect1.x = 0;
rect1.y = 0;
rect1.w = SCREEN_WIDTH;
rect1.h = 0;
SDL_Rect rect2;
rect2.x = 0;
rect2.y = 0;
rect2.w = SCREEN_WIDTH;
rect2.h = 0;
SDL_RenderPresent(gRenderer);
SDL_SetRenderDrawBlendMode(gRenderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 64);
for (Uint16 i = 0; i < (SCREEN_HEIGHT / 2); i = i + 4)
{
rect1.h = i;
SDL_RenderFillRect(gRenderer, &rect1);
rect2.h = i;
rect2.y = SCREEN_HEIGHT - (i);
SDL_RenderFillRect(gRenderer, &rect2);
SDL_RenderPresent(gRenderer);
}
rect1.x = 0;
rect1.y = 0;
rect1.w = SCREEN_WIDTH;
rect1.h = SCREEN_HEIGHT;
for (Uint16 i = 0; i < (SCREEN_HEIGHT / 2); i = i + 4)
{
SDL_RenderFillRect(gRenderer, &rect1);
SDL_RenderPresent(gRenderer);
}
break;
default:
break;
}
}
//Pinta el texto GetReady en pantalla
void GameDirector::renderGetReady()
{
if (mGetReady)
{
Sprite sprite;
sprite.setTexture(gMiscTexture);
sprite.setWidth(53);
sprite.setHeight(10);
sprite.setPosX((PLAY_AREA_WIDTH / 2) - (sprite.getWidth() / 2));
sprite.setPosY((PLAY_AREA_HEIGHT / 2) - (sprite.getHeight() / 2));
sprite.setSpriteClip(0, 0, sprite.getWidth(), sprite.getHeight());
for (Uint8 i = 0; i < 1; i++)
{
sprite.render();
SDL_RenderPresent(gRenderer);
SDL_Delay(1500);
}
mGetReady = false;
}
}
//Bucle para el titulo del juego
void GameDirector::runTitle()
{
//Si la música no está sonando
if (Mix_PlayingMusic() == 0)
{
//Reproduce la música
Mix_PlayMusic(gTitleMusic, -1);
}
//Comprueba los eventos que hay en la cola
while (SDL_PollEvent(&eventHandler) != 0)
{
//Evento de salida de la aplicación
if (eventHandler.type == SDL_QUIT)
{
setGameStatus(GAME_STATE_QUIT);
}
}
//Limpia la pantalla
SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(gRenderer);
//Dibuja los objetos
titleBackground.render();
menuTitle.render(whiteText);
//Actualiza la pantalla
SDL_RenderPresent(gRenderer);
//Comprueba las entradas para el menu
checkMenuInput(&menuTitle);
//Comprueba si se ha seleccionado algún item del menú
switch (menuTitle.getItemSelected())
{
case 0:
setGameStatus(GAME_STATE_PLAYING);
menuTitle.resetMenu();
renderTransition(1);
Mix_HaltMusic();
SDL_Delay(1200);
break;
case 1:
setGameStatus(GAME_STATE_QUIT);
menuTitle.resetMenu();
renderTransition(1);
Mix_HaltMusic();
break;
default:
break;
}
}
//Bucle para el juego
void GameDirector::runGame()
{
//Si la música no está sonando
if (Mix_PlayingMusic() == 0)
{
//Reproduce la música
Mix_PlayMusic(gPlayingMusic, -1);
}
//Lógica del juego
//Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - mOldTicks > mGameSpeed)
{
//Actualiza el contador de ticks
mOldTicks = SDL_GetTicks();
//Comprueba el teclado/mando
checkGameInput();
//Comprueba los eventos que hay en la cola
while (SDL_PollEvent(&eventHandler) != 0)
{
//Evento de salida de la aplicación
if (eventHandler.type == SDL_QUIT)
{
setGameStatus(GAME_STATE_QUIT);
}
//Tecla T pulsada
if (eventHandler.key.keysym.sym == SDLK_t)
{
//startAllBalloons();
popBalloon(0);
break;
}
#ifdef TEST
//W key pressed
if (eventHandler.key.keysym.sym == SDLK_w)
{
bulletTest.setPosY(bulletTest.getPosY() - 1);
bulletTest.testMove();
break;
}
//S key pressed
if (eventHandler.key.keysym.sym == SDLK_s)
{
bulletTest.setPosY(bulletTest.getPosY() + 1);
bulletTest.testMove();
break;
}
//A key pressed
if (eventHandler.key.keysym.sym == SDLK_a)
{
bulletTest.setPosX(bulletTest.getPosX() - 1);
bulletTest.testMove();
break;
}
//D key pressed
if (eventHandler.key.keysym.sym == SDLK_d)
{
bulletTest.setPosX(bulletTest.getPosX() + 1);
bulletTest.testMove();
break;
}
#endif
}
//Actualiza el jugador
player.update();
//Mueve los globos
moveBalloons();
#ifdef TEST
balloonTest.move();
#endif
//Mueve las balas
moveBullets();
//Procesa las colisiones entre globos y balas
processBulletBallonCollision();
//Comprueba el nivel de amenaza
checkMenaceLevel();
//Comprueba la colisión entre el jugador y los globos
if (checkPlayerBallonCollision())
{
//stopAllBalloons();
}
}
//Limpia la pantalla
SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(gRenderer);
//Dibuja los objetos
gameBackground.render();
renderBalloons();
#ifdef TEST
balloonTest.render();
bulletTest.render();
if (checkCollision(balloonTest.getCollider(), bulletTest.getCollider()))
{
whiteText.write(0, 0, "X");
}
#endif
//whiteText.write(0, 0, std::to_string(mMenaceLevelThreshold));
//whiteText.write(0, BLOCK, std::to_string(player.getPosX() + player.getWidth()));
renderBullets();
player.render();
renderScoreBoard(whiteText);
renderGetReady();
//Actualiza la pantalla
SDL_RenderPresent(gRenderer);
}
//Bucle para el menu de pausa del juego
void GameDirector::runPausedGame()
{
//Comprueba los eventos que hay en la cola
while (SDL_PollEvent(&eventHandler) != 0)
{
//Evento de salida de la aplicación
if (eventHandler.type == SDL_QUIT)
{
setGameStatus(GAME_STATE_QUIT);
}
}
//Dibuja los objetos
gameBackground.render();
renderBalloons();
renderBullets();
player.render();
renderScoreBoard(whiteText);
menuPause.render(whiteText);
//Limpia la pantalla
SDL_RenderPresent(gRenderer);
//Comprueba las entradas para el menu
checkMenuInput(&menuPause);
//Comprueba si se ha seleccionado algún item del menú
switch (menuPause.getItemSelected())
{
case 0:
setGameStatus(GAME_STATE_PLAYING);
menuPause.resetMenu();
break;
case 1:
setGameStatus(GAME_STATE_TITLE);
menuPause.resetMenu();
renderTransition(1);
init();
break;
default:
break;
}
}

196
source/gamedirector.h Normal file
View File

@@ -0,0 +1,196 @@
#pragma once
#include "background.h"
#include "balloon.h"
#include "bullet.h"
#include "const.h"
#include "globals.h"
#include "ltexture.h"
#include "menu.h"
#include "player.h"
#include "struct.h"
#include "text.h"
#ifndef GAMEDIRECTOR_H
#define GAMEDIRECTOR_H
//GameDirector
class GameDirector
{
public:
//Constructor
GameDirector();
//Iniciador
void init();
//Hace una pausa de milisegundos
void sleep(Uint16 time);
//Establece el valor de la variable
void setScore(Uint32 score);
//Establece el valor de la variable
void setHiScore(Uint32 score);
//Actualiza el valor de HiScore en caso necesario
void updateHiScore();
//Transforma un valor numérico en una cadena de 6 cifras
std::string updateScoreText(Uint32 num);
//Pinta el marcador en pantalla usando un objeto texto
void renderScoreBoard(Text &text);
//Mueve todos los globos activos
void moveBalloons();
//Pinta en pantalla todos los globos activos
void renderBalloons();
//Devuelve el primer indice no activo del vector de globos
Uint8 getBallonFreeIndex();
//Crea un globo nuevo en el vector de globos
Uint8 createNewBalloon(int x, int y, Uint8 kind, float velx, Uint16 stoppedtimer);
//Establece a cero todos los valores del vector de objetos globo
void resetBalloons();
//Explosiona un globo. Lo destruye y crea otros dos si es el caso
void popBalloon(Uint8 index);
//Detiene todos los globos
void stopAllBalloons();
//Pone en marcha todos los globos
void startAllBalloons();
//Obtiene el numero de globos activos
Uint8 countBalloons();
//Comprueba la colisión entre el jugador y los globos activos
bool checkPlayerBallonCollision();
//Comprueba y procesa la colisión entre las balas y los globos
void processBulletBallonCollision();
//Mueve las balas activas
void moveBullets();
//Pinta las balas activas
void renderBullets();
//Devuelve el primer indice no activo del vector de balas
Uint8 getBulletFreeIndex();
//Establece a cero todos los valores del vector de objetos bala
void resetBullets();
//Crea un objeto bala
void createBullet(int x, int y, Uint8 kind);
//Calcula y establece el valor de amenaza en funcion de los globos activos
void calculateMenaceLevel();
//Obtiene el valor de la variable
Uint8 getMenaceLevel();
//Gestiona el nivel de amenaza
void checkMenaceLevel();
//Gestiona la entrada de teclado y mando durante el juego
void checkGameInput();
//Gestiona la entrada de teclado y mando durante el menu
void checkMenuInput(Menu* menu);
//Obtiene el valor de la variable
Uint8 getGameStatus();
//Establece el valor de la variable
void setGameStatus(Uint8 status);
//Pinta una transición en pantalla
void renderTransition(Uint8 index);
//Pinta el texto GetReady en pantalla
void renderGetReady();
//Bucle para el titulo del juego
void runTitle();
//Bucle para el juego
void runGame();
//Bucle para el menu de pausa del juego
void runPausedGame();
private:
//Manejador de eventos
SDL_Event eventHandler;
//El jugador
Player player;
//Vector con los objetos globo
Balloon balloon[50];
#ifdef TEST
Balloon balloonTest;
Bullet bulletTest;
#endif
//Vector con los objetos bala
Bullet bullet[50];
//Fondo del juego
Background gameBackground;
//Fondo de la pantalla de titulo
Background titleBackground;
//Texto blanco
Text whiteText;
//Texto negro
Text blackText;
//Menu de la pantalla de título
Menu menuTitle;
//Menú de la pantalla de pausa
Menu menuPause;
//Indicador para el bucle principal
Uint8 mGameStatus;
//Puntuación actual y puntuación máxima
Uint32 mScore;
Uint32 mHiScore;
//Cadena de texto con la puntuación actual y la puntuación máxima de 6 cifras
std::string mScoreText;
std::string mHiScoreText;
//Número máximo de globos y balas que puede almacenar el vector
Uint8 mMaxBalloons;
Uint8 mMaxBullets;
//Contador de ticks para ajustar la velocidad del juego
Uint32 mOldTicks;
//Velocidad a la que se repite el bucle de juego
Uint8 mGameSpeed;
//Nivel de amenaza actual
Uint8 mMenaceLevel;
//Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral,
//se generan más globos. Si el umbral aumenta, aumenta el numero de globos
Uint8 mMenaceLevelThreshold;
//Indica si ha de aparecer el texto de GetReady en pantalla
bool mGetReady;
};
#endif

27
source/globals.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_mixer.h>
#ifndef GLOBALS_H
#define GLOBALS_H
//La ventana donde dibujamos
SDL_Window *gWindow = NULL;
//El renderizador de la ventana
SDL_Renderer *gRenderer = NULL;
//Manejador para el mando 1
SDL_Joystick* gGameController = NULL;
//Objetos con la música del juego
Mix_Music *gTitleMusic = NULL;
Mix_Music *gPlayingMusic = NULL;
//Objetos con los efectos de sonido del juego
Mix_Chunk *gPopBalloonFX = NULL;
Mix_Chunk *gBulletFX = NULL;
#endif

19
source/globals2.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include "ltexture.h"
#ifndef GLOBALS2_H
#define GLOBALS2_H
//Texturas con gráficos
LTexture gPlayerTexture;
LTexture gGameBackgroundTexture;
LTexture gTitleBackgroundTexture;
LTexture gWhiteFontTexture;
LTexture gBlackFontTexture;
LTexture gMenuTexture;
LTexture gBalloonTexture;
LTexture gBulletTexture;
LTexture gMiscTexture;
#endif

135
source/ltexture.cpp Normal file
View File

@@ -0,0 +1,135 @@
#include "ltexture.h"
LTexture::LTexture()
{
//Initialize
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
LTexture::~LTexture()
{
//Deallocate
free();
}
bool LTexture::loadFromFile(std::string path)
{
//Get rid of preexisting texture
free();
//The final texture
SDL_Texture *newTexture = NULL;
//Load image at specified path
SDL_Surface *loadedSurface = IMG_Load(path.c_str());
if (loadedSurface == NULL)
{
printf("Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError());
}
else
{
//Color key image
SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, COLOR_KEY_R, COLOR_KEY_G, COLOR_KEY_B));
//Create texture from surface pixels
newTexture = SDL_CreateTextureFromSurface(gRenderer, loadedSurface);
if (newTexture == NULL)
{
printf("Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
}
else
{
//Get image dimensions
mWidth = loadedSurface->w;
mHeight = loadedSurface->h;
}
//Get rid of old loaded surface
SDL_FreeSurface(loadedSurface);
}
//Return success
mTexture = newTexture;
return mTexture != NULL;
}
bool LTexture::createBlank( int width, int height, SDL_TextureAccess access )
{
//Create uninitialized texture
mTexture = SDL_CreateTexture( gRenderer, SDL_PIXELFORMAT_RGBA8888, access, width, height );
if( mTexture == NULL )
{
printf( "Unable to create blank texture! SDL Error: %s\n", SDL_GetError() );
}
else
{
mWidth = width;
mHeight = height;
}
return mTexture != NULL;
}
void LTexture::free()
{
//Free texture if it exists
if (mTexture != NULL)
{
SDL_DestroyTexture(mTexture);
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
}
void LTexture::setColor(Uint8 red, Uint8 green, Uint8 blue)
{
//Modulate texture rgb
SDL_SetTextureColorMod(mTexture, red, green, blue);
}
void LTexture::setBlendMode(SDL_BlendMode blending)
{
//Set blending function
SDL_SetTextureBlendMode(mTexture, blending);
}
void LTexture::setAlpha(Uint8 alpha)
{
//Modulate texture alpha
SDL_SetTextureAlphaMod(mTexture, alpha);
}
void LTexture::render(int x, int y, SDL_Rect *clip, double angle, SDL_Point *center, SDL_RendererFlip flip)
{
//Set rendering space and render to screen
SDL_Rect renderQuad = {x, y, mWidth, mHeight};
//Set clip rendering dimensions
if (clip != NULL)
{
renderQuad.w = clip->w;
renderQuad.h = clip->h;
}
//Render to screen
SDL_RenderCopyEx(gRenderer, mTexture, clip, &renderQuad, angle, center, flip);
}
void LTexture::setAsRenderTarget()
{
//Make self render target
SDL_SetRenderTarget( gRenderer, mTexture );
}
int LTexture::getWidth()
{
return mWidth;
}
int LTexture::getHeight()
{
return mHeight;
}

70
source/ltexture.h Normal file
View File

@@ -0,0 +1,70 @@
#pragma once
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "const.h"
#include "globals2.h"
#include <stdio.h>
#include <string>
#ifndef LTEXTURE_H
#define LTEXTURE_H
//Texture wrapper class
class LTexture
{
public:
//Initializes variables
LTexture();
//Deallocates memory
~LTexture();
//Loads image at specified path
bool loadFromFile( std::string path );
//Creates blank texture
bool createBlank( int width, int height, SDL_TextureAccess = SDL_TEXTUREACCESS_STREAMING );
//Deallocates texture
void free();
//Set color modulation
void setColor( Uint8 red, Uint8 green, Uint8 blue );
//Set blending
void setBlendMode( SDL_BlendMode blending );
//Set alpha modulation
void setAlpha( Uint8 alpha );
//Renders texture at given point
void render( int x, int y, SDL_Rect* clip = NULL, double angle = 0.0, SDL_Point* center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE );
//Set self as render target
void setAsRenderTarget();
//Gets image dimensions
int getWidth();
int getHeight();
//Pixel manipulators
bool lockTexture();
bool unlockTexture();
void* getPixels();
void copyPixels( void* pixels );
int getPitch();
Uint32 getPixel32( unsigned int x, unsigned int y );
private:
//The actual hardware texture
SDL_Texture* mTexture;
void* mPixels;
int mPitch;
//Image dimensions
int mWidth;
int mHeight;
};
#endif

348
source/main.cpp Normal file
View File

@@ -0,0 +1,348 @@
/*
This source code copyrighted by JailDesigner (2020)
started on Castalla 15-07-2020.
Using some sample source code from Lazy Foo' Productions
*/
/*Descripción del enfoque utilizado para crear el juego.
El programa contine una serie de clases/objetos básicos: la clase sprite
permite dibujar partes de un fichero png en pantalla. La clase spriteanimated
contiene funcionalidad adicional para crear animaciones. La clase text permite
dibujar letras de un png en pantalla a partir de una cadena de texto. La clase
menu permite crear una estructura con diferentes elementos, escribirlos en
pantalla y seleccionar uno de ellos.
A continuación tenemos las clases enfocadas a la lógica del juego, la clase player
contiene la información del jugador, la clase balloon la de los enemigos y la
clase bullet para las balas que dispara el jugador. La clase background es
muy simple y sirve para pintar el fondo de la pantalla. Por ultimo, la clase
gamedirector es la que realiza toda la lógica y se encarga de hacer interactuar
al resto de objetos entre si.
El objeto gamedirector tiene tres estados: titulo, juego y pausa. Segun su estado
el bucle que recorre es distinto. En el bucle juego, el objeto gamedirector
tiene un objeto jugador, un vector con los objetos globo y un vector con los
objetos bala. Se encarga de comprobar las entradas de teclado o gamepad para
cerrar la aplicacion, saltar al estado de pausa y para mover al jugador. Recorre
el vector de globos y de balas y si tienen algun tipo asignado las gestiona.
Comprueba las colisiones entre los globos y el jugador y entre las balas y los
globos. Tiene ademas un nivel de amenaza que calcula en funcion del numero de globos
en pantalla y que se va incrementando conforme aumenta la puntuación del jugador.
Los objetos globo tienen varios contadores para alternar de un estado a otro.
En los vectores que contienen objetos, se considera activos los objetos que tienen
un tipo asociado diferente a NO_KIND
*/
#define TEST_
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_mixer.h>
#include "background.h"
#include "balloon.h"
#include "bullet.h"
#include "const.h"
#include "gamedirector.h"
#include "globals.h"
#include "globals2.h"
#include "ltexture.h"
#include "menu.h"
#include "player.h"
#include "sprite.h"
#include "spriteanimated.h"
#include "struct.h"
#include "text.h"
#include <iostream>
#include <stdio.h>
#include <string>
//Arranca SDL y crea la ventana
bool init();
//Carga todos los recursos
bool loadMedia();
//Libera todos los recursos y cierra SDL
void close();
//Arranca SDL y crea la ventana
bool init()
{
//Indicador de inicialización
bool success = true;
//Inicializa SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO) < 0)
{
printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
//Establece el filtro de la textura a nearest
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"))
{
printf("Warning: Nearest texture filtering not enabled!");
}
//Inicializa SDL_mixer
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0)
{
printf("SDL_mixer could not initialize! SDL_mixer Error: %s\n", Mix_GetError());
success = false;
}
//Comprueba los mandos
if (SDL_NumJoysticks() < 1)
{
printf("Warning: No joysticks connected!\n");
}
else
{
//Carga el mando
gGameController = SDL_JoystickOpen(0);
if (gGameController == NULL)
{
printf("Warning: Unable to open game controller! SDL Error: %s\n", SDL_GetError());
}
printf("%i joysticks were found.\n", SDL_NumJoysticks());
std::cout << SDL_JoystickNumButtons(gGameController) << " buttons\n";
}
//Crea la ventana
gWindow = SDL_CreateWindow("Super Popping (Like Loc) in Jailers World", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, VIEW_WIDTH, VIEW_HEIGHT, SDL_WINDOW_SHOWN);
if (gWindow == NULL)
{
printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
//Crea un renderizador para la ventana con vsync
gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (gRenderer == NULL)
{
printf("Renderer could not be created! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
//Inicializa el color de renderizado
SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0xFF);
//Establece el tamaño del buffer de renderizado
SDL_RenderSetLogicalSize(gRenderer, SCREEN_WIDTH, SCREEN_HEIGHT);
//Inicializa el cargador de PNG
int imgFlags = IMG_INIT_PNG;
if (!(IMG_Init(imgFlags) & imgFlags))
{
printf("SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError());
success = false;
}
}
}
}
return success;
}
//Carga todos los recursos
bool loadMedia()
{
//Indicador de éxito en la carga
bool success = true;
//Carga los gráficos del jugador
if (!gPlayerTexture.loadFromFile("media/gfx/player.png"))
{
printf("Failed to load player texture!\n");
success = false;
}
//Carga los gráficos de los globos
if (!gBalloonTexture.loadFromFile("media/gfx/balloon.png"))
{
printf("Failed to load balloon texture!\n");
success = false;
}
//Carga los gráficos de las balas
if (!gBulletTexture.loadFromFile("media/gfx/bullet.png"))
{
printf("Failed to load bullet texture!\n");
success = false;
}
//Carga los gráficos del fondo del juego
if (!gGameBackgroundTexture.loadFromFile("media/gfx/background.png"))
{
printf("Failed to load game background texture!\n");
success = false;
}
//Carga los gráficos del fondo de la pantalla de titulo
if (!gTitleBackgroundTexture.loadFromFile("media/gfx/title.png"))
{
printf("Failed to load title texture!\n");
success = false;
}
//Carga varios gráficos para varios propósitos
if (!gMiscTexture.loadFromFile("media/gfx/misc.png"))
{
printf("Failed to load misc texture!\n");
success = false;
}
//Carga los gráficos para el menu
if (!gMenuTexture.loadFromFile("media/gfx/menu.png"))
{
printf("Failed to load menu texture!\n");
success = false;
}
//Carga los gráficos para el texto blanco
if (!gWhiteFontTexture.loadFromFile("media/gfx/white_font.png"))
{
printf("Failed to load white font texture!\n");
success = false;
}
//Carga los gráficos para el texto negro
if (!gBlackFontTexture.loadFromFile("media/gfx/black_font.png"))
{
printf("Failed to load black font texture!\n");
success = false;
}
//Carga la música del titulo
gTitleMusic = Mix_LoadMUS("media/music/title.ogg");
if (gTitleMusic == NULL)
{
printf("Failed to load title music! SDL_mixer Error: %s\n", Mix_GetError());
success = false;
}
//Carga la música del juego
gPlayingMusic = Mix_LoadMUS("media/music/playing.ogg");
if (gPlayingMusic == NULL)
{
printf("Failed to load playing music! SDL_mixer Error: %s\n", Mix_GetError());
success = false;
}
//Carga los efectos de sonido para la explosión de los globos
gPopBalloonFX = Mix_LoadWAV("media/sound/balloon.wav");
if (gPopBalloonFX == NULL)
{
printf("Failed to load balloon sound effect! SDL_mixer Error: %s\n", Mix_GetError());
success = false;
}
//Carga los efectos de sonido para los disparos del jugador
gBulletFX = Mix_LoadWAV("media/sound/bullet.wav");
if (gBulletFX == NULL)
{
printf("Failed to load bullet sound effect! SDL_mixer Error: %s\n", Mix_GetError());
success = false;
}
return success;
}
//Libera todos los recursos y cierra SDL
void close()
{
//Libera todas las imagenes
gPlayerTexture.free();
gGameBackgroundTexture.free();
gTitleBackgroundTexture.free();
gWhiteFontTexture.free();
gBlackFontTexture.free();
gMenuTexture.free();
gBalloonTexture.free();
gMiscTexture.free();
//Libera los efectos de sonido
Mix_FreeChunk(gPopBalloonFX);
Mix_FreeChunk(gBulletFX);
gPopBalloonFX = NULL;
gBulletFX = NULL;
//Libra la música
Mix_FreeMusic(gTitleMusic);
gTitleMusic = NULL;
Mix_FreeMusic(gPlayingMusic);
gPlayingMusic = NULL;
//Libera el mando
SDL_JoystickClose(gGameController);
gGameController = NULL;
//Destruye la ventana
SDL_DestroyRenderer(gRenderer);
SDL_DestroyWindow(gWindow);
gWindow = NULL;
gRenderer = NULL;
//Sal del subsistema SDL
IMG_Quit();
SDL_Quit();
}
int main(int argc, char *args[])
{
//Arranca SDL y crea la ventana
if (!init())
{
printf("Failed to initialize!\n");
return -1;
}
else
{
//Carga los recursos
if (!loadMedia())
{
printf("Failed to load media!\n");
}
else
{
//Crea el objeto gameDirector
GameDirector gameDirector;
//Inicializa el objeto gameDirector
gameDirector.init();
#ifdef TEST
gameDirector.resetBalloons();
#endif
//Mientras no se quiera salir del juego
while (!(gameDirector.getGameStatus() == GAME_STATE_QUIT))
{
switch (gameDirector.getGameStatus())
{
case GAME_STATE_TITLE:
gameDirector.runTitle();
break;
case GAME_STATE_PLAYING:
gameDirector.runGame();
break;
case GAME_STATE_PAUSED:
gameDirector.runPausedGame();
break;
}
}
}
//Libera todos los recursos y cierra SDL
close();
return 0;
}
}

195
source/menu.cpp Normal file
View File

@@ -0,0 +1,195 @@
#include "menu.h"
//Constructor
Menu::Menu()
{
init(0, 0, 0, MENU_BACKGROUND_SOLID);
}
//Inicializador
void Menu::init(int x, int y, int offset_sprite_selector, int backgroundType)
{
//Inicia variables
mSelectorIndex = 0;
mTotalItems = 0;
mItemSelected = MENU_NO_OPTION;
mPosX = x;
mPosY = y;
mRect.x = 0;
mRect.y = 0;
mRect.w = 0;
mRect.h = 0;
mRectR = 0;
mRectG = 0;
mRectB = 0;
mBackgroundType = backgroundType;
//Sprite con los graficos del selector
mSelectorSprite.setWidth(8);
mSelectorSprite.setHeight(8);
mSelectorSprite.setPosX(0);
mSelectorSprite.setPosY(0);
mSelectorSprite.setTexture(gMenuTexture);
mSelectorSprite.setSpriteClip(offset_sprite_selector, 0, mSelectorSprite.getWidth(), mSelectorSprite.getHeight());
//Elementos del menu
for (Uint8 i = 0; i < 10; i++)
{
mMenuItem[i].label = "";
mMenuItem[i].x = mPosX;
mMenuItem[i].y = mPosY + (i * (BLOCK + 2));
}
//Mueve el grafico del selector al elemento seleccionado
moveSelectorSprite(mSelectorIndex);
}
//Obtiene el valor de la variable
Uint8 Menu::getItemSelected()
{
return mItemSelected;
};
//Mueve el grafico del selector al elemento seleccionado
void Menu::moveSelectorSprite(int pos)
{
mSelectorSprite.setPosX(mMenuItem[pos].x - (BLOCK * 1));
mSelectorSprite.setPosY(mMenuItem[pos].y);
}
//Deja el menu apuntando al primer elemento
void Menu::resetMenu()
{
mItemSelected = MENU_NO_OPTION;
mSelectorIndex = 0;
moveSelectorSprite(mSelectorIndex);
};
//Deja el menu apuntando al siguiente elemento
void Menu::increaseSelectorIndex()
{
if (mSelectorIndex < (mTotalItems - 1))
{
++mSelectorIndex;
}
};
//Deja el menu apuntando al elemento anterior
void Menu::decreaseSelectorIndex()
{
if (mSelectorIndex > 0)
{
--mSelectorIndex;
}
};
//Comprueba la entrada (teclado, gamepad) y actua en consecuencia
void Menu::checkInput(Uint8 input)
{
switch (input)
{
case INPUT_UP:
decreaseSelectorIndex();
moveSelectorSprite(mSelectorIndex);
break;
case INPUT_DOWN:
increaseSelectorIndex();
moveSelectorSprite(mSelectorIndex);
break;
case INPUT_FIRE:
mItemSelected = mSelectorIndex;
break;
}
}
//Pinta el menu en pantalla
void Menu::render(Text &text)
{
//Render color filled quad
if (mBackgroundType == MENU_BACKGROUND_SOLID)
{
SDL_SetRenderDrawColor(gRenderer, mRectR, mRectG, mRectB, mRectA);
SDL_RenderFillRect(gRenderer, &mRect);
}
//Render text
int i = 0;
mSelectorSprite.render();
for (i = 0; i < mTotalItems; i++)
{
text.write(mMenuItem[i].x, mMenuItem[i].y, mMenuItem[i].label);
}
}
//Establece el rectangulo de fondo del menu
void Menu::setRectSize()
{
int i = 0;
int maxLength = 0;
//La altura se corresponde al numero de items mas un hueco arriba y otro abajo
mRect.h = (mTotalItems + 2) * BLOCK;
//La anchura es la de la cadena mas larga mas tres bloques, uno por la derecha
//y dos por la izquierda, uno de ellos para el selector
for (i = 0; i < mTotalItems; i++)
{
if ((int)mMenuItem[i].label.length() > maxLength)
{
maxLength = mMenuItem[i].label.length();
}
}
mRect.w = (maxLength + 3) * BLOCK;
mRect.x = mPosX - (BLOCK * 2);
mRect.y = mPosY - BLOCK;
}
//Establece el valor de la variable
void Menu::setTotalItems(int num)
{
mTotalItems = num;
}
//Establece el color del rectangulo de fondo
void Menu::setBackgroundColor(int r, int g, int b, int alpha)
{
mRectR = r;
mRectG = g;
mRectB = b;
mRectA = alpha;
}
//Centra el menu en pantalla
void Menu::centerMenuOnScreen()
{
//Actualiza el rectangulo de fondo para recalcular las dimensiones
setRectSize();
//Establece la nueva posición centrada en funcion del ancho
//de la pantalla y del ancho del rectangulo
mPosX = (SCREEN_WIDTH / 2) - (mRect.w / 2) + (BLOCK * 2);
//Reposiciona los elementos del menu
for (Uint8 i = 0; i < 10; i++)
{
mMenuItem[i].x = mPosX;
}
//Recalcula el rectangulo de fondo
setRectSize();
//Recoloca el selector
moveSelectorSprite(mSelectorIndex);
}
//Añade un item al menu
void Menu::addItem(std::string text)
{
if (mTotalItems < 10)
{
mMenuItem[mTotalItems].label = text;
setTotalItems(mTotalItems + 1);
setRectSize();
}
}

93
source/menu.h Normal file
View File

@@ -0,0 +1,93 @@
#pragma once
#include "sprite.h"
#include "const.h"
#include "globals.h"
#include "text.h"
#ifndef MENU_H
#define MENU_H
//Clase menu
class Menu
{
public:
//Constructor
Menu();
//Inicializador
void init(int x, int y, int offset_sprite_selector, int backgroundType);
//Obtiene el valor de la variable
Uint8 getItemSelected();
//Mueve el grafico del selector al elemento seleccionado
void moveSelectorSprite(int pos);
//Deja el menu apuntando al primer elemento
void resetMenu();
//Deja el menu apuntando al siguiente elemento
void increaseSelectorIndex();
//Deja el menu apuntando al elemento anterior
void decreaseSelectorIndex();
//Comprueba la entrada (teclado, gamepad) y actua en consecuencia
void checkInput(Uint8 input);
//Pinta el menu en pantalla
void render(Text &text);
//Establece el rectangulo de fondo del menu
void setRectSize();
//Establece el valor de la variable
void setTotalItems(int num);
//Establece el color del rectangulo de fondo
void setBackgroundColor(int r, int g, int b, int alpha);
//Centra el menu en pantalla
void centerMenuOnScreen();
//Añade un item al menu
void addItem(std::string text);
private:
//Posicion X/Y del texto del primer elemento del menu
int mPosX;
int mPosY;
//Elemento del menu que tiene el foco
Uint8 mSelectorIndex;
//Numero de items del menu
Uint8 mTotalItems;
//Item del menu que ha sido seleccionado
Uint8 mItemSelected;
//Tipo de fondo para el menu
Uint8 mBackgroundType;
//Sprite con los graficos del selector
Sprite mSelectorSprite;
//Rectangulo y colores para el fondo del menu
SDL_Rect mRect;
Uint8 mRectR; //Rojo
Uint8 mRectG; //Verde
Uint8 mRectB; //Azul
Uint8 mRectA; //Alfa o transparencia
//Estructura para cada elemento del menu
struct MenuItem
{
std::string label;
int x;
int y;
} mMenuItem[10];
};
#endif

252
source/player.cpp Normal file
View File

@@ -0,0 +1,252 @@
#include "player.h"
//Constructor
Player::Player()
{
init();
}
//Iniciador
void Player::init()
{
//Establece la altura y el ancho del jugador
mWidth = 3 * BLOCK;
mHeight = 3 * BLOCK;
//Establece la posición inicial del jugador
mPosX = PLAY_AREA_LEFT + (PLAY_AREA_WIDTH / 2) - (mWidth / 2);
mPosY = PLAY_AREA_BOTTOM - mHeight;
//Establece el tamaño del circulo de colisión
mCollider.r = 7;
//Actualiza la posición del circulo de colisión
shiftColliders();
//Establece la velocidad inicial
mVelX = 0;
mVelY = 0;
//Establece la velocidad base
mBaseSpeed = 1.5;
//Establece el numero inicial de vidas
mStartingLives = 3;
mLives = mStartingLives;
//Establece la puntuación inicial
mScore = 0;
//Inicia el contador
mCooldown = 10;
//Inicia el sprite
mSprite.init();
//Set width and height of the player sprite
mSprite.setWidth(mWidth);
mSprite.setHeight(mHeight);
//Set sprite position
mSprite.setPosX(mPosX);
mSprite.setPosY(mPosY);
//Set sprite sheet
mSprite.setTexture(gPlayerTexture);
//Set status
mStatus = PLAYER_STATE_STOPPED;
//Initialize animation variables
mSprite.setCurrentFrame(0);
mSprite.setAnimationCounter(0);
mSprite.setAnimationNumFrames(PLAYER_STATE_STOPPED, 1);
mSprite.setAnimationNumFrames(PLAYER_STATE_WALKING_LEFT, 4);
mSprite.setAnimationNumFrames(PLAYER_STATE_WALKING_RIGHT, 4);
mSprite.setAnimationSpeed(PLAYER_STATE_STOPPED, 10);
mSprite.setAnimationSpeed(PLAYER_STATE_WALKING_LEFT, 5);
mSprite.setAnimationSpeed(PLAYER_STATE_WALKING_RIGHT, 5);
//Set animation clips
mSprite.setAnimationFrames(PLAYER_STATE_WALKING_LEFT, 0, mSprite.getWidth() * 0, mSprite.getWidth() * 0, mSprite.getWidth(), mSprite.getHeight());
mSprite.setAnimationFrames(PLAYER_STATE_WALKING_LEFT, 1, mSprite.getWidth() * 1, mSprite.getWidth() * 0, mSprite.getWidth(), mSprite.getHeight());
mSprite.setAnimationFrames(PLAYER_STATE_WALKING_LEFT, 2, mSprite.getWidth() * 2, mSprite.getWidth() * 0, mSprite.getWidth(), mSprite.getHeight());
mSprite.setAnimationFrames(PLAYER_STATE_WALKING_LEFT, 3, mSprite.getWidth() * 3, mSprite.getWidth() * 0, mSprite.getWidth(), mSprite.getHeight());
mSprite.setAnimationFrames(PLAYER_STATE_WALKING_RIGHT, 0, mSprite.getWidth() * 0, mSprite.getHeight() * 1, mSprite.getWidth(), mSprite.getHeight());
mSprite.setAnimationFrames(PLAYER_STATE_WALKING_RIGHT, 1, mSprite.getWidth() * 1, mSprite.getHeight() * 1, mSprite.getWidth(), mSprite.getHeight());
mSprite.setAnimationFrames(PLAYER_STATE_WALKING_RIGHT, 2, mSprite.getWidth() * 2, mSprite.getHeight() * 1, mSprite.getWidth(), mSprite.getHeight());
mSprite.setAnimationFrames(PLAYER_STATE_WALKING_RIGHT, 3, mSprite.getWidth() * 3, mSprite.getHeight() * 1, mSprite.getWidth(), mSprite.getHeight());
mSprite.setAnimationFrames(PLAYER_STATE_STOPPED, 0, mSprite.getWidth() * 0, mSprite.getHeight() * 2, mSprite.getWidth(), mSprite.getHeight());
//Set window for sprite sheet
mSprite.setSpriteClip(mSprite.getAnimationClip(PLAYER_STATE_STOPPED, 0));
}
//Comprueba el teclado y actua en consecuencia
void Player::checkInput(Uint8 input)
{
switch (input)
{
case INPUT_LEFT:
mVelX = -mBaseSpeed;
setStatus(PLAYER_STATE_WALKING_LEFT);
break;
case INPUT_RIGHT:
mVelX = mBaseSpeed;
setStatus(PLAYER_STATE_WALKING_RIGHT);
break;
default:
mVelX = 0;
setStatus(PLAYER_STATE_STOPPED);
break;
}
}
//Mueve el jugador a la posición y animación que le corresponde
void Player::move()
{
//Move the player left or right
mPosX += mVelX;
//If the player went too far to the left or right
if ((mPosX < PLAY_AREA_LEFT) || (mPosX + mWidth > PLAY_AREA_RIGHT))
{
//Move back
mPosX -= mVelX;
}
//Move the player up or down
mPosY += mVelY;
//If the player went too far up or down
if ((mPosY < PLAY_AREA_TOP) || (mPosY + mHeight > PLAY_AREA_BOTTOM))
{
//Move back
mPosY -= mVelY;
}
//Update sprite position
mSprite.setPosX(getPosX());
mSprite.setPosY(mPosY);
}
//Pinta el jugador en pantalla
void Player::render()
{
mSprite.render();
}
//Establece el estado del jugador
void Player::setStatus(int status)
{
//Si cambiamos de estado, reiniciamos la animación
if (mStatus != status)
{
mStatus = status;
mSprite.setCurrentFrame(0);
}
}
//Establece la animación correspondiente al estado
void Player::setAnimation()
{
mSprite.animate(mStatus);
}
//Obtiene el valor de la variable
int Player::getPosX()
{
return int(mPosX);
}
//Obtiene el valor de la variable
int Player::getPosY()
{
return mPosY;
}
//Obtiene el valor de la variable
int Player::getWidth()
{
return mWidth;
}
//Obtiene el valor de la variable
int Player::getHeight()
{
return mHeight;
}
//Indica si el jugador puede disparar
bool Player::canFire()
{
//Si el contador a llegado a cero, podemos disparar. En caso contrario decrementamos el contador
if (mCooldown > 0)
{
return false;
}
else
{
return true;
}
}
//Establece el valor de la variable
void Player::setFireCooldown(int time)
{
mCooldown = time;
}
//Actualiza el valor de la variable
void Player::updateCooldown()
{
if (mCooldown > 0)
{
--mCooldown;
}
}
//Actualiza al jugador a su posicion, animación y controla los contadores
void Player::update()
{
move();
setAnimation();
shiftColliders();
updateCooldown();
}
//Obtiene la puntuación del jugador
int Player::getScore()
{
return mScore;
}
//Establece la puntuación del jugador
void Player::setScore(int score)
{
mScore = score;
}
//Añade a la puntuación del jugador
void Player::addScore(int score)
{
mScore += score;
}
//Obtiene el circulo de colisión
Circle &Player::getCollider()
{
return mCollider;
}
//Actualiza el circulo de colisión a la posición del jugador
void Player::shiftColliders()
{
//Align collider to center of player
mCollider.x = mPosX + (mWidth / 2);
mCollider.y = mPosY + (mHeight / 2);
}

114
source/player.h Normal file
View File

@@ -0,0 +1,114 @@
#pragma once
#include "struct.h"
#include "spriteanimated.h"
#include "const.h"
#include "globals.h"
#include "globals2.h"
#ifndef PLAYER_H
#define PLAYER_H
//The player
class Player
{
public:
//Constructor
Player();
//Iniciador
void init();
//Comprueba la entrada (teclado, gamepad) y actua en consecuencia
void checkInput(Uint8 input);
//Mueve el jugador a la posición y animación que le corresponde
void move();
//Pinta el jugador en pantalla
void render();
//Establece el estado del jugador
void setStatus(int status);
//Establece la animación correspondiente al estado
void setAnimation();
//Obtiene el valor de la variable
int getPosX();
//Obtiene el valor de la variable
int getPosY();
//Obtiene el valor de la variable
int getWidth();
//Obtiene el valor de la variable
int getHeight();
//Indica si el jugador puede disparar
bool canFire();
//Establece el valor de la variable
void setFireCooldown(int time);
//Actualiza el valor de la variable
void updateCooldown();
//Actualiza al jugador a su posicion, animación y controla los contadores
void update();
//Obtiene la puntuación del jugador
int getScore();
//Establece la puntuación del jugador
void setScore(int score);
//Añade a la puntuación del jugador
void addScore(int score);
//Obtiene el circulo de colisión
Circle &getCollider();
private:
//Posición X, Y del jugador
float mPosX;
int mPosY;
//Altura y anchura del jugador
Uint8 mWidth;
Uint8 mHeight;
//Velocidad X, Y del jugador
float mVelX;
int mVelY;
//Velocidad base del jugador
float mBaseSpeed;
//Contador durante el cual no puede disparar
int mCooldown;
//Vidas actuales del jugador
Uint8 mLives;
//Vidas iniciales del jugador
Uint8 mStartingLives;
//Puntos del jugador
int mScore;
//Estado del jugador
Uint8 mStatus;
//Sprite para dibujar al jugador en pantalla
SpriteAnimated mSprite;
//Circulo de colisión del jugador
Circle mCollider;
//Actualiza el circulo de colisión a la posición del jugador
void shiftColliders();
};
#endif

88
source/sprite.cpp Normal file
View File

@@ -0,0 +1,88 @@
#include "sprite.h"
void Sprite::render()
{
//Muestra el sprite por pantalla
mTexture->render(mPosX, mPosY, &mSpriteClip);
}
//Obten el valor de la variable
int Sprite::getPosX()
{
return mPosX;
}
//Obten el valor de la variable
int Sprite::getPosY()
{
return mPosY;
}
//Obten el valor de la variable
int Sprite::getWidth()
{
return mWidth;
}
//Obten el valor de la variable
int Sprite::getHeight()
{
return mHeight;
}
//Establece el valor de la variable
void Sprite::setPosX(int x)
{
mPosX = x;
}
//Establece el valor de la variable
void Sprite::setPosY(int y)
{
mPosY = y;
}
//Establece el valor de la variable
void Sprite::setWidth(int w)
{
mWidth = w;
}
//Establece el valor de la variable
void Sprite::setHeight(int h)
{
mHeight = h;
}
//Obten el valor de la variable
SDL_Rect Sprite::getSpriteClip()
{
return mSpriteClip;
}
//Establece el valor de la variable
void Sprite::setSpriteClip(SDL_Rect rect)
{
mSpriteClip = rect;
}
//Establece el valor de la variable
void Sprite::setSpriteClip(int x, int y, int w, int h)
{
mSpriteClip.x = x;
mSpriteClip.y = y;
mSpriteClip.w = w;
mSpriteClip.h = h;
}
//Obten el valor de la variable
LTexture* Sprite::getTexture()
{
return mTexture;
}
//Establece el valor de la variable
void Sprite::setTexture(LTexture &texture)
{
mTexture = &texture;
}

73
source/sprite.h Normal file
View File

@@ -0,0 +1,73 @@
#pragma once
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "ltexture.h"
#ifndef SPRITE_H
#define SPRITE_H
//Clase sprite
class Sprite
{
public:
//Muestra el sprite por pantalla
void render();
//Obten el valor de la variable
int getPosX();
//Obten el valor de la variable
int getPosY();
//Obten el valor de la variable
int getWidth();
//Obten el valor de la variable
int getHeight();
//Establece el valor de la variable
void setPosX(int x);
//Establece el valor de la variable
void setPosY(int y);
//Establece el valor de la variable
void setWidth(int w);
//Establece el valor de la variable
void setHeight(int h);
//Obten el valor de la variable
SDL_Rect getSpriteClip();
//Establece el valor de la variable
void setSpriteClip(SDL_Rect rect);
//Establece el valor de la variable
void setSpriteClip(int x, int y, int w, int h);
//Obten el valor de la variable
LTexture* getTexture();
//Establece el valor de la variable
void setTexture(LTexture &texture);
private:
//Posición X,Y donde dibujar el sprite
int mPosX;
int mPosY;
//Alto y ancho del sprite
Uint16 mWidth;
Uint16 mHeight;
//Textura donde estan todos los dibujos del sprite
LTexture *mTexture;
//Rectangulo de la textura que se dibujará en pantalla
SDL_Rect mSpriteClip;
};
#endif

87
source/spriteanimated.cpp Normal file
View File

@@ -0,0 +1,87 @@
#include "spriteanimated.h"
//Constructor
SpriteAnimated::SpriteAnimated()
{
init();
}
//Iniciador
void SpriteAnimated::init()
{
for (Uint8 i = 0; i < 20; i++)
{
mAnimation[i].numFrames = 0;
mAnimation[i].speed = 0;
for (Uint8 j = 0; i < 20; i++)
{
mAnimation[i].frames[j].x = 0;
mAnimation[i].frames[j].y = 0;
mAnimation[i].frames[j].w = 0;
mAnimation[i].frames[j].h = 0;
}
}
mCurrentFrame = 0;
mAnimationCounter = 0;
}
//Calcula el frame correspondiente a la animación
void SpriteAnimated::animate(int index)
{
//Calculamos el frame actual a partir del contador
mCurrentFrame = mAnimationCounter / mAnimation[index].speed;
//Si alcanzamos el final de la animación, reiniciamos el contador de la animación
if (mCurrentFrame >= mAnimation[index].numFrames)
{
mAnimationCounter = 0;
}
//En caso contrario
else
{
//Escogemos el frame correspondiente de la animación
setSpriteClip(mAnimation[index].frames[mCurrentFrame]);
//Incrementamos el contador de la animacion
++mAnimationCounter;
}
}
//Establece el frame actual de la animación
void SpriteAnimated::setCurrentFrame(Uint8 num)
{
mCurrentFrame = num;
}
//Establece el numero de frames de la animacion
void SpriteAnimated::setAnimationCounter(Uint16 num)
{
mAnimationCounter = num;
}
//Establece el rectangulo para un frame de una animación
void SpriteAnimated::setAnimationFrames(Uint8 index_animation, Uint8 index_frame, int x, int y, int w, int h)
{
mAnimation[index_animation].frames[index_frame].x = x;
mAnimation[index_animation].frames[index_frame].y = y;
mAnimation[index_animation].frames[index_frame].w = w;
mAnimation[index_animation].frames[index_frame].h = h;
}
//Establece la velocidad de una animación
void SpriteAnimated::setAnimationSpeed(Uint8 index, Uint8 speed)
{
mAnimation[index].speed = speed;
}
//Establece el numero de frames de una animación
void SpriteAnimated::setAnimationNumFrames(Uint8 index, Uint8 num)
{
mAnimation[index].numFrames = num;
}
//Devuelve el rectangulo de una animación y frame concreto
SDL_Rect SpriteAnimated::getAnimationClip(Uint8 index_animation, Uint8 index_frame)
{
return mAnimation[index_animation].frames[index_frame];
}

61
source/spriteanimated.h Normal file
View File

@@ -0,0 +1,61 @@
#pragma once
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "sprite.h"
#include "const.h"
#include <iostream>
#ifndef SPRITEANIMATED_H
#define SPRITEANIMATED_H
//Clase spriteAnimated
class SpriteAnimated : public Sprite
{
public:
//Constructor
SpriteAnimated();
//Iniciador
void init();
//Calcula el frame correspondiente a la animación
void animate(int index);
//Establece el frame actual de la animación
void setCurrentFrame(Uint8 num);
//Establece el numero de frames de la animacion
void setAnimationCounter(Uint16 num);
//Establece el rectangulo para un frame de una animación
void setAnimationFrames(Uint8 index_animation, Uint8 index_frame, int x, int y, int w, int h);
//Establece la velocidad de una animación
void setAnimationSpeed(Uint8 index, Uint8 speed);
//Establece el numero de frames de una animación
void setAnimationNumFrames(Uint8 index, Uint8 num);
//Devuelve el rectangulo de una animación y frame concreto
SDL_Rect getAnimationClip(Uint8 index_animation, Uint8 index_frame);
private:
struct sAnimation
{
SDL_Rect frames[20];
Uint8 numFrames;
Uint8 speed;
};
//Vector con las diferentes animaciones y los diferentes frames de cada animación
sAnimation mAnimation[20];
//Frame actual
Uint8 mCurrentFrame;
//Contador para las animaciones
Uint16 mAnimationCounter;
};
#endif

16
source/struct.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include <SDL2/SDL.h>
#ifndef STRUCT_H
#define STRUCT_H
//Estructura para definir un circulo
struct Circle
{
Uint16 x;
Uint16 y;
Uint8 r;
};
#endif

49
source/text.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "text.h"
//Constructor
Text::Text()
{
init(NULL);
}
//Inicializador
void Text::init(LTexture *texture)
{
//Inicia los valores del sprite que dibuja las letras
mSprite.setWidth(8);
mSprite.setHeight(8);
mSprite.setPosX(0);
mSprite.setPosY(0);
mSprite.setTexture(*texture);
mSprite.setSpriteClip(8, 8, mSprite.getWidth(), mSprite.getHeight());
//Cadena con los caracteres ascii que se van a inicializar
std::string text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-/().:#";
Uint8 i;
//Inicializa a cero el vector con las coordenadas
for (i = 0; i < 255; ++i)
{
mOffset[i].x = 0;
mOffset[i].y = 0;
}
//Establece las coordenadas para cada caracter ascii de la cadena
for (i = 0; i < text.length(); ++i)
{
mOffset[int(text[i])].x = (((int(text[i]) - 32) % 15) - 0) * BLOCK;
mOffset[int(text[i])].y = ((int(text[i]) - 32) / 15) * BLOCK;
}
}
//Escribe el texto en pantalla
void Text::write(int x, int y, std::string text)
{;
for (Uint8 i = 0; i < text.length(); ++i)
{
mSprite.setSpriteClip(mOffset[int(text[i])].x, mOffset[int(text[i])].y, 8, 8);
mSprite.setPosX(x + (i * BLOCK));
mSprite.setPosY(y);
mSprite.render();
}
}

34
source/text.h Normal file
View File

@@ -0,0 +1,34 @@
#pragma once
#include "sprite.h"
#include "const.h"
#ifndef TEXT_H
#define TEXT_H
//Text class
class Text
{
public:
//Constructor
Text();
//Inicializador
void init(LTexture *texture);
//Escribe el texto en pantalla
void write(int x, int y, std::string text);
private:
//Objeto con los graficos para el texto
Sprite mSprite;
//Coordenadas dentro del PNG para cada código ascii
struct Offset
{
int x;
int y;
} mOffset[255];
};
#endif