Giter Club home page Giter Club logo

dark-snake-game's Introduction

Dark Snake Game

Jogo produzido em C++ para a disciplina de Algoritmos e Estruturas de Dados I da Universidade Federal de São Carlos(UFSCar), utilizando a biblioteca SFML.

O jogo se baseia em uma releitura do famoso jogo SNAKE, utilizando texturas e ambientações mais "pesadas", sendo entitulado "Dark Snake".

Desenvolvedores

Gabriel Bertho

Carlos Eduardo Fontaneli

ESTRUTURAÇÃO

No nosso projeto, utilizamos dois Tipos Abstratos de Dados(TAD) aprendidos na disciplina de Algoritmos e Estruturas de Dados I: Pilha(Stack) e Lista(Linked List). Nesse contexto, destacamos a implementação e uso das referidas estruturas:

PILHA

#pragma once

#include <stack>
#include <memory>

#include <State.hpp>

namespace Engine
{
class StateMan
{
private:
    std::stack<std::unique_ptr<State>> m_stateStack;
    std::unique_ptr<State> m_newState;

    bool m_add;
    bool m_replace;
    bool m_remove;

public:
    StateMan();
    ~StateMan();

    void Add(std::unique_ptr<State> toAdd, bool replace = false);
    void PopCurrent();
    void ProcessStateChange();
    std::unique_ptr<State>& GetCurrent();
};

} // namespace Engine

No trecho acima, observamos a linha em que incluímos a biblioteca stack da linguagem C++, a qual conta com métodos nativos para a implementação de pilhas, sempre mantendo o aspecto LIFO(Last In First Out) da estrutura.

#include <stack>

Continuando, criamos um header(.hpp) chamado StateMan, que é a interface da classe StateManager(Controlador de Estados) responsável por controlar os diferentes estados do jogo. Esse conceito é semelhante à uma Máquina de Estados Finitos, em que, dada uma situação, um estado a representa e outro estado pode ser atingido a partir desse.

A pilha foi a estrutura de dado escolhida para essa classe pois é perfeita para controlar os estados do jogo. Para entender melhor, considere a situação de que você está jogando(estado de jogo) e clica no botão "Pause", abrindo o meno de Pause. Nesse caso, você provoca uma transição do estado de jogo para o estado de pause, ou melhor, você está empilhando o "estado de pause" que passa a ser o topo da pilha. Dada as propriedades da pilha, o único modo de você retornar para o estado de jogo, é desempilhando o estado de pause, ou seja, para voltar a jogar, você precisa despausar!

Assim, temos os atributos privados booleanos m_add, m_replace e m_remove, os quais servem para podemos verificar o estado que o jogo se encontra e controlar as transições de acordo com certas situações.

Em relação aos métodos públicos, temos o destrutor e o construtor padrão da classe, além do método Add(empilhar), PopCurrent(desempilhar)l, ProcessStateChange(faz o processamento necessário para as mudanças no jogo que a mudança de estado causará) e uma referência do tipo para obter o estado atual do jogo.

Agora, veremos a implementação dessa classe, presente no arquivo "StateMan.cpp"

#include "StateMan.hpp"

Engine::StateMan::StateMan() : m_add(false), m_replace(false), m_remove(false)
{
}

Engine::StateMan::~StateMan()
{
}

void Engine::StateMan::Add(std::unique_ptr<State> toAdd, bool replace)
{
  m_add = true;
  m_newState = std::move(toAdd);

  m_replace = replace;
}

void Engine::StateMan::PopCurrent()
{
  m_remove = true;
}

void Engine::StateMan::ProcessStateChange()
{
  if(m_remove && (!m_stateStack.empty()))
  {
      m_stateStack.pop();

      if(!m_stateStack.empty())
      {
          m_stateStack.top()->Start();
      }

      m_remove = false;
  }

  if(m_add)
  {
      if(m_replace && (!m_stateStack.empty()))
      {
          m_stateStack.pop();
          m_replace = false;
      }

      if(!m_stateStack.empty())
      {
          m_stateStack.top()->Pause();
      }

      m_stateStack.push(std::move(m_newState));
      m_stateStack.top()->Init();
      m_stateStack.top()->Start();
      m_add = false;
  }
}

std::unique_ptr<Engine::State> &Engine::StateMan::GetCurrent()
{
  return m_stateStack.top();
}

No construtor, observamos a inicialização dos atributos privados.

No método add, observamos o uso do método move para empilhar o novo estado recebido como parâmetro.

No método PopCurrent, setamos o valor da variável boolean m_remove como verdadeiro, pois estamos desempilhando o topo.

No método ProcessStateChange, fazemos uma verificação com estruturas condicionais para verificar como a pilha deve se comportar nos casos listados a seguir:

a) No primeiro caso, vemos que a pilha não está vazia e m_remove é verdadeiro, ou seja, devemos desempilhar o estado do topo. Para isso, utilizamos o método pop() presente na biblioteca Stack e posteriormente transicionamos o jogo para o novo topo utilizando o método Start() da biblioteca SFML.

b) No segundo caso, vemos que é necessário empilhar um estado no jogo, e com isso verificamos se é necessário substituir o topo da pilha ou não. Depois, verificamos se a pilha está vazia e pausamos o estado do topo para que possamos adicionar um novo estado, o qual passa a ser o novo topo, o qual é inicializado com os métodos Init() e Start() da biblioteca SFML.

No método GetCurrent, retornamos uma referência para o topo da pilha(estado atual do jogo).

Lista Encadeada Circular

Para a elaboração da cobra, utilizamos um Tipo de Dado Abstrato lista encadeada, pois cada porção do corpo da cobra é um item desta lista. Dessa forma, o fim da lista sempre será a cabeça da cobra(um iterador do método `list`), seu corpo são os itens da lista como um todo(usamos o método `list` tendo como itens `Sprites` uma classe da biblioteca gráfica responsável por desenhos que podem ser alterados ao longo do tempo) e seu começo sempre será a calda da cobra(um iterador do método `list`). Para tal criamos um classe cobra que abriga os métodos de criação da cobra e seus métodos de movimento. Para tal criamos um cabeçalho que inclue a biblioteca `` da qual usaremos o TAD lista. Além disso, adicionamos os demais cabeçalhos necessários que fazem parte da biblioteca gráfica `SFML`. Dentro do cabeçalho declaramos os métods que são responsáveis pela criação da cobra e sua movimentação.

#pragma once

#include <list>

#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/RenderStates.hpp>

class Snake : public sf::Drawable
{
private:
    std::list<sf::Sprite> m_body;
    std::list<sf::Sprite>::iterator m_head;
    std::list<sf::Sprite>::iterator m_tail;

public:
    Snake();
    ~Snake();

    void Init(const sf::Texture &texture);
    void Move(const sf::Vector2f &direction);
    bool IsOn(const sf::Sprite &other) const;
    void Grow(const sf::Vector2f &direction);
    bool IsSelfIntersecting() const;

    void draw(sf::RenderTarget &target, sf::RenderStates states) const override;
};

Agora, dentro da unidade de software dos métods da cobra temos o cabeçalho declarado apenas. Como construtor inicilizamos a cabeça como o final da lista e a cauda como o começo. A cabeça é inicilizada com um decremento, pois neste estado a lista ainda está vazia. O destrutor é vazio(padrão).

#include "Snake.hpp"

Snake::Snake() : m_body(std::list<sf::Sprite>(4))
{
    m_head = --m_body.end();
    m_tail = m_body.begin();
}

Snake::~Snake()
{
}

O método Init fica responsável por dar início a cobra, colocando seu tamanho conforme a proporção do mapa e iniciando sua textura através de métodos da biblioteca gráfica.

void Snake::Init(const sf::Texture &texture)
{
    float x = 16.f;
    for (auto &piece : m_body)
    {
        piece.setTexture(texture);
        piece.setPosition({x, 16.f});
        x += 16.f;
    }
}

O método Move cuida do movimento da cobra, atualizando o valor da cauda para frente da cabeça, isso cria a sensação de movimento do personagem. Incrementa-se a cauda e caso ela esteja na posição da cabeça reinicia-se sua posição.

void Snake::Move(const sf::Vector2f &direction)
{
    m_tail->setPosition(m_head->getPosition() + direction);
    m_head = m_tail;
    ++m_tail;

    if (m_tail == m_body.end())
    {
        m_tail = m_body.begin();
    }
}

Os métodos IsOn, Grow e IsSelfIntersecting verificação se a cobra está tocando os limites do mapa, deve crescer, pois está sobre uma comida e se a cobra encostou em si mesma, respectivamente.

void Snake::Grow(const sf::Vector2f &direction)
{
    sf::Sprite newPiece;
    newPiece.setTexture(*(m_body.begin()->getTexture()));
    newPiece.setPosition(m_head->getPosition() + direction);

    m_head = m_body.insert(++m_head, newPiece);
}

bool Snake::IsSelfIntersecting() const
{
    bool flag = false;

    for (auto piece = m_body.begin(); piece != m_body.end(); ++piece)
    {
        if (m_head != piece)
        {
            flag = IsOn(*piece);

            if (flag)
            {
                break;
            }
        }
    }

    return flag;
}

O método draw fica responsável pelo desenho do corpo da cobra pelo mapa.

void Snake::draw(sf::RenderTarget &target, sf::RenderStates states) const
{
    for (auto &piece : m_body)
    {
        target.draw(piece);
    }
}

dark-snake-game's People

Contributors

gabertho avatar

Stargazers

Augusto Matos avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.