From 952a8af9c6f4724f00328993a117c5c05767be23 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Fri, 27 Jan 2017 16:07:50 +0000 Subject: [PATCH] Register things that need the world size as observers to WorldGrid. Character and WorldViewport get notified when the world size is set. This way even if world size never changes they still get notified about the proper world size as soon as that information is ready, be it during instantiation or registration. Previously I was having trouble because at the point where Character was instantiated, the world size was not known yet. As such, I had to call a character.update_world_size() function after it became available. The same happened for the viewport, and this looked a bit too brittle from the programmer's perspective. Now you just instantiate the Character and things get sorted out automatically. --- CMakeLists.txt | 5 ++-- src/character.cpp | 34 +++++++++++++++++----- src/character.hpp | 12 +++++--- src/constrained_value.hpp | 49 ++++++++++++++++++++----------- src/ingamescene.cpp | 5 ++-- src/texture.cpp | 4 +++ src/texture.hpp | 1 + src/worldgrid.cpp | 26 +++++++++++++++-- src/worldgrid.hpp | 12 +++++++- src/worldsizenotifiable.cpp | 57 +++++++++++++++++++++++++++++++++++++ src/worldsizenotifiable.hpp | 51 +++++++++++++++++++++++++++++++++ src/worldviewport.cpp | 9 ++++-- src/worldviewport.hpp | 8 ++++-- 13 files changed, 234 insertions(+), 39 deletions(-) create mode 100644 src/worldsizenotifiable.cpp create mode 100644 src/worldsizenotifiable.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 59596cc..d51b57b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,9 +7,9 @@ include(TargetArch) include(FindPkgConfig) set(common_gcc_flags "-Wall -Wextra -pedantic -Wconversion") -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${common_gcc_flags}") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${common_gcc_flags} -O0") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${common_gcc_flags}") -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${common_gcc_flags}") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${common_gcc_flags} -O0") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${common_gcc_flags}") option(CURRY_FORCE_OPENGLES "Try to chose the openGL ES renderer if available. Enable this on Raspberry Pi" OFF) @@ -67,6 +67,7 @@ add_executable(${PROJECT_NAME} src/movingobject.cpp src/character.cpp src/rect_to_sdl.cpp + src/worldsizenotifiable.cpp ) target_include_directories(${PROJECT_NAME} SYSTEM diff --git a/src/character.cpp b/src/character.cpp index f5568e1..fd572ea 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016 Michele "King_DuckZ" Santullo + Copyright 2016, 2017 Michele "King_DuckZ" Santullo This file is part of MyCurry. @@ -18,15 +18,31 @@ */ #include "character.hpp" +#include namespace curry { - Character::Character() : - m_position(0.0f) + namespace { + vec2f get_safe_width_height (const Texture& parTex) { + return (parTex.texture() ? + vector_cast(parTex.width_height()) + : + vec2f(0.0f) + ); + } + } //unnamed namespace + + Character::Character (const WorldSizeNotifiable::DeferredRegister& parDeferredRegister) : + WorldSizeNotifiable(parDeferredRegister), + m_position(vec2f(0.0f), vec2f(0.0f), vec2f(0.0f)) { } void Character::load (const char* parTexturePath, cloonel::SDLMain& parSDLMain) { + const auto old_wh = get_safe_width_height(m_texture); m_texture.load(parTexturePath, parSDLMain); + const auto new_wh = vector_cast(m_texture.width_height()); + if (m_position.get_min() != m_position.get_max()) + m_position.change_minmax(m_position.get_min(), m_position.get_max() + old_wh - new_wh); } void Character::unload() { @@ -38,11 +54,11 @@ namespace curry { } const vec2f& Character::position() const { - return m_position; + return m_position.get(); } vec2us Character::width_height() const { - return m_texture.width_height(); + return get_safe_width_height(m_texture); } Texture& Character::texture() { @@ -51,8 +67,8 @@ namespace curry { Rect Character::destination_rect (const vec2f& parWorldPos) const { return Rect( - m_position - parWorldPos, - m_position - parWorldPos + vector_cast(width_height()) + m_position.get() - parWorldPos, + m_position.get() - parWorldPos + vector_cast(width_height()) ); } @@ -62,4 +78,8 @@ namespace curry { vector_cast(width_height()) ); } + + void Character::world_size_changed (const vec2us&, const vec2i& parPixelSize) { + m_position.change_minmax(m_position.get_min(), vector_cast(parPixelSize)); + } } //namespace curry diff --git a/src/character.hpp b/src/character.hpp index ba0495b..43abf80 100644 --- a/src/character.hpp +++ b/src/character.hpp @@ -1,5 +1,5 @@ /* - Copyright 2016 Michele "King_DuckZ" Santullo + Copyright 2016, 2017 Michele "King_DuckZ" Santullo This file is part of MyCurry. @@ -22,15 +22,18 @@ #include "texture.hpp" #include "vector.hpp" #include "rect.hpp" +#include "constrained_position.hpp" +#include "worldsizenotifiable.hpp" namespace cloonel { class SDLMain; } //namespace cloonel namespace curry { - class Character { + class Character : public WorldSizeNotifiable { public: - Character(); + explicit Character (const WorldSizeNotifiable::DeferredRegister& parDeferredRegister); + virtual ~Character() noexcept = default; void load (const char* parTexturePath, cloonel::SDLMain& parSDLMain); void unload(); @@ -40,9 +43,10 @@ namespace curry { Texture& texture(); Rect destination_rect (const vec2f& parWorldPos) const; Rect source_rect() const; + virtual void world_size_changed (const vec2us& parSize, const vec2i& parPixelSize); private: + ConstrainedPosition m_position; Texture m_texture; - vec2f m_position; }; } //namespace curry diff --git a/src/constrained_value.hpp b/src/constrained_value.hpp index 1157388..ce1f63e 100644 --- a/src/constrained_value.hpp +++ b/src/constrained_value.hpp @@ -21,6 +21,7 @@ #include #include +#include namespace curry { template @@ -37,20 +38,18 @@ namespace curry { template > class ConstrainedValue { + using T_param = typename boost::call_traits::param_type; public: - ConstrainedValue ( - typename boost::call_traits::param_type parMin, - typename boost::call_traits::param_type parMax, - typename boost::call_traits::param_type parValue - ); + ConstrainedValue (T_param parMin, T_param parMax, T_param parValue); virtual ~ConstrainedValue() noexcept = default; - void set (typename boost::call_traits::param_type parNew); + void set (T_param parNew); typename boost::call_traits::const_reference get() const; - void change_minmax ( - typename boost::call_traits::param_type parMin, - typename boost::call_traits::param_type parMax - ); + typename boost::call_traits::const_reference get_min() const; + typename boost::call_traits::const_reference get_max() const; + void change_minmax (T_param parMin, T_param parMax); + + ConstrainedValue& operator= (T_param parValue); private: T m_min; @@ -60,18 +59,19 @@ namespace curry { template ConstrainedValue::ConstrainedValue ( - typename boost::call_traits::param_type parMin, - typename boost::call_traits::param_type parMax, - typename boost::call_traits::param_type parValue + T_param parMin, + T_param parMax, + T_param parValue ) : m_min(parMin), m_max(parMax), m_value(parValue) { + assert(parMin <= parMax); } template - void ConstrainedValue::set (typename boost::call_traits::param_type parNew) { + void ConstrainedValue::set (T_param parNew) { m_value = Constrainer()(m_min, m_max, parNew); } @@ -80,13 +80,30 @@ namespace curry { return m_value; } + template + typename boost::call_traits::const_reference ConstrainedValue::get_min() const { + return m_min; + } + + template + typename boost::call_traits::const_reference ConstrainedValue::get_max() const { + return m_max; + } + template void ConstrainedValue::change_minmax ( - typename boost::call_traits::param_type parMin, - typename boost::call_traits::param_type parMax + T_param parMin, + T_param parMax ) { + assert(parMin <= parMax); m_min = parMin; m_max = parMax; this->set(m_value); } + + template + ConstrainedValue& ConstrainedValue::operator= (T_param parValue) { + this->set(parValue); + return *this; + } } //namespace curry diff --git a/src/ingamescene.cpp b/src/ingamescene.cpp index 962efa4..edc77bc 100644 --- a/src/ingamescene.cpp +++ b/src/ingamescene.cpp @@ -50,8 +50,9 @@ namespace curry { struct IngameScene::LocalData { LocalData (const vec2us& parTileSize, cloonel::SDLMain* parSDLMain) : world(parTileSize), - viewport(&world, vec2f(parSDLMain->WidthHeight())), - player(parTileSize) + viewport(&world, vec2f(parSDLMain->WidthHeight()), WorldSizeNotifiable::DeferredRegister(&world, &viewport)), + player(parTileSize), + character(WorldSizeNotifiable::DeferredRegister(&world, &character)) { } diff --git a/src/texture.cpp b/src/texture.cpp index d6a9c76..7f2201d 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -61,6 +61,10 @@ namespace curry { return m_texture.get(); } + const SDL_Texture* Texture::texture() const { + return m_texture.get(); + } + void Texture::load (const char* parPath, cloonel::SDLMain& parSDLMain) { m_texture = load_texture(parPath, parSDLMain, false, RGBA()); } diff --git a/src/texture.hpp b/src/texture.hpp index f9ffcae..119cef0 100644 --- a/src/texture.hpp +++ b/src/texture.hpp @@ -47,6 +47,7 @@ namespace curry { void load (const char* parPath, cloonel::SDLMain& parSDLMain, RGBA parColorKey); void unload() noexcept; SDL_Texture* texture(); + const SDL_Texture* texture() const; vec2us width_height() const; private: diff --git a/src/worldgrid.cpp b/src/worldgrid.cpp index 7e213eb..4a7ee04 100644 --- a/src/worldgrid.cpp +++ b/src/worldgrid.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016 Michele "King_DuckZ" Santullo + Copyright 2016, 2017 Michele "King_DuckZ" Santullo This file is part of MyCurry. @@ -18,6 +18,7 @@ */ #include "worldgrid.hpp" +#include "worldsizenotifiable.hpp" #include #include #include @@ -59,7 +60,7 @@ namespace curry { new_vec.push_back(tile); } ++m_layer_count; - m_world_size = parWorldSize; + set_world_size(parWorldSize); std::swap(m_layers, new_vec); } @@ -81,7 +82,7 @@ namespace curry { m_layer_count = static_cast(parLayers.size()); std::swap(m_layers, new_vec); - m_world_size = parWorldSize; + set_world_size(parWorldSize); } uint16_t WorldGrid::layer_count() const { @@ -98,6 +99,25 @@ namespace curry { m_layer_count = 0; } + void WorldGrid::set_world_size (const vec2us& parSize) { + if (parSize != m_world_size) { + m_world_size = parSize; + + for (auto& observer : m_world_size_watchers) { + observer->world_size_changed(m_world_size, world_size_pixel(*this)); + } + } + } + + auto WorldGrid::register_observer (WorldSizeNotifiable* parWatcher) -> WorldSizeWatcherTicket { + assert(parWatcher); + return m_world_size_watchers.Add(parWatcher); + } + + void WorldGrid::unregister_observer (WorldSizeWatcherTicket parTicket) { + m_world_size_watchers.Remove(parTicket); + } + vec2i world_size_pixel (const WorldGrid& parWorld) { return parWorld.tile_size() * parWorld.world_size(); } diff --git a/src/worldgrid.hpp b/src/worldgrid.hpp index e45dd8d..6e405a1 100644 --- a/src/worldgrid.hpp +++ b/src/worldgrid.hpp @@ -1,5 +1,5 @@ /* - Copyright 2016 Michele "King_DuckZ" Santullo + Copyright 2016, 2017 Michele "King_DuckZ" Santullo This file is part of MyCurry. @@ -21,11 +21,16 @@ #include "vector.hpp" #include "tileindextype.hpp" +#include "observersmanager.hpp" #include #include namespace curry { + class WorldSizeNotifiable; + class WorldGrid { + using WorldSizeWatcherTicket = cloonel::ObserversManager::TicketType; + public: explicit WorldGrid (vec2us parTileSize); @@ -36,10 +41,15 @@ namespace curry { void add_layer (vec2us parWorldSize, const std::vector& parLayer); void set_layers (vec2us parWorldSize, const std::vector>& parLayers); void unload_layers(); + WorldSizeWatcherTicket register_observer (WorldSizeNotifiable* parWatcher); + void unregister_observer (WorldSizeWatcherTicket parTicket); private: + void set_world_size (const vec2us& parSize); + std::size_t tile_count() const; + cloonel::ObserversManager m_world_size_watchers; std::vector m_layers; vec2us m_tile_size; vec2us m_world_size; diff --git a/src/worldsizenotifiable.cpp b/src/worldsizenotifiable.cpp new file mode 100644 index 0000000..3ffcf7d --- /dev/null +++ b/src/worldsizenotifiable.cpp @@ -0,0 +1,57 @@ +/* + Copyright 2017 Michele "King_DuckZ" Santullo + + This file is part of MyCurry. + + MyCurry is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + MyCurry is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MyCurry. If not, see . +*/ + +#include "worldsizenotifiable.hpp" +#include "worldgrid.hpp" +#include +#include +#include + +namespace curry { + WorldSizeNotifiable::WorldSizeNotifiable (const DeferredRegister& parDeferredRegister) : + m_ticket(cloonel::ObserversManager::Ticket_Null), + m_world(parDeferredRegister.world()) + { + assert(m_world); + } + + WorldSizeNotifiable::~WorldSizeNotifiable() noexcept { + m_world->unregister_observer(m_ticket); + } + + WorldSizeNotifiable::DeferredRegister::DeferredRegister (WorldGrid* parWorld, WorldSizeNotifiable* parObj) noexcept : + m_notifiable(parObj), + m_world(parWorld) + { + assert(m_notifiable); + assert(m_world); + } + + WorldSizeNotifiable::DeferredRegister::~DeferredRegister() { + assert(m_notifiable); + assert(m_world); + if (not std::uncaught_exception()) + m_notifiable->register_to_world(m_world); + } + + void WorldSizeNotifiable::register_to_world (WorldGrid* parWorld) { + assert(cloonel::ObserversManager::Ticket_Null == m_ticket); + m_ticket = parWorld->register_observer(this); + } +} //namespace curry diff --git a/src/worldsizenotifiable.hpp b/src/worldsizenotifiable.hpp new file mode 100644 index 0000000..3d5d251 --- /dev/null +++ b/src/worldsizenotifiable.hpp @@ -0,0 +1,51 @@ +/* + Copyright 2017 Michele "King_DuckZ" Santullo + + This file is part of MyCurry. + + MyCurry is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + MyCurry is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MyCurry. If not, see . +*/ + +#pragma once + +#include "vector.hpp" +#include "observersmanager.hpp" + +namespace curry { + class WorldGrid; + + class WorldSizeNotifiable { + public: + class DeferredRegister { + public: + DeferredRegister (WorldGrid* parWorld, WorldSizeNotifiable* parObj) noexcept; + ~DeferredRegister(); + WorldGrid* world() const { return m_world; } + private: + WorldSizeNotifiable* const m_notifiable; + WorldGrid* const m_world; + }; + + virtual void world_size_changed (const vec2us& parSize, const vec2i& parPixelSize) = 0; + + protected: + explicit WorldSizeNotifiable (const DeferredRegister& parDeferredRegister); + virtual ~WorldSizeNotifiable() noexcept; + void register_to_world (WorldGrid* parWorld); + + private: + cloonel::ObserversManager::TicketType m_ticket; + WorldGrid* m_world; + }; +} //namespace curry diff --git a/src/worldviewport.cpp b/src/worldviewport.cpp index 360f2df..611b668 100644 --- a/src/worldviewport.cpp +++ b/src/worldviewport.cpp @@ -36,8 +36,9 @@ namespace curry { ); } } //unnamed namespace - WorldViewport::WorldViewport (WorldGrid* parWorld, vec2f parSize) : - m_position(vec2f(0.0f), vector_cast(world_size_pixel(*parWorld)) - parSize, vec2f(0.0f)), + WorldViewport::WorldViewport (WorldGrid* parWorld, vec2f parSize, const WorldSizeNotifiable::DeferredRegister& parDeferredRegister) : + WorldSizeNotifiable(parDeferredRegister), + m_position(vec2f(0.0f), vec2f(0.0f), vec2f(0.0f)), m_size(parSize), m_world(parWorld) { @@ -69,6 +70,10 @@ namespace curry { m_position.set(parPos); } + void WorldViewport::world_size_changed (const vec2us&, const vec2i& parPixelSize) { + m_position.change_minmax(vec2f(0.0f), vector_cast(parPixelSize) - m_size); + } + const TileIndex* WorldViewport::tile (const vec2us& parIndex) const { return world()->tile(parIndex + starting_index(*this)); } diff --git a/src/worldviewport.hpp b/src/worldviewport.hpp index 682ae14..228fd8f 100644 --- a/src/worldviewport.hpp +++ b/src/worldviewport.hpp @@ -23,15 +23,18 @@ #include "tileindextype.hpp" #include "tileiterator.hpp" #include "constrained_position.hpp" +#include "worldsizenotifiable.hpp" namespace curry { class WorldGrid; - class WorldViewport { + class WorldViewport : public WorldSizeNotifiable { public: typedef TileIterator iterator; - WorldViewport (WorldGrid* parWorld, vec2f parSize); + WorldViewport (WorldGrid* parWorld, vec2f parSize, const WorldSizeNotifiable::DeferredRegister& parDeferredRegister); + virtual ~WorldViewport() noexcept = default; + const WorldGrid* world() const; const vec2f& position() const; const vec2f& size() const; @@ -39,6 +42,7 @@ namespace curry { void allow_overscan (bool parAllow); void set_position (const vec2f& parPos); + virtual void world_size_changed (const vec2us& parSize, const vec2i& parPixelSize) override; iterator begin(); iterator end();