This commit is contained in:
Rémi Cérès 2016-04-06 15:35:37 +02:00
commit 9130adec55
22 changed files with 486 additions and 49 deletions

2
CMakeLists.txt vendored
View File

@ -3,7 +3,7 @@ project(ptf)
# Inclusion des fichiers d'en-tête et de source
include_directories(include)
file(GLOB SOURCES "src/*.cpp" "src/*.c")
file(GLOB SOURCES "src/*.cpp" "src/*.c" "src/util/*.cpp")
# Affichage de tous les avertisements
if(MSVC)

View File

@ -17,10 +17,15 @@ public:
Block();
virtual ~Block();
/**
* Clone cet objet en un objet avec les mêmes propriétés
*/
virtual ObjectPtr clone() const;
/**
* Chargement de l'objet depuis le fichier donné
*/
static std::shared_ptr<Object> load(std::ifstream& file);
static ObjectPtr load(std::ifstream& file);
/**
* Dessin du bloc dans la fenêtre donnée

View File

@ -1,8 +1,9 @@
#ifndef __PTF_GAME_HPP__
#define __PTF_GAME_HPP__
#ifndef __PTF_EDITOR_HPP__
#define __PTF_EDITOR_HPP__
#include <unordered_map>
#include "level.hpp"
#include "util/widget_timer.hpp"
/**
* La classe Editor permet l'édition de
@ -11,6 +12,7 @@
class Editor : public Level {
private:
std::unordered_map<ObjectPtr, bool> selection;
WidgetTimer widget_timer;
/**
* Ajoute un objet du type actuel à la position donnée
@ -30,6 +32,11 @@ private:
*/
bool updateSelection(sf::Vector2f position);
/**
* Lance le test du niveau
*/
void testLevel();
protected:
/**
* Dessine tous les objets, le fond et

View File

@ -2,6 +2,7 @@
#define __PTF_GAME_HPP__
#include "level.hpp"
#include "editor.hpp"
/**
* La classe Game gère l'affichage et les objets
@ -9,7 +10,10 @@
*/
class Game : public Level {
private:
WidgetTimer widget_timer;
sf::Time next_frame_time;
bool test_mode;
std::shared_ptr<View> return_view;
/**
* Met à jour les objets du jeu pour
@ -17,6 +21,13 @@ private:
*/
void update();
protected:
/**
* Dessine tous les objets, le fond et
* l'interface de jeu
*/
virtual void draw();
public:
Game(Manager& manager);
virtual ~Game();
@ -31,6 +42,12 @@ public:
* cette vue
*/
void frame();
/**
* Mise en mode test : l'appui sur espace renvoie
* vers l'éditeur donné
*/
void setTestMode(std::shared_ptr<View> set_return_view);
};
#endif

View File

