Está en la página 1de 63

Instituto Politécnico Nacional

Unidad Profesional Interdisciplinaria en Ingeniería y


Tecnologías Avanzadas

PROYECTO FINAL
JUEGO SNAKE

Programación Orientada a Objetos

1BM4
Pérez Piña Jimena

14 de junio de 2021
Índice general

1. Introducción 2

2. Desarrollo 3
2.1. Planteamiento del problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2. Justificación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.3. Propuesta de solución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.4. Análisis y diseño . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.4.1. Diagrama de Flujo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4.2. Diagramas de clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.4.3. Código del programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.4.4. Acción del programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

3. Conclusión 62

1
Capítulo 1

Introducción

A continuación, se dará un resumen detallado de todos los procesos que se llevaron a cabo para concluir con
el juego de la serpiente se utilizaron 11 clases con sus respectivos códigos gracias a ellos logramos hacer funcionar
dicho programa no tan fluidamente como se deseaba desde el inicio.
Para lleva acabo todo este programa se utilizaron los principios de C++ todo el proceso que llevamos con los
vectores y parámetros sin ellos nunca se hubiese obtenido el funcionamiento adecuado y deseado del sistema.
La conexión de estas celdas hace que funcione todo de dicho modo se tiene que saber colocar la cantidad exacta
para que los fotogramas corran de forma correcta y que no haya fallas cuando se lleve a cabo dicho proceso
también son indispensables los vectores para llevar el orden correcto en el que cada objeto se formara a su debido
tiempo y dicho ritmo.
Y de dicho así se lleva una construcción de un objeto el cual se complementa con los distintos parámetros que
llevan en esto se cuenta el área diámetro y demás de objeto que se construirá, también hay ciertos parámetros
que no se pueden superar ya que esto violaría las normas de construcción del objeto gracias a todo esto y al
cumplir todos los requisitos se obtiene un alto rendimiento en el programa creado.

2
Capítulo 2

Desarrollo

2.1. Planteamiento del problema


Realizar un videojuego sencillo, en este caso he planteado que el videojuego a realizar será “Snake”.

3
2.2. Justificación
Este proyecto tiene la finalidad de resolver el problema que se ha planteado desde un inicio, hemos tomado en
consideración lo visto en clase para poder implementar la solución más eficaz. Antes que nada, hemos definido los
parámetros que tomaremos en cuenta para poder definir, con base en ellos, las distintas variables que el programa
contendrá.
Anteriormente hemos definido los principios que se utilizarán para poder solucionar este problema, es decir las
estructuras que incluiremos en nuestro programa.
Esta justificación servirá para que lleguemos a la solución del planteamiento de problema el cual está dicho pre-
viamente a esto y nos guiara a una forma rápida de dar solución hacia dicho enigma.
Anteriormente dimos a conocer cuáles son los principios necesarios para implementar una solución sobre dichos
problemas o enigmas en el cual usamos matrices vectores y sus respectivas 11 clases y llevar un orden sobre sus
columnas y reglones, todo esto ha sido asignado con ciertos parámetros para que puedan ser realizados de manera
correcta y ordenada.

4
2.3. Propuesta de solución
Para la propuesta de solución de este programa me he basado en información obtenida en libros, tutoriales
de internet y en las clases impartidas para la materia de POO.
Cuando escribimos un programa orientado a objetos, lo que hacemos es diseñar un conjunto de clases, desde las
cuales se crearán los objetos necesarios cuando el programa se ejecute. Cada una de estas clases incluye dos partes
fácilmente diferenciables: los atributos y los métodos. Los atributos definen el estado de cada uno de los objetos
de esa clase y los métodos, su comportamiento.
Para ello hemos utilizado lo anteriormente visto en clases y en otras prácticas:
Una clase (class) es un tipo de datos definido por el usuario. En C++, una estructura (struct) no es más
que una particularización de una clase en la que todos sus miembros tienen acceso público.
Uno de los contenedores más útiles de la biblioteca estándar es vector. Un vector, al igual que un array C,
es una secuencia de elementos de un tipo dado almacena- dos consecutivamente en memoria.
La asignación dinámica de memoria consiste en asignar la cantidad de memoria necesaria para almacenar
un objeto durante la ejecución del programa, en vez de hacerlo en el momento de la compilación del mismo.

Una clase equivale a la generalización de un tipo específico de objetos, pero cada objeto que construyamos
de esa clase tendrá sus propios datos.
Cuando escribimos un programa orientado a objetos, lo que hacemos es diseñar un conjunto de clases, desde las
cuales se crearán los objetos necesarios cuando el programa se ejecute. Cada una de estas clases incluye dos partes
fácilmente diferenciables: los atributos y los métodos. Los atributos definen el estado de cada uno de los objetos
de esa clase y los métodos, su comportamiento. . Los métodos son funciones miembro de la clase, que se ejecutan
en respuesta a un mensaje que recibe el objeto.
Un miembro declarado privado (private) es accesible solamente por los métodos de su propia clase. Esto significa
que no puede ser accedido por los métodos de cualquier otra clase, incluidas las subclases, o por cualquier otra
función externa (función no perteneciente a una clase), por ejemplo, por la función main.

