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.
This commit is contained in:
King_DuckZ 2017-01-27 16:07:50 +00:00
parent 0dab671ce4
commit 952a8af9c6
13 changed files with 234 additions and 39 deletions

View file

@ -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

View file

@ -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 <ciso646>
namespace curry {
Character::Character() :
m_position(0.0f)
namespace {
vec2f get_safe_width_height (const Texture& parTex) {
return (parTex.texture() ?
vector_cast<vec2f>(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<vec2f>(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<float> Character::destination_rect (const vec2f& parWorldPos) const {
return Rect<float>(
m_position - parWorldPos,
m_position - parWorldPos + vector_cast<vec2f>(width_height())
m_position.get() - parWorldPos,
m_position.get() - parWorldPos + vector_cast<vec2f>(width_height())
);
}
@ -62,4 +78,8 @@ namespace curry {
vector_cast<vec2f>(width_height())
);
}
void Character::world_size_changed (const vec2us&, const vec2i& parPixelSize) {
m_position.change_minmax(m_position.get_min(), vector_cast<vec2f>(parPixelSize));
}
} //namespace curry

View file

@ -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<float> destination_rect (const vec2f& parWorldPos) const;
Rect<float> source_rect() const;
virtual void world_size_changed (const vec2us& parSize, const vec2i& parPixelSize);
private:
ConstrainedPosition<vec2f> m_position;
Texture m_texture;
vec2f m_position;
};
} //namespace curry

View file

@ -21,6 +21,7 @@
#include <boost/call_traits.hpp>
#include <boost/algorithm/clamp.hpp>
#include <cassert>
namespace curry {
template <typename T>
@ -37,20 +38,18 @@ namespace curry {
template <typename T, typename Constrainer=ScalarConstrainer<T>>
class ConstrainedValue {
using T_param = typename boost::call_traits<T>::param_type;
public:
ConstrainedValue (
typename boost::call_traits<T>::param_type parMin,
typename boost::call_traits<T>::param_type parMax,
typename boost::call_traits<T>::param_type parValue
);
ConstrainedValue (T_param parMin, T_param parMax, T_param parValue);
virtual ~ConstrainedValue() noexcept = default;
void set (typename boost::call_traits<T>::param_type parNew);
void set (T_param parNew);
typename boost::call_traits<T>::const_reference get() const;
void change_minmax (
typename boost::call_traits<T>::param_type parMin,
typename boost::call_traits<T>::param_type parMax
);
typename boost::call_traits<T>::const_reference get_min() const;
typename boost::call_traits<T>::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 <typename T, typename Constrainer>
ConstrainedValue<T, Constrainer>::ConstrainedValue (
typename boost::call_traits<T>::param_type parMin,
typename boost::call_traits<T>::param_type parMax,
typename boost::call_traits<T>::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 <typename T, typename Constrainer>
void ConstrainedValue<T, Constrainer>::set (typename boost::call_traits<T>::param_type parNew) {
void ConstrainedValue<T, Constrainer>::set (T_param parNew) {
m_value = Constrainer()(m_min, m_max, parNew);
}
@ -80,13 +80,30 @@ namespace curry {
return m_value;
}
template <typename T, typename Constrainer>
typename boost::call_traits<T>::const_reference ConstrainedValue<T, Constrainer>::get_min() const {
return m_min;
}
template <typename T, typename Constrainer>
typename boost::call_traits<T>::const_reference ConstrainedValue<T, Constrainer>::get_max() const {
return m_max;
}
template <typename T, typename Constrainer>
void ConstrainedValue<T, Constrainer>::change_minmax (
typename boost::call_traits<T>::param_type parMin,
typename boost::call_traits<T>::param_type parMax
T_param parMin,
T_param parMax
) {
assert(parMin <= parMax);
m_min = parMin;
m_max = parMax;
this->set(m_value);
}
template <typename T, typename Constrainer>
ConstrainedValue<T, Constrainer>& ConstrainedValue<T, Constrainer>::operator= (T_param parValue) {
this->set(parValue);
return *this;
}
} //namespace curry

View file

@ -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))
{
}

View file

@ -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());
}

View file

@ -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:

View file

@ -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 <cassert>
#include <utility>
#include <algorithm>
@ -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<uint16_t>(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();
}

View file

@ -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 <vector>
#include <cstdint>
namespace curry {
class WorldSizeNotifiable;
class WorldGrid {
using WorldSizeWatcherTicket = cloonel::ObserversManager<WorldSizeNotifiable*>::TicketType;
public:
explicit WorldGrid (vec2us parTileSize);
@ -36,10 +41,15 @@ namespace curry {
void add_layer (vec2us parWorldSize, const std::vector<TileIndex>& parLayer);
void set_layers (vec2us parWorldSize, const std::vector<std::vector<TileIndex>>& 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<WorldSizeNotifiable*> m_world_size_watchers;
std::vector<TileIndex> m_layers;
vec2us m_tile_size;
vec2us m_world_size;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "worldsizenotifiable.hpp"
#include "worldgrid.hpp"
#include <exception>
#include <cassert>
#include <ciso646>
namespace curry {
WorldSizeNotifiable::WorldSizeNotifiable (const DeferredRegister& parDeferredRegister) :
m_ticket(cloonel::ObserversManager<WorldSizeNotifiable*>::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<WorldSizeNotifiable*>::Ticket_Null == m_ticket);
m_ticket = parWorld->register_observer(this);
}
} //namespace curry

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<WorldSizeNotifiable*>::TicketType m_ticket;
WorldGrid* m_world;
};
} //namespace curry

View file

@ -36,8 +36,9 @@ namespace curry {
);
}
} //unnamed namespace
WorldViewport::WorldViewport (WorldGrid* parWorld, vec2f parSize) :
m_position(vec2f(0.0f), vector_cast<vec2f>(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<vec2f>(parPixelSize) - m_size);
}
const TileIndex* WorldViewport::tile (const vec2us& parIndex) const {
return world()->tile(parIndex + starting_index(*this));
}

View file

@ -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();