@ -15,7 +15,7 @@
*/
class Level : public View {
private:
std::string name;
sf::String name;
int total_time;
sf::Sprite background;
@ -45,23 +45,33 @@ public:
/**
* Récupère le nom du niveau
*/
std::string getName();
sf::String getName() const;
/**
* Modifie le nom du niveau
*/
void setName(std::string set_name);
void setName(sf::String set_name);
/**
* Récupère le temps total alloué pour terminer le niveau
*/
int getTotalTime();
int getTotalTime() const;
/**
* Modifie le temps total du niveau
*/
void setTotalTime(int set_total_time);
/**
* Récupère le fond du niveau
*/
sf::Sprite getBackground() const;
/**
* Modifie le fond du niveau
*/
void setBackground(sf::Sprite set_background);
/**
* Récupère la liste des objets
*/

View File

@ -14,7 +14,7 @@ private:
sf::RenderWindow window;
sf::Clock clock;
sf::View window_view;
std::string title;
sf::String title;
ResourceManager resource_manager;
std::vector<sf::Event> events;
@ -29,6 +29,11 @@ public:
*/
void start();
/**
* Renvoie la vue actuelle du jeu
*/
std::shared_ptr<View> getView();
/**
* Charge la vue donnée dans le jeu
*/
@ -68,12 +73,12 @@ public:
/**
* Renvoie le titre actuel de la fenêtre
*/
std::string getTitle();
sf::String getTitle();
/**
* Modifie le titre actuel de la fenêtre
*/
void setTitle(std::string set_title);
void setTitle(sf::String set_title);
/**
* Renvoie un booléen attestant de l'appui sur la

View File

@ -45,11 +45,16 @@ public:
Object();
virtual ~Object();
/**
* Clone cet objet en un objet avec les mêmes propriétés
*/
virtual ObjectPtr clone() const = 0;
/**
* Charge les propriétés communes à tous les objets
* depuis le fichier donné dans l'objet donné
*/
static void load(std::ifstream& file, std::shared_ptr<Object> object);
static void load(std::ifstream& file, ObjectPtr object);
/**
* Dessine l'objet dans la fenêtre donnée

View File

@ -24,16 +24,21 @@ public:
Player();
virtual ~Player();
/**
* Clone cet objet en un objet avec les mêmes propriétés
*/
virtual ObjectPtr clone() const;
/**
* Chargement de l'objet depuis le fichier donné
*/
static std::shared_ptr<Object> load(std::ifstream& file);
static ObjectPtr load(std::ifstream& file);
/**
* Dessine la balle dans la fenêtre donnée
*/
virtual void draw(Manager& manager);
/**
* Met à jour la position de l'objet selon sa
* vitesse actuelle

View File

@ -0,0 +1,41 @@
#ifndef __PTF_UTIL_WIDGET_BUTTON_HPP__
#define __PTF_UTIL_WIDGET_BUTTON_HPP__
#include <SFML/Graphics.hpp>
#include <functional>
#include "manager.hpp"
/**
* Affiche un bouton pouvant être cliqué
*/
class WidgetButton {
private:
Manager& manager;
std::function<void(void)> click_cb;
unsigned int shape;
sf::RectangleShape button;
sf::VertexArray button_shape;
public:
static const unsigned int ARROW_UP;
static const unsigned int ARROW_DOWN;
WidgetButton(
Manager& manager, std::function<void(void)> click_cb,
sf::Vector2f size, unsigned int shape
);
/**
* Process l'événement et renvoie true si
* on s'en est servi
*/
bool processEvent(const sf::Event& event);
/**
* Dessine le widget à la position (haut-gauche) donnée
*/
void draw(sf::Vector2f position);
};
#endif

View File

@ -0,0 +1,58 @@
#ifndef __PTF_UTIL_WIDGET_TIMER_HPP__
#define __PTF_UTIL_WIDGET_TIMER_HPP__
#include <SFML/Graphics.hpp>
#include <functional>
#include "util/widget_button.hpp"
#include "manager.hpp"
/**
* Affiche le compteur de temps pouvant (ou non)
* être modifié
*/
class WidgetTimer {
private:
Manager& manager;
bool can_change;
std::function<void(int)> time_left_cb;
int time_left;
sf::RectangleShape timer_zone;
sf::Text timer_seconds_text;
sf::Text timer_sep_text;
sf::Text timer_minutes_text;
WidgetButton timer_up;
WidgetButton timer_down;
public:
WidgetTimer(Manager& manager, bool can_change, std::function<void(int)> time_left_cb = std::function<void(int)>());
/**
* Process l'événement et renvoie true si
* on s'en est servi
*/
bool processEvent(const sf::Event& event);
/**
* Dessine le widget à la position (haut-gauche) donnée
*/
void draw(sf::Vector2f position);
/**
* Augmente le temps de 5 secondes
*/
void addTime();
/**
* Diminue le temps de 5 secondes
*/
void subtractTime();
/**
* Modifie le temps restant
*/
void setTimeLeft(int set_time_left);
};
#endif

Binary file not shown.

BIN
res/monoid.ttf Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 B

View File

@ -16,8 +16,12 @@ Block::Block() : Object() {
Block::~Block() {}
std::shared_ptr<Object> Block::load(std::ifstream& file) {
std::shared_ptr<Object> object = std::shared_ptr<Object>(new Block);
ObjectPtr Block::clone() const {
return ObjectPtr(new Block(*this));
}
ObjectPtr Block::load(std::ifstream& file) {
ObjectPtr object = ObjectPtr(new Block);
// lecture des propriétés communes des objets
Object::load(file, object);

View File

@ -1,28 +1,32 @@
#include <cmath>
#include <iostream>
#include "editor.hpp"
#include "game.hpp"
#include "block.hpp"
#include "constants.hpp"
Editor::Editor(Manager& manager) : Level(manager) {
// activation de la synchronisation verticale
// car, dans l'éditeur, nous n'avons besoin que de dessiner
// (pas de mise à jour physique)
manager.getWindow().setVerticalSyncEnabled(true);
}
Editor::Editor(Manager& manager) : Level(manager),
widget_timer(manager, true, std::bind(&Editor::setTotalTime, this, std::placeholders::_1)) {}
Editor::~Editor() {}
void Editor::load(std::ifstream& file) {
Level::load(file);
manager.setTitle("Edition de " + getName());
manager.setTitle(sf::String(L"Édition de ") + getName());
}
void Editor::frame() {
const std::vector<sf::Event>& events = manager.getEvents();
// traitement des événements
for (unsigned int i = 0; i < events.size(); i++) {
const sf::Event& event = events[i];
// traitement des événements du widget timer
if (widget_timer.processEvent(event)) {
continue;
}
// lorsque l'on clique dans l'éditeur
if (event.type == sf::Event::MouseButtonPressed) {
sf::Vector2f position(event.mouseButton.x, event.mouseButton.y);
@ -33,25 +37,36 @@ void Editor::frame() {
if (!updateSelection(position)) {
addObject(position);
}
}
if (event.mouseButton.button == sf::Mouse::Right) {
} else if (event.mouseButton.button == sf::Mouse::Right) {
// clic droit : on supprime l'objet pointé
removeObject(position);
}
}
// gestion des touches
if (event.type == sf::Event::KeyPressed) {
// appui sur espace : test du niveau en cours d'édition
if (event.key.code == sf::Keyboard::Space) {
testLevel();
return; // important : ne pas dessiner la frame
// on risque d'avoir perdu le pointeur en changeant de vue
}
}
}
// dessin de la frame
draw();
sf::sleep(sf::seconds(1.f / 60));
}
void Editor::draw() {
Level::draw();
sf::RenderWindow& window = manager.getWindow();
sf::View window_view = manager.getWindowView();
sf::Color selection_color(255, 50, 41);
// on dessine des carrés de sélection autour des objets sélectionnés
// dessin de la sélection autour des objets sélectionnés
for (auto iter = selection.begin(); iter != selection.end(); iter++) {
sf::VertexArray selection(sf::LinesStrip, 5);
std::unique_ptr<sf::FloatRect> aabb = iter->first->getAABB();
@ -70,6 +85,10 @@ void Editor::draw() {
window.draw(selection);
}
// dessin du widget timer
widget_timer.setTimeLeft(getTotalTime());
widget_timer.draw(sf::Vector2f(window_view.getSize().x / 2 - 50, 0));
// menu
sf::RectangleShape menu(sf::Vector2f(window_view.getSize().x, 64));
menu.setPosition(sf::Vector2f(0, window_view.getSize().y - 64));
@ -87,7 +106,7 @@ void Editor::addObject(sf::Vector2f position) {
position *= Constants::GRID;
// TODO: ajouter un objet du type choisi, pas uniquement de bloc
std::shared_ptr<Object> object = std::shared_ptr<Object>(new Block);
ObjectPtr object = ObjectPtr(new Block);
object->setPosition(position);
// avant d'ajouter l'objet, on vérifie qu'il ne soit
@ -149,3 +168,30 @@ bool Editor::updateSelection(sf::Vector2f position) {
return has_changed;
}
void Editor::testLevel() {
std::shared_ptr<Game> game = std::shared_ptr<Game>(new Game(manager));
// copie des propriétés
game->setName(getName());
game->setTotalTime(getTotalTime());
game->setBackground(getBackground());
// copie des objets du niveau vers le jeu
std::vector<ObjectPtr>& objects = getObjects();
for (unsigned int i = 0; i < objects.size(); i++) {
game->getObjects().push_back(objects[i]->clone());
}
// copie de la zone de jeu
std::vector<std::pair<float, float>>& zone = getZone();
for (unsigned int i = 0; i < zone.size(); i++) {
game->getZone().push_back(zone[i]);
}
// mise en mode test
game->setTestMode(manager.getView());
manager.setView(game);
}

View File

@ -1,13 +1,10 @@
#include "game.hpp"
#include "constants.hpp"
Game::Game(Manager& manager) : Level(manager), next_frame_time(manager.getCurrentTime()) {
// on s'assure que la synchronisation verticale soit
// bien désactivée : on s'occupe de la limitation du
// framerate manuellement dans la fonction frame pour mettre
// une mise à jour fluide de la physique du jeu
manager.getWindow().setVerticalSyncEnabled(false);
}
Game::Game(Manager& manager) : Level(manager),
widget_timer(manager, false),
next_frame_time(manager.getCurrentTime()),
test_mode(false), return_view(nullptr) {}
Game::~Game() {}
@ -17,8 +14,22 @@ void Game::load(std::ifstream& file) {
}
void Game::frame() {
const std::vector<sf::Event>& events = manager.getEvents();
sf::Time current_time = manager.getCurrentTime();
// traitement des événements
for (unsigned int i = 0; i < events.size(); i++) {
const sf::Event& event = events[i];
// appui sur espace en mode test : retour à l'éditeur
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Space && test_mode) {
test_mode = false;
manager.setView(return_view);
return; // important : ne pas dessiner la frame
// on risque d'avoir perdu le pointeur en changeant de vue
}
}
if (current_time >= next_frame_time) {
// si nous sommes en retard ou dans les temps
// on replanifie la prochaine frame
@ -38,6 +49,16 @@ void Game::frame() {
}
}
void Game::draw() {
Level::draw();
sf::View window_view = manager.getWindowView();
// dessin du widget
widget_timer.setTimeLeft(getTotalTime());
widget_timer.draw(sf::Vector2f(window_view.getSize().x / 2 - 50, 0));
}
void Game::update() {
std::vector<CollisionData> colliding;
@ -84,3 +105,8 @@ void Game::update() {
getObjects()[i]->updateVelocity(manager, getObjects(), Constants::PHYSICS_TIME.asSeconds() / 2);
}
}
void Game::setTestMode(std::shared_ptr<View> set_return_view) {
return_view = set_return_view;
test_mode = true;
}

View File

@ -12,7 +12,7 @@
* à des instances qui seront utilisées pour la
* construction d'autres objets de ces types
*/
std::map<unsigned int, std::function<std::shared_ptr<Object>(std::ifstream&)>> object_type_map = {
std::map<unsigned int, std::function<ObjectPtr(std::ifstream&)>> object_type_map = {
{Player::TYPE_ID, Player::load},
{Block::TYPE_ID, Block::load}
};
@ -49,7 +49,9 @@ void Level::load(std::ifstream& file) {
}
// lecture du nom du niveau
std::getline(file, name, '\0');
std::string std_name;
std::getline(file, std_name, '\0');
name = sf::String(std_name);
// lecture du temps total du niveau
file.read(reinterpret_cast<char*>(&total_time), sizeof(total_time));
@ -129,22 +131,34 @@ void Level::draw() {
}
}
std::string Level::getName() {
sf::String Level::getName() const {
return name;
}
void Level::setName(std::string set_name) {
void Level::setName(sf::String set_name) {
name = set_name;
}
int Level::getTotalTime() {
int Level::getTotalTime() const {
return total_time;
}
void Level::setTotalTime(int set_total_time) {
// faisons rester le temps entre 10s et 59:59
set_total_time = std::min(set_total_time, 3599);
set_total_time = std::max(set_total_time, 10);
total_time = set_total_time;
}
sf::Sprite Level::getBackground() const {
return background;
}
void Level::setBackground(sf::Sprite set_background) {
background = set_background;
}
std::vector<ObjectPtr>& Level::getObjects() {
return objects;
}

View File

@ -3,7 +3,7 @@
Manager::Manager() : window(
sf::VideoMode(704, 480), "Skizzle", sf::Style::Default,
sf::ContextSettings(0, 0, 2)
), window_view(window.getView()), title(""), view(NULL) {}
), window_view(window.getView()), title(sf::String(L"")), view(NULL) {}
void Manager::start() {
while (window.isOpen()) {
@ -39,6 +39,10 @@ void Manager::start() {
}
}
std::shared_ptr<View> Manager::getView() {
return view;
}
void Manager::setView(std::shared_ptr<View> set_view) {
view = set_view;
}
@ -68,17 +72,17 @@ void Manager::setWindowView(sf::View set_window_view) {
window_view = set_window_view;
}
std::string Manager::getTitle() {
sf::String Manager::getTitle() {
return title;
}
void Manager::setTitle(std::string set_title) {
void Manager::setTitle(sf::String set_title) {
title = set_title;
if (title.empty()) {
window.setTitle("Skizzle");
if (title.isEmpty()) {
window.setTitle(sf::String(L"Skizzle"));
} else {
window.setTitle("Skizzle - " + title);
window.setTitle(sf::String(L"Skizzle ") + title);
}
}

View File

@ -23,7 +23,7 @@ Object::Object() :
Object::~Object() {}
void Object::load(std::ifstream& file, std::shared_ptr<Object> object) {
void Object::load(std::ifstream& file, ObjectPtr object) {
// lecture de la position de l'objet
float pos_x, pos_y;

View File

@ -13,8 +13,12 @@ Player::Player() : Object() {
Player::~Player() {}
std::shared_ptr<Object> Player::load(std::ifstream& file) {
std::shared_ptr<Object> object = std::shared_ptr<Object>(new Player);
ObjectPtr Player::clone() const {
return ObjectPtr(new Player(*this));
}
ObjectPtr Player::load(std::ifstream& file) {
ObjectPtr object = ObjectPtr(new Player);
std::shared_ptr<Player> player = std::dynamic_pointer_cast<Player>(object);
// lecture du numéro de joueur

View File

@ -0,0 +1,73 @@
#include "util/widget_button.hpp"
const unsigned int WidgetButton::ARROW_UP = 0;
const unsigned int WidgetButton::ARROW_DOWN = 1;
const sf::Color ARROW_COLOR = sf::Color(33, 33, 33);
const sf::Color CLICKED_COLOR = sf::Color(190, 190, 190);
const sf::Color REST_COLOR = sf::Color(230, 230, 230);
WidgetButton::WidgetButton(
Manager& manager, std::function<void(void)> click_cb,
sf::Vector2f size, unsigned int shape
) : manager(manager), click_cb(click_cb), shape(shape), button(size) {
if (shape == WidgetButton::ARROW_UP || shape == WidgetButton::ARROW_DOWN) {
button_shape.setPrimitiveType(sf::Triangles);
button_shape.resize(3);
button_shape[0].color = ARROW_COLOR;
button_shape[1].color = ARROW_COLOR;
button_shape[2].color = ARROW_COLOR;
}
}
bool WidgetButton::processEvent(const sf::Event& event) {
if (event.type == sf::Event::MouseButtonPressed) {
sf::Vector2f position(event.mouseButton.x, event.mouseButton.y);
if (event.mouseButton.button == sf::Mouse::Left) {
// clic gauche sur le bouton : appel de la callback
if (button.getGlobalBounds().contains(position)) {
click_cb();
return true;
}
}
}
return false;
}
void WidgetButton::draw(sf::Vector2f position) {
sf::RenderWindow& window = manager.getWindow();
// positionnement du bouton
button.setPosition(position);
sf::FloatRect box = button.getGlobalBounds();
sf::Vector2f center(box.left + box.width / 2, box.top + box.height / 2);
if (shape == WidgetButton::ARROW_UP) {
button_shape[0].position = center + sf::Vector2f(-5, 2);
button_shape[1].position = center + sf::Vector2f(5, 2);
button_shape[2].position = center + sf::Vector2f(0, -2);
}
if (shape == WidgetButton::ARROW_DOWN) {
button_shape[0].position = center + sf::Vector2f(-5, -2);
button_shape[1].position = center + sf::Vector2f(5, -2);
button_shape[2].position = center + sf::Vector2f(0, 2);
}
// coloration des boutons si enfoncement
button.setFillColor(REST_COLOR);
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
sf::Vector2f mouse_position = (sf::Vector2f) sf::Mouse::getPosition(window);
if (button.getGlobalBounds().contains(mouse_position)) {
button.setFillColor(CLICKED_COLOR);
}
}
window.draw(button);
window.draw(button_shape);
}

113
src/util/widget_timer.cpp Normal file
View File

@ -0,0 +1,113 @@
#include <cmath>
#include "util/widget_timer.hpp"
WidgetTimer::WidgetTimer(Manager& manager, bool can_change, std::function<void(int)> time_left_cb) :
manager(manager), can_change(can_change), time_left_cb(time_left_cb),
timer_zone(sf::Vector2f(100, 32)),
timer_up(manager, std::bind(&WidgetTimer::addTime, this), sf::Vector2f(30, 16), WidgetButton::ARROW_UP),
timer_down(manager, std::bind(&WidgetTimer::subtractTime, this), sf::Vector2f(30, 16), WidgetButton::ARROW_DOWN) {
// initialisation des formes
timer_seconds_text.setFont(manager.getResourceManager().getFont("monoid.ttf"));
timer_seconds_text.setCharacterSize(18);
timer_seconds_text.setColor(sf::Color::Black);
timer_sep_text.setString(":");
timer_sep_text.setFont(manager.getResourceManager().getFont("monoid.ttf"));
timer_sep_text.setCharacterSize(18);
timer_sep_text.setColor(sf::Color::Black);
timer_minutes_text.setFont(manager.getResourceManager().getFont("monoid.ttf"));
timer_minutes_text.setCharacterSize(18);
timer_minutes_text.setColor(sf::Color::Black);
}
bool WidgetTimer::processEvent(const sf::Event& event) {
// si le timer n'est pas modifiable, pas d'évent à gérer
if (!can_change) {
return false;
}
// gestion des boutons
if (timer_up.processEvent(event)) {
return true;
}
if (timer_down.processEvent(event)) {
return true;
}
if (event.type == sf::Event::MouseButtonPressed) {
sf::Vector2f position(event.mouseButton.x, event.mouseButton.y);
// clic dans le widget : ne rien faire, mais empêcher le traversement
if (timer_zone.getGlobalBounds().contains(position)) {
return true;
}
}
if (event.type == sf::Event::MouseWheelScrolled && event.mouseWheelScroll.wheel == sf::Mouse::VerticalWheel) {
// scroll sur le timer : modification du temps alloué au niveau
sf::Vector2f position(event.mouseWheelScroll.x, event.mouseWheelScroll.y);
if (timer_zone.getGlobalBounds().contains(position)) {
time_left_cb(time_left + round(event.mouseWheelScroll.delta * 10));
return true;
}
}
return false;
}
void WidgetTimer::draw(sf::Vector2f position) {
sf::RenderWindow& window = manager.getWindow();
// zone de fond du timer
timer_zone.setPosition(position);
window.draw(timer_zone);
// affichage du temps du niveau
sf::String minutes = std::to_string(time_left / 60);
sf::String seconds = std::to_string(time_left % 60);
// ajout d'un zéro devant les secondes si nécessaire
if (minutes.getSize() == 1) {
minutes = "0" + minutes;
}
if (seconds.getSize() == 1) {
seconds = "0" + seconds;
}
timer_minutes_text.setString(minutes);
timer_seconds_text.setString(seconds);
float base_x = can_change ? 30 : 45;
timer_sep_text.setPosition(position + sf::Vector2f(base_x, 6));
timer_seconds_text.setPosition(position + sf::Vector2f(base_x + 8, 6));
timer_minutes_text.setPosition(position + sf::Vector2f(
base_x - 3 - floor(timer_minutes_text.getGlobalBounds().width), 6
));
window.draw(timer_sep_text);
window.draw(timer_seconds_text);
window.draw(timer_minutes_text);
// interface de modification du temps
if (can_change) {
timer_up.draw(position + sf::Vector2f(70, 0));
timer_down.draw(position + sf::Vector2f(70, 16));
}
}
void WidgetTimer::addTime() {
time_left_cb(time_left + 1);
}
void WidgetTimer::subtractTime() {
time_left_cb(time_left - 1);
}
void WidgetTimer::setTimeLeft(int set_time_left) {
time_left = set_time_left;
}