Un constructor es un método especial de una clase que es llamado automática- mente siempre que se crea
un objeto de esa clase. Su función es iniciar el objeto.
La herencia es una de las características más importantes en la POO porque per- mite que una clase herede los
atributos y métodos de otra clase (los constructores no se heredan). Esta característica garantiza la reutilización
del código.
Con la herencia todas las clases están clasificadas en una jerarquía estricta. Cada clase tiene su superclase (la
clase superior en la jerarquía, también llamada clase base), y cada clase puede tener una o más subclases (las
clases inferiores en la jerarquía; también llamadas clases derivadas).
Las clases que están en la parte inferior en la jerarquía se dice que heredan de las clases que están en la parte
superior en la jerarquía.
El término heredar significa que las subclases disponen de todos los métodos y propiedades de su superclase. Este
mecanismo proporciona una forma rápida y cómoda de extender la funcionalidad de una clase. En C++ cada
clase puede tener una superclase (o clase base), lo que se denomina herencia simple, o dos o más superclases, lo
que se denomina herencia múltiple.
C++ permite el polimorfismo, que es la habilidad de los objetos de diferentes clases que están relacionados
mediante la herencia para responder en forma diferente al mismo mensaje (es decir, a la llamada de función
miembro). El mismo mensaje que se envía a muchos tipos de objetos diferentes toma "muchas formas", y de ahí
viene el término polimorfismo.

Por ejemplo, si la clase Rectangle se deriva de la clase Quadrilateral, un objeto Rectangle es una versión
más específica de un objeto Quadrilateral. Una operación (como el cálculo del perímetro o el área) que puede
realizarse en un objeto Quadrilateral también puede realizarse en un objeto Rectangle.

El polimorfismo se implementa por medio de funciones virtual.

5
Cuando se hace una petición por medio de un apuntador de clase base (o referencia) , para utilizar una función
virtual, C++ elige la función sobrepuesta correcta en la clase derivada adecuada que está asociada con ese objeto.
Hay muchas veces en que una función miembro no virtual está definida en la clase base y sobrepuesta en una
clase derivada. Si a una función de estas se le llama mediante un apuntador de clase base al objeto de la clase
derivada, se utiliza la versión de la clase base. Si la función miembro se llama mediante un apuntador de la clase
derivada, se utiliza la versión de dicha clase derivada. Este comportamiento no es polimórfico.

Mediante el uso de funciones virtual y el polimorfismo, una llamada de función miembro puede causar que
sucedan diferentes acciones, dependiendo del tipo de objeto que recibe la llamada. Esto le da una capacidad
expresiva tremenda al programador.

El polimorfismo promueve la extensibilidad: el software que está escrito para llamar al comportamiento poli-
mórfico se escribe en forma independiente de los tipos de objetos a los cuales se envían los mensajes. Por lo tanto
los nuevos tipos de objetos que pueden responder a los mensajes existentes se pueden agregar a un sistemas, sin
modificar el sistema base.

WINDOW
sf::Cursor- Cursor define la apariencia de un cursor del sistema.
sf::Keyboard- Dar acceso al estado en tiempo real del teclado.
sf:Mouse- Dar acceso al estado del mouse en tiempo real.
sf:VideoMode- VideoMode define un modo de video (ancho, alto, bpp).
sf:Window- Window sirve como destino para el renderizado OpenGL.

Cuadro 2.1: SFML Módulo “ Windows”

GRAPHICS
sf::Color- Clase de utilidad para manipular colores RGBA.
sf::Font- Clase para cargar y manipular fuentes de caracteres.
sf::Image- Clase para cargar, manipular y guardar imágenes.
sf::RectangleShape- Forma especializada que representa un rectángulo.
sf::RenderTexture- Objetivo para la renderización 2D fuera de la pantalla en una textura.
sf::RenderWindow- Ventana que puede servir como destino para el dibujo 2D.
sf::Sprite- Representación dibujable de una textura, con sus propias transformaciones, color, etc.
sf::Text- Texto gráfico que se puede dibujar en un destino de renderizado.
sf::Texture- Imagen viva en la tarjeta gráfica que se puede utilizar para dibujar.
sf::View- Cámara 2D que define qué región se muestra en la pantalla.

Cuadro 2.2: SFML Módulo “ Graphics”

6
SYSTEM
sf::String- Clase de utilidad para manipular cadenas.
sf::Time- Representa un valor de tiempo.
sf:: Vector2<t>-Clase de plantilla de utilidad para manipular vectores bidimensionales.
t= float (Vector2f), int (Vector2i), unsigned int (Vector2u)

Cuadro 2.3: SFML Módulo “ System”

2.4. Análisis y diseño


El análisis del planteamiento del problema nos llevó a diseñar la propuesta de solución con base a lo anterior-
mente planteado por medio de varios pasos, los cuáles fueron:
Explicación del programa (Documentación don Doxygen)
Diagrama de flujo

Redacción del código del programa


Análisis del comportamiento del programa
Pruebas de escritorio (para verificación de su correcto funcionamiento)

7
2.4.1. Diagrama de Flujo

Inicio

Comenzar juego

Tecla W= UP (Arriba)
Tecla A= LEFT (Izquierda)
Tecla S=DOWN (Abajo)
Tecla D=RIGHT (Derecha)

Presionando teclas
’WASD’

’W’ ’A’ ’S’ ’D’

Mover hacia arriba Mover hacia la Mover hacia la


Mover hacia abajo
derecha izquierda

¿Con que objeto


interactuo la serpiente?

Colisión cabeza-borde Colisión cabeza-cola Colisión cabeza-fruta


Sin colisión

Con su propio cuerpo Fruta


Borde
Ninguno (cola)

Crecer la cola en 1
Terminar juego unidad

Aumentar puntaje
Mostrar puntaje final

Mostrar puntaje en pantalla

Continuar jugando

Si
¿Desea jugar
nuevamente?

No

Fin

8
2.4.2. Diagramas de clases

BORDER

public Border
public ∼ Border
public void update(double deltaT)

Cuadro 2.4: Diagrama de clase “Border”

BUTTON
private Text text
private ButtonState state
private Color iddleColor
private Color hoverColor
private Color pressedColor
public string getText
public Button
public ∼ Button
public void update(double deltaT)
public void draw(RenderWindow∗w)
public void checkState(Vector2f mousePos)
public bool isPressed
void setTextPos(Vector2f newPos)
void setText(string newText)

Cuadro 2.5: Diagrama de clase “ Button”

.
.
.
.
.
.
.
.
.
.
.

9
COLLIDER
private RectangleSghape ∗ body
public Collider
public ∼ Collider
public bool checkCollision(const Collider & other)
public Vector2f getHalfSize
public Vector2f getPos

Cuadro 2.6: Diagrama de clase “Collider”

ENTITY
private RectangleShape body
private Collider∗c
public Entity
public ∼ Entity
public virtual void draw (RenderWindow ∗w)
public virtual void update(double deltaT)
public Vector2f getPos
public Collider getCollider
public RectangleShape getBody
public getSize
public void setPos(Vector2f newPos)
public void setSize(Vector2f newSize)
public void setFillCol(Color newColor)

Cuadro 2.7: Diagrama de clase ‘Entity”

FRUIT
private bool collide
public Fruit
public ∼ Fruit
piblic void update(double deltaT)
public void setCollide(bool newCollide)

Cuadro 2.8: Diagrama de clase “Fruit”

10
GAME
private RenderWindow window
private stack<State ∗>states
private double deltaT
private int score
public Game
public ∼ Game
public void start
private void update
private void draw
private void processStuff
private void run
private void initStates

Cuadro 2.9: Diagrama de clase “Game”

HEAD
private DIRECTION dir
private double counter
private vector<Tail ∗>tail
private Texture tailTexture
public Head
public ∼ Head
public void draw(RenderWindow∗ w)
public void update(double deltaT)
public void keyboardInput
public void growTail
public void updateTails
public void headToTail

Cuadro 2.10: Diagrama de clase “Head”

.
.

11
GAME STATE
private Head ∗ head
private Texture headTexture
private Fruit ∗fruit
private Texture fruitTexture
private vector<Border ∗>borders
private Texture borderTexture
private double counterHF
private double counterHT
private double counterHB
private double pauseCounter
private RectangleShape background
private Text pauseText
private Font font
private Texture scoreTexture
private Button∗ scoreButton
public GameState
public ∼ GameState
public void update(double deltaT)
public void draw(RenderWindow∗ w)
public void processStuff(double deltaT, Vector2f mousePos)
public void updateKeyBinds(double deltaT)
public initBorder

Cuadro 2.11: Diagrama de clase “GameState”

TAIL

public Tail
public ∼ Tail
public void update(double deltaT)

Cuadro 2.12: Diagrama de clase “Tail”

.
.

12
LOST STATE
private Font font
private int ∗score
private Text playerScore
private Button∗ playAgain
private RectangleShape backgroung
private Texture backgroundTexture
public LostState
public ∼ LostState
public void draw(RenderWindow∗ w)
public void update(double deltaT)
public void processStuff(double deltaT, Vector2f mousePos)
public void updateKeyBinds(double deltaT)
public void switchState
public void setScore(string newScore)

Cuadro 2.13: Diagrama de clase “LostState”

STATE
private bool quit
private int holderScore
public State
public ∼ State
public virtual void update(double deltaT)
public virtual void draw(RenderWindow∗ w)
public virtual void processStuff(double deltaT, Vector2f mousePos)
public virtual void updateKeyBinds(double deltaT)
public virtual void checkForQuit
public void setQuit(bool newQuit)
public bool getQuit
public void setHolderScore
public int getHolderScore
public virtual bool switchState

Cuadro 2.14: Diagrama de clase “State”

13
2.4.3. Código del programa
Border.h

#ifndef BORDER_H
#define BORDER_H

#include "Entity.h"
class Border :
public Entity
{
public:
Border(sf::Vector2f position, sf::Texture * texture);
~Border();

void update(double deltaT) override;


};

#endif

Border.cpp

#include "Border.h"

Border::Border(sf::Vector2f position, sf::Texture * texture) :


Entity(position, texture)
{
}

Border::~Border()
{
}

void Border::update(double deltaT)


{

14
Button.h

#ifndef BUTTON_H
#define BUTTON_H

#include <SFML/Graphics.hpp>
#include "Entity.h"
#include <string>

enum ButtonState{IDLE = 0, HOVER, PRESSED};

class Button : public Entity


{
public:
Button(sf::Vector2f position, sf::Texture * texture, sf::Vector2f size,
sf::Color idleColor, sf::Color hoverColor, sf::Color pressedColor,sf::Font * font,
std::string value, int textSize, sf::Color textColor);
~Button();

void update(double deltaT) override;


void draw(sf::RenderWindow * w) override;

void checkState(sf::Vector2f mousePos);

bool isPressed();

void setTextPos(sf::Vector2f newPos);


void setText(std::string newText);

std::string getText() const;

private:

sf::Text text;
ButtonState state;

sf::Color idleColor;

15
sf::Color hoverColor;
sf::Color pressedColor;
};

#endif

Button.cpp

#include "Button.h"

Button::Button(sf::Vector2f position, sf::Texture * texture, sf::Vector2f size,


sf::Color idleColor, sf::Color hoverColor, sf::Color pressedColor, sf::Font * font,
std::string value, int textSize, sf::Color textColor)
: Entity(position, texture)
{
Entity::setSize(size);

this->state = IDLE;

this->idleColor = idleColor;
this->hoverColor = hoverColor;
this->pressedColor = pressedColor;

this->text.setFont(*font);
this->text.setCharacterSize(textSize);
this->text.setString(value);
this->text.setFillColor(textColor);

this->text.setPosition(Entity::getSize());
}

Button::~Button()
{
}

void Button::update(double deltaT)


{

16
}

void Button::draw(sf::RenderWindow * w)
{
w->draw(Entity::getBody());
w->draw(this->text);
}

Hover, or Pressed
void Button::checkState(sf::Vector2f mousePos)
{
this->state = IDLE;

if (Entity::getBody().getGlobalBounds().contains(mousePos))
{
this->state = HOVER;
if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
this->state = PRESSED;
}
}
switch (this->state)
{
case IDLE:
Entity::setFillCol(this->idleColor);
break;
case HOVER:
Entity::setFillCol(this->hoverColor);
break;
case PRESSED:
Entity::setFillCol(this->pressedColor);
break;
default:
Entity::setFillCol(sf::Color::Red);
}

17
}

bool Button::isPressed()
{
if (this->state == PRESSED)
{
return true;
}

return false;
}

void Button::setTextPos(sf::Vector2f newPos)


{
this->text.setPosition(newPos);
}

void Button::setText(std::string newText)


{
this->text.setString(newText);
}

std::string Button::getText() const


{
return this->text.getString();
}

Collider.h

#ifndef COLLIDER_H
#define COLLIDER_H
#include <SFML/Graphics.hpp>

class Collider
{
public:

18
Collider(sf::RectangleShape* body);

~Collider();

bool checkCollision(const Collider & other);

sf::Vector2f getHalfSize() const;


sf::Vector2f getPos() const;

private:
sf::RectangleShape* body;
};

#endif

Collider.cpp

#include "Collider.h"
#include <cmath>
#include <iostream>

Collider::Collider(sf::RectangleShape* body)
:body(body)
{
}

Collider::~Collider()
{
}

bool Collider::checkCollision(const Collider& other)


{
//std::cout << "Fruit POS" << other.getPos().x << " , " << other.getPos().y << std::endl;

sf::Vector2f delta(this->getPos() - other.getPos());

sf::Vector2f halfSize(this->getHalfSize() + other.getHalfSize());

19
if (abs(delta.x) - halfSize.x < 0 && abs(delta.y) - halfSize.y < 0)
{
return true;
}

return false;
}

sf::Vector2f Collider::getHalfSize() const


{
return sf::Vector2f(body->getSize().x / 2.0f, body->getSize().y / 2.0f);
}

sf::Vector2f Collider::getPos() const


{
return sf::Vector2f(body->getPosition());
}

Entity.h

#ifndef ENTITY_H
#define ENTITY_H
#include <SFML/Graphics.hpp>
#include "Collider.h"

class Entity
{
public:
Entity(sf::Vector2f position, sf::Texture * texture);

~Entity();

virtual void draw(sf::RenderWindow *w);


virtual void update(double deltaT) = 0;

20
sf::Vector2f getPos();
Collider getCollider();
sf::RectangleShape getBody();
sf::Vector2f getSize();

void setPos(sf::Vector2f newPos);


void setSize(sf::Vector2f newSize);

void setFillCol(sf::Color newColor);

private:
sf::RectangleShape body;

Collider * c;
};

#endif

Entity.cpp

#include "Entity.h"
#include <iostream>

Entity::Entity(sf::Vector2f position, sf::Texture *texture)


{

this->c = new Collider(&this->body);


this->body.setSize(sf::Vector2f(40, 40));

this->body.setOrigin(this->body.getSize().x / 2.0f, this->body.getSize().y / 2.0f);


this->body.setPosition(position);
this->body.setTexture(texture);
}

Entity::~Entity()
{
std::cout << "Deallocating Collider" << std::endl;

21
delete c;
}

void Entity::draw(sf::RenderWindow * w)
{
w->draw(this->body);
}

sf::Vector2f Entity::getPos()
{
return this->body.getPosition();
}
Collider Entity::getCollider()
{
return *c;
}

sf::RectangleShape Entity::getBody()
{
return body;
}

sf::Vector2f Entity::getSize()
{
return body.getSize();
}

void Entity::setPos(sf::Vector2f newPos)


{
this->body.setPosition(newPos);
}

void Entity::setSize(sf::Vector2f newSize)


{
this->body.setSize(newSize);
}

22
void Entity::setFillCol(sf::Color newColor)
{
this->body.setFillColor(newColor);
}

Fruit.h

#ifndef FRUIT_H
#define FRUIT_H

#include <SFML/Graphics.hpp>
#include "Entity.h"
#include <ctime>
#include <cstdlib>

class Fruit : public Entity


{
public:
Fruit(sf::Vector2f position, sf::Texture * texture);
~Fruit();

void update(double deltaT) override;

void setCollide(bool newCollide);

private:
bool collide;
};

#endif

Fruit.cpp

#include "Fruit.h"

Fruit::Fruit(sf::Vector2f position, sf::Texture * texture)

23
:Entity(position, texture)
{
this->collide = false;
srand(time(0));
}

Fruit::~Fruit()
{
}

void Fruit::update(double deltaT)


{
if (this->collide == true)
{
int randNumX = rand() % 740 + 40;

int randNumY = rand() % 600 + 40;

Entity::setPos(sf::Vector2f(randNumX, randNumY));

this->collide = false;
}
}

void Fruit::setCollide(bool newCollide)


{
this->collide = newCollide;
}

Game.h

#ifndef GAME_H
#define GAME_H

#include <SFML/Graphics.hpp>
#include <stack>

24
#include <string>
#include "Entity.h"
#include "State.h"
#include "GameState.h"
#include "Head.h"
#include "Fruit.h"
#include "Button.h"
#include "LostState.h"

class Game
{
public:
Game();
~Game();

void start();
private:

void update();
void draw();
void processStuff();
void run();

void initStates();

private:

sf::RenderWindow window;

std::stack<State *> states;

double deltaT;
int score;

};

25
#endif

Game.cpp

#include "Game.h"
#include <iostream>
#include <cstdlib>
#include <ctime>

Game::Game() :
window(sf::VideoMode(800, 800), "SnakeGame", sf::Style::Close)
{
this->deltaT = 0;
this->initStates();
this->score = 0;

Game::~Game()
{
for (int i = 0; i < this->states.size(); i++)
{
delete states.top();
states.pop();
}
}

void Game::start()
{
this->run();
}

void Game::run()
{
sf::Clock clock;
while (this->window.isOpen())

26
{
this->deltaT = clock.restart().asSeconds();
sf::Event evnt;
while (this->window.pollEvent(evnt))
{
if (evnt.type == sf::Event::Closed)
{
this->window.close();
}
}

this->window.clear();

Game::update();
Game::processStuff();
Game::draw();

this->window.display();
}

void Game::update()
{
if (!this->states.empty())
{
this->states.top()->update(this->deltaT);
}
}

void Game::draw()
{
if (!this->states.empty())
{
this->states.top()->draw(&this->window);
}

27
}

void Game::processStuff()
{
if (!this->states.empty())
{
this->states.top()->processStuff(this->deltaT, sf::Vector2f(sf::Mouse::getPosition(this->window)));

if (this->states.top()->getQuit() == true)
{
this->score = this->states.top()->State::getHolderScore();
delete this->states.top();
this->states.pop();
}
}
if (this->states.size() <= 1)
{
this->states.top()->State::setHolderScore(this->score);

if (this->states.top()->switchState())
{
delete this->states.top();
this->states.top() = new LostState();

this->states.push(new GameState());
}
}
}

void Game::initStates()
{
this->states.push(new LostState());
this->states.push(new GameState());
}

GameState.h

28
#ifndef GAMESTATE_H
#define GAMESTATE_H

#include <SFML/Graphics.hpp>
#include <string>
#include "State.h"
#include "Entity.h"
#include "Fruit.h"
#include "Head.h"
#include "Border.h"
#include <vector>
#include "Button.h"

class GameState : public State


{
public:
GameState();
~GameState();

void update(double deltaT) override;


void draw(sf::RenderWindow * w) override;
void processStuff(double deltaT, sf::Vector2f mousePos) override;

void updateKeyBinds(double deltaT) override;

void initBorder();

private:
Head * head;
sf::Texture headTexture;

Fruit * fruit;
sf::Texture fruitTexture;

std::vector<Border *> borders;


sf::Texture borderTexture;

29
double counterHF;

double counterHT;

double counterHB;

double pauseCounter;

sf::RectangleShape background;

bool pause;

sf::Text pauseText;
sf::Font font;

sf::Texture scoreTexture;
Button* scoreButton;

};

#endif

GameState.cpp

#include "GameState.h"
#include <iostream>

GameState::GameState()
{

this->headTexture.loadFromFile("Sprites/head.png");
this->fruitTexture.loadFromFile("Sprites/fruit.png");

this->head = new Head(sf::Vector2f(400, 400), &headTexture);

srand(time(0));

30
int randNum = rand() % 740 + 40;

this->fruit = new Fruit(sf::Vector2f(randNum, randNum), &fruitTexture);

this->initBorder();

this->counterHF = 0;
this->counterHT = 0;
this->counterHB = 0;
this->pauseCounter = 0;

this->background.setPosition(sf::Vector2f(0, 0));
this->background.setFillColor(sf::Color(100, 100, 100));

this->font.loadFromFile("Fonts/DejaVuSans.ttf");

this->pause = false;
this->pauseText.setString("Pause");
this->pauseText.setPosition(sf::Vector2f(300, 300));
this->pauseText.setFont(font);
this->pauseText.setCharacterSize(60);

this->scoreTexture.loadFromFile("Sprites/blank.png");

this->scoreButton = new Button(sf::Vector2f(80,60), &this->scoreTexture, sf::Vector2f(100, 40), sf::C


sf::Color::Cyan, sf::Color::Cyan, &this->font, std::to_string(State::getHolderScore()) , 40, sf::Colo
this->scoreButton->setTextPos(sf::Vector2f(700, 40));

GameState::~GameState()
{
std::cout << "Deleting Head" << std::endl;
std::cout << "Deleting Fruit" << std::endl;
delete this->head;

31
delete this->fruit;

for (int i = 0; i < this->borders.size(); i++)


{
delete borders[i];
}
delete this->scoreButton;
}

void GameState::update(double deltaT)


{
this->updateKeyBinds(deltaT);

if (this->pause == false)
{
this->head->update(deltaT);
this->fruit->update(deltaT);
}
}

void GameState::draw(sf::RenderWindow * w)
{
this->background.setSize(sf::Vector2f(w->getSize().x, w->getSize().y));
w->draw(this->background);

this->head->draw(w);
this->fruit->draw(w);
for (int i = 0; i < this->borders.size(); i++)
{
this->borders[i]->draw(w);
}
if (pause == true)
{
w->draw(this->pauseText);
}

32
this->scoreButton->draw(w);

void GameState::processStuff(double deltaT, sf::Vector2f mousePos)


{
if (this->head->getCollider().checkCollision(this->fruit->getCollider()) == true)
{
if (counterHF >= 0.3)
{
std::cout << "Head And Fruit Are Colliding " << std::endl;
this->head->growTail();
this->fruit->setCollide(true);
State::setHolderScore(State::getHolderScore() + 1);
this->scoreButton->setText(std::to_string(State::getHolderScore()));
counterHF = 0;
}
}
if (this->head->headToTail() == true)
{
if (counterHT >= 0.3)
{
std::cout << "Head Touched Tail " << std::endl;
counterHT = 0;
State::setQuit(true);
}
}
for (int i = 0; i < this->borders.size(); i++)
{
if (this->head->getCollider().checkCollision(this->borders[i]->getCollider()) == true)
{
if (counterHB >= 0.3)
{
std::cout << "Head is Touching the Border " << std::endl;
counterHB = 0;

33
State::setQuit(true);
}
}
}
counterHF += deltaT;
counterHT += deltaT;
counterHB += deltaT;

void GameState::updateKeyBinds(double deltaT)


{
State::checkForQuit();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::P))
{
if (pauseCounter >= 0.3)
{
if (this->pause == false)
{
this->pause = true;
}
else
{
this->pause = false;
}
std::cout << "Pausing Screen " << this->pause<< std::endl;
pauseCounter = 0;
}
}
pauseCounter += deltaT;
}

void GameState::initBorder()
{
this->borderTexture.loadFromFile("Sprites/wall.png");

34
int amountNeeded = 83;
sf::Vector2f pos(0, 0);

int flag = 0;

for (int i = 0; i < amountNeeded; i++)


{
this->borders.push_back(new Border(pos, &this->borderTexture));

if (i < 20)
{
pos.x += 40;
pos.y = 0;
}
else if (i < 60 && i >= 20)
{
if (i % 2 == 0)
{
pos.x = 0;
}
else
{
pos.x = 800;
}
if (flag == 2)
{
pos.y += 40;
flag = 0;
}
flag++;
}
else if (i >= 60)
{
if (i == 60)
{
pos.x = 0;

35
pos.y = 800;
}
else
{
pos.y = 800;
pos.x += 40;
}
}
}
}

Head.h

#ifndef HEAD_H
#define HEAD_H

#include <SFML/Graphics.hpp>
#include "Entity.h"
#include <vector>
#include "Tail.h"

enum DIRECTION{ UP = 0, RIGHT, DOWN, LEFT };

class Head : public Entity


{
public:
Head(sf::Vector2f position, sf::Texture * texture);
~Head();
void draw(sf::RenderWindow* w) override;
void update(double deltaT) override;
void keyboardInput();
void growTail();
void updateTails();
bool headToTail();

private:

36
DIRECTION dir;

double counter;
std::vector <Tail *> tail;
sf::Texture tailTexture;
};

#endif

Head.cpp

#include "Head.h"
#include <iostream>

Head::Head(sf::Vector2f position, sf::Texture * texture)


:Entity(position, texture)
{
this->dir = RIGHT;

this->counter = 0;

this->tailTexture.loadFromFile("Sprites/tail.png");

this->tail.push_back(new Tail(Entity::getPos(), texture));


}

Head::~Head()
{
for (int i = 0; i < tail.size(); i++)
{
delete tail[i];
}
}

37
void Head::draw(sf::RenderWindow * w)
{
w->draw(Entity::getBody());
for (int i = 0; i < tail.size(); i++)
{
w->draw(tail[i]->Entity::getBody());
}
}

void Head::update(double deltaT)


{
Head::keyboardInput();
if (counter >= 0.06)
{
if (this->dir == UP)
{
Entity::setPos(Entity::getPos() + sf::Vector2f(0, -Entity::getSize().y));
}
if (this->dir == RIGHT)
{
Entity::setPos(Entity::getPos() + sf::Vector2f(Entity::getSize().x, 0));
}
if (this->dir == DOWN)
{
Entity::setPos(Entity::getPos() + sf::Vector2f(0, Entity::getSize().y));
}
if (this->dir == LEFT)
{
Entity::setPos(Entity::getPos() + sf::Vector2f(-Entity::getSize().x, 0));
}
this->updateTails();
counter = 0;
}
counter += deltaT;
}

38
void Head::keyboardInput()
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
{
dir = UP;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
dir = RIGHT;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
{
dir = DOWN;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
dir = LEFT;
}
}

void Head::updateTails()
{
for (int i = tail.size() - 1; i > 0; i--)
{
tail[i]->setPos(tail[i - 1]->getPos());
}
if (tail.size() >= 1)
{
tail[0]->setPos(Entity::getPos());
}
}

void Head::growTail()
{
if (this->tail.size() == 0)
{

39
this->tail.push_back(new Tail(Entity::getPos(), &this->tailTexture));
}
else
{
this->tail.push_back(new Tail(this->tail[this->tail.size()-1]->getPos(), &this->tailTexture));
}

bool Head::headToTail()
{
if (this->tail.size() > 2)
{
for (int i = 1; i < this->tail.size(); i++)
{
if (Entity::getCollider().checkCollision(this->tail[i]->getCollider()) == true)
{
return true;
}
}
}
return false;
}

LostState.h

#ifndef LOSTSTATE_H
#define LOSTSTATE_H
#include <SFML/Graphics.hpp>
#include "State.h"
#include "Button.h"
#include <string>

class LostState :
public State
{
public:

40
LostState();
~LostState();
void update(double deltaT) override;
void draw(sf::RenderWindow * w) override;
void processStuff(double deltaT, sf::Vector2f mousePos) override;
void updateKeyBinds(double deltaT) override;
bool switchState() override;
void setScore(std::string newScore);

private:
sf::Font font;
int *score;

sf::Text playerScore;
Button* playAgain;

sf::RectangleShape background;
sf::Texture backgroundTexture;

};

#endif

LostState.cpp

#include "LostState.h"
#include <iostream>

LostState::LostState()
{
this->font.loadFromFile("Fonts/DejaVuSans.ttf");
this->backgroundTexture.loadFromFile("Sprites/lost.png");

this->score = nullptr;
this->playerScore.setPosition(sf::Vector2f(350, 200));
this->playerScore.setFillColor(sf::Color::Black);

41
this->playerScore.setFont(this->font);
this->playerScore.setCharacterSize(80);

this->playAgain = new Button(sf::Vector2f(275,400),nullptr,sf::Vector2f(300,100),


sf::Color(200,200,200), sf::Color(140,140,140), sf::Color(100,100,100), &this->font, "Play Again",
45,sf::Color::Black);
this->playAgain->setTextPos(sf::Vector2f(300, 400));

this->background.setSize(sf::Vector2f(800, 800));
//this->background.setFillColor(sf::Color::Cyan);
this->background.setTexture(&this->backgroundTexture);
}

LostState::~LostState()
{
delete this->playAgain;
}

void LostState::update(double deltaT)


{
this->updateKeyBinds(deltaT);
}

void LostState::draw(sf::RenderWindow * w)
{
w->draw(this->background);
w->draw(this->playerScore);
this->playAgain->draw(w);
}

void LostState::processStuff(double deltaT, sf::Vector2f mousePos)


{
this->playerScore.setString(std::to_string(State::getHolderScore()));
this->playAgain->checkState(mousePos);
}

42
void LostState::updateKeyBinds(double deltaT)
{
State::checkForQuit();
}

bool LostState::switchState()
{
return (this->playAgain->isPressed());
}

void LostState::setScore(std::string newScore)


{
this->playerScore.setString(newScore);
}

State.h

#ifndef STATE_H
#define STATE_H

#include <SFML/Graphics.hpp>

class State
{
public:
State();
~State();
virtual void update(double deltaT) = 0;
virtual void draw(sf::RenderWindow * w) = 0;
virtual void processStuff(double deltaT , sf::Vector2f mousePos) = 0;
virtual void updateKeyBinds(double deltaT) = 0;
virtual void checkForQuit();
void setQuit(bool newQuit);
bool getQuit() const;
void setHolderScore(int newScore);
int getHolderScore() const;
virtual bool switchState();

43
private:
bool quit;
int holderScore;
};

#endif

State.cpp

#include "State.h"
#include <iostream>

State::State()
{
this->quit = false;

this->holderScore = 0;
}

State::~State()
{
}

void State::checkForQuit()
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
{
setQuit(true);
}
}

void State::setQuit(bool newQuit)


{
this->quit = newQuit;
}

bool State::getQuit() const

44
{
return this->quit;
}

void State::setHolderScore(int newScore)


{
this->holderScore = newScore;
}

int State::getHolderScore() const


{
return this->holderScore;
}

bool State::switchState()
{
return false;
}

Tail.h

#ifndef TAIL_H
#define TAIL_H

#include <SFML/Graphics.hpp>
#include "Entity.h"

class Tail : public Entity


{
public:
Tail(sf::Vector2f position, sf::Texture * texture);

void update(double deltaT) override;


~Tail();
};

45
#endif

Tail.cpp

#include "Tail.h"

Tail::Tail(sf::Vector2f position, sf::Texture * texture) :


Entity(position,texture)
{
}

Tail::~Tail()
{
}

void Tail::update(double deltaT)


{

main.cpp

#include <iostream>
#include "Game.h"

int main()
{
Game snakeGame;

snakeGame.start();

return 0;
}

46
2.4.4. Acción del programa

Figura 2.1: Ventana creada por SFML, en la imagen observamos cuando el juego comienza y la cola es igual a
la cabeza

47
Figura 2.2: Ventana creada por SFML, podemos ver que ahora se ha comido una fruta y la cabeza y la cola son
de distintos colores

48
Figura 2.3: Puntuación vista en la ventana después de perder

49
Figura 2.4: Ejectuable del programa en la consola, vemos como ha comido 2 frutas y ha perdido el juego

50
Figura 2.5: Ventana creada por SFML puesta en pausa con ’P’

51
Figura 2.6: Ejectuable del programa (consola) cuando el juego es puesto en pausa

52
Figura 2.7: Ventana creada por SFML puesta en play después de pausar

53
Figura 2.8: Ejecutable del programa (consola) cuando se presiona ’P’ estando en pausa para poder continuar

54
Figura 2.9: Cambio de color cuando el mouse esta sobre el botón de jugar de nuevo

55
Figura 2.10: Ventana creada por SFML cuando se ha perdido

56
Figura 2.11: Ventana creada por SFML del programa cuando se han logrado 8 puntos y se ha perdido

57
Figura 2.12: Muestra el ejecutable cuando se ha perdido porque la cabeza de la vibora choco con el borde

58
Figura 2.13: Ventana creada por SFML del programa cuando se han logrado 6 puntos y se ha perdido

59
Figura 2.14: Muestra el ejecutable cuando se ha perdido porque la cabeza de la vibora choco con su cuerpo (co-
la)

60
Figura 2.15: Podemos ver como las colas se van arrastrando hacia atrás, además de como se va actualizando el
score

61
Capítulo 3

Conclusión

Podemos concluir que muchas de las aplicaciones prácticas de lo que nosotros aprendemos por medio de la
teoría solo se pueden implementar por medio de la misma práctica, es necesario además de comprender lo que
debemos de crear, trabajar en llegar a una solución lógica por medio de muchos pasos más sencillos.En este caso,
aprendí a utilizar correctamente la herencia como parte de un proyecto, además de aprender a investigar por mi
cuenta situaciones que necesito aprender para lograr un objetivo.
Aprendí a usar la composición y la herencia, además de como usar de manera más adecuada operadores, apun-
tadores, punteros, etc. También conocí más acerca de SFML, de los módulos Graphics, Window y System, y
algunos de sus usos y aplicaciones práticas más comunes.
Aprendimos a utilizar las clases y a identificar de mejor manera los parámetros y los métodos que las conforman,
así como a trabajar con objetos y a definir condiciones para que el programa funcione de manera autónoma. Uti-
lizamos funciones e instrucciones nuevas, por ende fue necesario investigar y realizar pruebas del funcionamiento
de estas.

62

También podría gustarte