Skip to content

Instantly share code, notes, and snippets.

@PeterHajdu
Last active December 14, 2015 09:09
Show Gist options
  • Save PeterHajdu/5063326 to your computer and use it in GitHub Desktop.
Save PeterHajdu/5063326 to your computer and use it in GitHub Desktop.
object container
*swp
*~
*.o
a.out
demo
testFile
a
gmon*
prof*
*core*

object container poc code

#include "Aabb.hpp"
platformer::Aabb::Aabb()
: m_bottomLeft{ 0, 0 }
, m_topRight{ 0, 0 }
{
}
platformer::Aabb::Aabb( const Coordinate& bottomLeft, const Coordinate& topRight )
: m_bottomLeft( bottomLeft )
, m_topRight( topRight )
{
}
platformer::Aabb::Aabb( const TileCoordinate& bottomLeft, const TileCoordinate& topRight )
: m_bottomLeft{ Coordinate::Type( bottomLeft.x * TILEWIDTH ),
Coordinate::Type( bottomLeft.y * TILEHEIGHT ) }
, m_topRight{ Coordinate::Type( ( topRight.x + 1 ) * TILEWIDTH ),
Coordinate::Type( ( topRight.y + 1 ) * TILEHEIGHT ) }
{
}
bool
platformer::Aabb::intersectsWith( const Aabb& other) const
{
return ! (m_bottomLeft.x >= other.m_topRight.x ||
other.m_bottomLeft.x >= m_topRight.x ||
m_bottomLeft.y >= other.m_topRight.y ||
other.m_bottomLeft.y >= m_topRight.y );
}
bool
platformer::Aabb::contains( const Coordinate& coordinate ) const
{
return coordinate.x <= m_topRight.x
&& coordinate.x >= m_bottomLeft.x
&& coordinate.y >= m_bottomLeft.y
&& coordinate.y <= m_topRight.y;
}
const platformer::Coordinate&
platformer::Aabb::bottomLeft() const
{
return m_bottomLeft;
}
const platformer::Coordinate&
platformer::Aabb::topRight() const
{
return m_topRight;
}
#pragma once
#include <vector>
#include "BaseTypes.hpp"
namespace platformer
{
/*
* Axis aligned bounding box with tile coordinates.
*/
class Aabb
{
public:
Aabb();
Aabb( const TileCoordinate& bottomLeft, const TileCoordinate& topRight );
Aabb( const Coordinate& bottomLeft, const Coordinate& topRight );
bool intersectsWith( const Aabb& other) const;
bool contains( const Coordinate& coordinate ) const;
const Coordinate& bottomLeft() const;
const Coordinate& topRight() const;
private:
Coordinate m_bottomLeft;
Coordinate m_topRight;
};
}
#include "BaseTypes.hpp"
bool
platformer::operator==( const TileCoordinate& l,
const TileCoordinate& r )
{
return l.x == r.x &&
l.y == r.y;
}
const platformer::TileCoordinate
platformer::operator+=( TileCoordinate& l, const TileCoordinate& r )
{
l.x += r.x;
l.y += r.y;
return l;
}
const platformer::TileCoordinate
platformer::operator+( TileCoordinate l, const TileCoordinate& r )
{
return l+=r;
}
std::ostream&
platformer::operator<<( std::ostream& output, const TileCoordinate& coordinate )
{
output << "( " << coordinate.x << ", " << coordinate.y << " )";
return output;
}
platformer::TileCoordinate
platformer::tileCoordinateOf( const Coordinate& coordinate )
{
return TileCoordinate{
TileCoordinate::Type( coordinate.x / TILEWIDTH ),
TileCoordinate::Type( coordinate.y / TILEHEIGHT ) };
}
const platformer::Coordinate
platformer::operator+=( Coordinate& l, const Coordinate& r )
{
l.x += r.x;
l.y += r.y;
return l;
}
const platformer::Coordinate
platformer::operator+( Coordinate l, const Coordinate& r )
{
return l+=r;
}
const platformer::Coordinate
platformer::operator-=( Coordinate& l, const Coordinate& r )
{
l.x -= r.x;
l.y -= r.y;
return l;
}
const platformer::Coordinate
platformer::operator-( Coordinate l, const Coordinate& r )
{
return l-=r;
}
std::ostream&
platformer::operator<<( std::ostream& output, const Coordinate& coordinate )
{
output << "( " << coordinate.x << ", " << coordinate.y << " )";
return output;
}
#pragma once
#include <ostream>
namespace platformer
{
static const int TILEWIDTH = 32;
static const int TILEHEIGHT = 32;
struct TileCoordinate
{
typedef int Type;
Type x;
Type y;
};
const TileCoordinate operator+=( TileCoordinate&, const TileCoordinate& );
const TileCoordinate operator+( TileCoordinate, const TileCoordinate& );
bool operator==( const TileCoordinate&, const TileCoordinate& );
std::ostream& operator<<( std::ostream&, const TileCoordinate& );
struct Coordinate
{
typedef double Type;
Type x;
Type y;
};
const Coordinate operator+=( Coordinate&, const Coordinate& );
const Coordinate operator+( Coordinate, const Coordinate& );
const Coordinate operator-=( Coordinate&, const Coordinate& );
const Coordinate operator-( Coordinate, const Coordinate& );
std::ostream& operator<<( std::ostream&, const Coordinate& );
typedef Coordinate Velocity;
TileCoordinate tileCoordinateOf( const Coordinate& );
}
#include <allegro5/allegro.h>
#include <stdlib.h>
#include <iostream>
#include <cmath>
#include <memory>
#include "Object.hpp"
#include "Updaters.hpp"
#include "ObjectContainer.hpp"
#include "Aabb.hpp"
#include "Graphics.hpp"
#include "World.hpp"
#include "WorldGenerator.hpp"
#include "PerlinNoise.hpp"
class Cricket : public platformer::Object
{
public:
Cricket() = delete;
Cricket( platformer::View& graphics, const platformer::ObjectContainer& tiles )
: platformer::Object( { { 50, 1750 }, { 0, 0 }, { 32, 32 } } )
, m_cricketPhysics( new platformer::MoveAndCollide( this, m_data, tiles ) )
, m_graphicsUpdater( new platformer::PlayerGraphics ( m_data, graphics ) )
{
platformer::Updater* gravity( new platformer::SpeedAddition( m_data, { 0, -0.3 } ) );
registerUpdater( gravity );
registerUpdater( new platformer::SpeedLimiter( m_data, { 5, 100 } ) );
registerUpdater( m_cricketPhysics );
registerUpdater( m_graphicsUpdater );
}
virtual ~Cricket() noexcept ( true )
{
}
const platformer::Coordinate& center() const
{
return m_data.coordinate;
}
void up()
{
m_cricketPhysics->jump();
}
void right()
{
m_cricketPhysics->moveRight();
m_graphicsUpdater->step();
}
void left()
{
m_cricketPhysics->moveLeft();
m_graphicsUpdater->step();
}
void fly()
{
m_data.velocity.y += 1;
}
private:
platformer::MoveAndCollide* m_cricketPhysics;
platformer::PlayerGraphics* m_graphicsUpdater;
};
class TerrainGenerator : public platformer::ObjectFactory
{
public:
TerrainGenerator( platformer::ObjectFactory& objectFactory )
: m_objectFactory( objectFactory )
, m_perlinNoise( 0.4, 5 )
{
}
virtual platformer::Object::Ref buildTopTile( const platformer::TileCoordinate& coordinate )
{
return m_objectFactory.buildGroundTile( coordinate );
}
virtual platformer::Object::Ref buildCaveTile( const platformer::TileCoordinate& coordinate )
{
return m_objectFactory.buildCaveTile( coordinate );
}
virtual platformer::Object::Ref buildGroundTile( const platformer::TileCoordinate& coordinate )
{
const int currentGroundLevel( groundLevel( coordinate ) );
if ( currentGroundLevel < coordinate.y )
{
return platformer::Object::Ref( nullptr );
}
if ( currentGroundLevel == coordinate.y )
{
return m_objectFactory.buildTopTile( coordinate );
}
if ( !isRock( coordinate ) )
{
return m_objectFactory.buildCaveTile( coordinate );
}
return m_objectFactory.buildGroundTile( coordinate );
}
private:
int groundLevel( const platformer::TileCoordinate& coordinate ) const
{
int y( m_perlinNoise.noiseFor( double( coordinate.x ) / 20 ) * 20 );
while ( !isRock( platformer::TileCoordinate{ coordinate.x, y-- } ) ) {}
return y;
}
bool isRock( const platformer::TileCoordinate& coordinate ) const
{
const double CAVETHRESHOLD( -20 );
return m_perlinNoise.noiseFor( double( coordinate.y ) / 20, double( coordinate.x ) / 20 ) * 100 > CAVETHRESHOLD;
}
platformer::ObjectFactory& m_objectFactory;
platformer::PerlinNoise m_perlinNoise;
};
int main()
{
const int x( 640 );
const int y( 480 );
platformer::Graphics graphics( x, y );
platformer::Image backGround( "/home/tacsko/bg.png" );
platformer::View view( graphics, { x, y }, backGround );
al_install_keyboard();
platformer::ObjectContainer objects( { platformer::TileCoordinate{ -10000, -10000 },
platformer::TileCoordinate{ 10000, 10000 } } );
platformer::GraphicalObjectFactory factory( view );
TerrainGenerator terrainFactory( factory );
platformer::WorldGenerator worldGenerator( objects, terrainFactory );
Cricket* cricket( new Cricket( view, objects ) );
objects.insert( platformer::Object::Ref( cricket ) );
bool shouldQuit( false );
while ( !shouldQuit )
{
ALLEGRO_KEYBOARD_STATE keyState;
al_get_keyboard_state(&keyState);
if( al_key_down(&keyState, ALLEGRO_KEY_UP) )
{
cricket->up();
}
if( al_key_down(&keyState, ALLEGRO_KEY_RIGHT) )
{
cricket->right();
}
if( al_key_down(&keyState, ALLEGRO_KEY_LEFT) )
{
cricket->left();
}
if( al_key_down(&keyState, ALLEGRO_KEY_Q) )
{
shouldQuit = true;
}
if( al_key_down(&keyState, ALLEGRO_KEY_F) )
{
cricket->fly();
}
view.clear();
view.focus( cricket->center() );
std::vector< platformer::Object* > objectsInScreen;
platformer::Aabb visibleArea( view.visibleArea() );
worldGenerator.generate( visibleArea );
objects.collectIn( visibleArea, std::back_inserter( objectsInScreen ) );
std::for_each( begin( objectsInScreen ), end( objectsInScreen ),
[]( platformer::Object* object )
{
object->update();
} );
graphics.printfps();
graphics.printCoordinate( cricket->center() );
view.show();
}
return 0;
}
#pragma once
#include "Object.hpp"
namespace platformer
{
namespace test
{
class DummyObject : public platformer::Object
{
public:
DummyObject()
: Object( { { 0.0, 0.0 }, { 0.0, 0.0 }, { platformer::TILEWIDTH + 1, platformer::TILEHEIGHT + 1 } } )
, updateTimes( 0 )
{
}
DummyObject( const platformer::ObjectData& objectData )
: Object( objectData )
, updateTimes( 0 )
{
}
unsigned int updateTimes;
};
}
}
#include "Graphics.hpp"
#include "BaseTypes.hpp"
#include "Object.hpp"
#include "Aabb.hpp"
#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_font.h>
#include <sstream>
#include <algorithm>
platformer::Image::Image( const char* fileName )
: m_image( al_load_bitmap( fileName ) )
{
}
void
platformer::Image::draw( unsigned int x, unsigned int y )
{
al_draw_bitmap( m_image, x, y, 0 );
}
unsigned int
platformer::Image::height() const
{
return al_get_bitmap_height( m_image );
}
unsigned int
platformer::Image::width() const
{
return al_get_bitmap_width( m_image );
}
platformer::Image::~Image()
{
al_destroy_bitmap( m_image );
}
platformer::Graphics::Graphics( size_t x, size_t y )
: m_display( nullptr )
, m_width( x )
, m_height( y )
, m_font( 0 )
, m_previousRefreshTime()
{
al_init();
al_init_primitives_addon();
al_init_image_addon();
al_init_font_addon();
m_display = al_create_display( m_width, m_height );
assert( m_display );
m_font = al_create_builtin_font();
assert( m_font );
m_previousRefreshTime = al_get_time();
}
void
platformer::Graphics::printText( int x, int y, const char* text )
{
al_draw_text( m_font, al_map_rgb(255,255,255), x, y,ALLEGRO_ALIGN_LEFT, text );
}
void
platformer::Graphics::printfps()
{
const double newTime( al_get_time() );
std::stringstream fps;
fps << ( 1 / ( ( newTime - m_previousRefreshTime ) * 1000 ) );
m_previousRefreshTime = newTime;
printText( 100, 10, fps.str().c_str() );
}
void
platformer::Graphics::printCoordinate( const Coordinate& coordinate )
{
std::stringstream coord;
coord<< "( " << coordinate.x << ", " << coordinate.y << " )";
printText( 100, 30, coord.str().c_str() );
}
void
platformer::Graphics::clear()
{
al_clear_to_color( al_map_rgb( 0, 0, 0 ) );
}
void
platformer::Graphics::show()
{
al_flip_display();
}
platformer::Graphics::~Graphics()
{
al_destroy_display( m_display );
}
platformer::View::View( Graphics& graphics, const Coordinate& border, Drawable& backGround )
: m_graphics( graphics )
, m_borders( border )
, m_focus{ border.x / 2, border.y / 2 }
, m_bg( backGround )
, m_ground( "/home/tacsko/ground.png" )
, m_cave( "/home/tacsko/cave.png" )
, m_topGround( "/home/tacsko/tile.png" )
, m_drawOperations()
{
}
void
platformer::View::clear()
{
m_graphics.clear();
m_bg.draw( 0, 0 );
}
void
platformer::View::show()
{
std::for_each( begin( m_drawOperations ), end( m_drawOperations ),
[] ( DrawOperationContainer::value_type& operation )
{
std::for_each( begin( operation.second ), end( operation.second ),
[] ( DrawOperation& operation )
{
operation.drawable.draw( operation.x, operation.y );
} );
} );
m_drawOperations.clear();
m_graphics.show();
}
void
platformer::View::draw( const Coordinate& coordinate, Drawable& drawable, Layer layer )
{
const Coordinate topLeft( translate( coordinate ) );
m_drawOperations[ layer ].push_back(
DrawOperation{ drawable, int( topLeft.x ), int( topLeft.y ) } );
}
void
platformer::View::focus( const Coordinate& focus )
{
m_focus = focus;
}
platformer::Aabb
platformer::View::visibleArea() const
{
const Coordinate half{ m_borders.x / 2, m_borders.y / 2 };
return Aabb(
Coordinate( m_focus - half ),
Coordinate( m_focus + half ) );
}
platformer::Coordinate
platformer::View::translate( const Coordinate& coordinate )
{
return Coordinate{
coordinate.x - m_focus.x + m_borders.x / 2,
m_borders.y * 2/3 - ( coordinate.y - m_focus.y ) };
}
platformer::Image&
platformer::View::groundImage()
{
return m_ground;
}
platformer::Image&
platformer::View::caveImage()
{
return m_cave;
}
platformer::Image&
platformer::View::groundTopImage()
{
return m_topGround;
}
platformer::PlayerSprite::PlayerSprite( const ObjectData& data )
: m_image()
, m_data( data )
{
m_image.push_back( Image::Ref( new Image( "/home/tacsko/l0.png" ) ) );
m_image.push_back( Image::Ref( new Image( "/home/tacsko/l1.png" ) ) );
m_image.push_back( Image::Ref( new Image( "/home/tacsko/l2.png" ) ) );
m_image.push_back( Image::Ref( new Image( "/home/tacsko/r0.png" ) ) );
m_image.push_back( Image::Ref( new Image( "/home/tacsko/r1.png" ) ) );
m_image.push_back( Image::Ref( new Image( "/home/tacsko/r2.png" ) ) );
}
void
platformer::PlayerSprite::draw( unsigned int x, unsigned int y )
{
size_t start( m_data.velocity.x > 0 ? 3 : 0 );
start += ( m_i / 3 ) % 3;
m_image[ start ]->draw( x, y );
}
void
platformer::PlayerSprite::step()
{
++m_i;
}
platformer::GraphicalTile::GraphicalTile( View& graphics, const TileCoordinate& coordinate, Image& image, const Layer layer )
: Tile( coordinate, layer )
{
registerUpdater( new TileGraphics( m_data, image, graphics ) );
}
platformer::TileGraphics::TileGraphics( ObjectData& objectData, Image& image, View& graphics )
: Updater()
, m_image( image )
, m_graphics( graphics )
, m_topLeftCorner( objectData.coordinate + Coordinate{ 0.0, TILEHEIGHT } )
, m_layer( objectData.layer )
{
}
void
platformer::TileGraphics::update()
{
m_graphics.draw( m_topLeftCorner, m_image, m_layer );
}
platformer::PlayerGraphics::PlayerGraphics( ObjectData& objectData, View& graphics )
: Updater()
, m_data( objectData )
, m_graphics( graphics )
, m_player( objectData )
, m_topLeftDelta{ 0, m_data.size.y }
{
}
void
platformer::PlayerGraphics::step()
{
m_player.step();
}
void
platformer::PlayerGraphics::update()
{
m_graphics.draw( m_data.coordinate + m_topLeftDelta, m_player );
}
platformer::GraphicalObjectFactory::GraphicalObjectFactory( View& view )
: ObjectFactory()
, m_view( view )
{
}
platformer::Object::Ref
platformer::GraphicalObjectFactory::buildTopTile( const TileCoordinate& coordinate )
{
return Object::Ref( new GraphicalTile( m_view, coordinate, m_view.groundTopImage() ) );
}
platformer::Object::Ref
platformer::GraphicalObjectFactory::buildGroundTile( const TileCoordinate& coordinate )
{
return Object::Ref( new GraphicalTile( m_view, coordinate, m_view.groundImage() ) );
}
platformer::Object::Ref
platformer::GraphicalObjectFactory::buildCaveTile( const TileCoordinate& coordinate )
{
return Object::Ref( new GraphicalTile( m_view, coordinate, m_view.caveImage(), -1 ) );
}
#pragma once
#include <memory>
#include <map>
#include <vector>
#include "BaseTypes.hpp"
#include "Object.hpp"
#include "ObjectFactory.hpp"
class ALLEGRO_BITMAP;
class ALLEGRO_DISPLAY;
class ALLEGRO_FONT;
namespace platformer
{
class Aabb;
class Drawable
{
public:
virtual void draw( unsigned int x, unsigned int y ) = 0;
};
class Image : public Drawable
{
public:
typedef std::unique_ptr< Image > Ref;
Image( const char* fileName );
~Image();
void draw( unsigned int x, unsigned int y );
unsigned int height() const;
unsigned int width() const;
private:
ALLEGRO_BITMAP* m_image;
};
class Graphics
{
public:
Graphics( size_t x, size_t y );
~Graphics();
void printText( int x, int y, const char* text );
void printfps();
void printCoordinate( const Coordinate& coordinate );
void clear();
void show();
private:
ALLEGRO_DISPLAY *m_display;
size_t m_width;
size_t m_height;
ALLEGRO_FONT* m_font;
double m_previousRefreshTime;
};
struct DrawOperation
{
Drawable& drawable;
int x;
int y;
};
class View
{
public:
View( Graphics& graphics, const Coordinate& border, Drawable& backGround );
void clear();
void draw( const Coordinate& coordinate, Drawable& stuff, Layer layer = DEFAULT_LAYER );
void focus( const Coordinate& focus );
void show();
Aabb visibleArea() const;
Image& groundImage();
Image& groundTopImage();
Image& caveImage();
private:
Coordinate translate( const Coordinate& coordinate );
Graphics& m_graphics;
Coordinate m_borders;
Coordinate m_focus;
Drawable& m_bg;
Image m_ground;
Image m_cave;
Image m_topGround;
typedef std::map< Layer, std::vector< DrawOperation > > DrawOperationContainer;
DrawOperationContainer m_drawOperations;
};
class PlayerSprite : public Drawable
{
public:
PlayerSprite( const ObjectData& data );
void draw( unsigned int x, unsigned int y );
void step();
private:
unsigned int m_i{ 0 };
std::vector< Image::Ref > m_image;
const ObjectData& m_data;
};
class GraphicalTile : public Tile
{
public:
GraphicalTile( View& graphics, const TileCoordinate& coordinate, Image& image, const Layer layer = DEFAULT_LAYER );
};
class TileGraphics : public Updater
{
public:
TileGraphics( ObjectData& objectData, Image& image, View& graphics );
virtual void update();
private:
Image& m_image;
View& m_graphics;
const Coordinate m_topLeftCorner;
const Layer m_layer;
};
class PlayerGraphics : public Updater
{
public:
PlayerGraphics( ObjectData& objectData, View& graphics );
void step();
virtual void update();
private:
ObjectData& m_data;
View& m_graphics;
PlayerSprite m_player;
const Coordinate m_topLeftDelta;
};
class GraphicalObjectFactory : public ObjectFactory
{
public:
GraphicalObjectFactory( View& );
virtual Object::Ref buildTopTile( const TileCoordinate& );
virtual Object::Ref buildGroundTile( const TileCoordinate& );
virtual Object::Ref buildCaveTile( const TileCoordinate& );
private:
View& m_view;
};
}
SRC=BaseTypes.cpp Object.cpp Aabb.cpp Updaters.cpp World.cpp PerlinNoise.cpp WorldGenerator.cpp
TEST_SRC=$(SRC) test_platformer.cpp
DEMO_SRC=$(SRC) demo.cpp Graphics.cpp
CFLAGS=-std=c++11 -Wall -pedantic -Werror -Wextra -std=c++11
default: demo
demo: $(DEMO_SRC)
g++ $(CFLAGS) $(DEMO_SRC) -o demo -l allegro -l allegro_primitives -l allegro_image -l allegro_font -O3 -pg
test: testFile runTest
testFile: $(TEST_SRC)
g++ $(CFLAGS) $(TEST_SRC) -ggdb -o testFile
runTest: testFile
valgrind ./testFile
clean:
rm -rf *.o demo test a.out
.PHONY: clean
#include "Object.hpp"
#include <algorithm>
platformer::ObjectData::ObjectData(
const Coordinate& coordinate,
const Velocity& velocity,
const Coordinate& size )
: coordinate( coordinate )
, velocity( velocity )
, size( size )
, layer( DEFAULT_LAYER )
{
}
platformer::ObjectData::ObjectData(
const Coordinate& coordinate,
const Velocity& velocity,
const Coordinate& size,
const Layer layer )
: coordinate( coordinate )
, velocity( velocity )
, size( size )
, layer( layer )
{
}
platformer::Object::Object( const ObjectData& data )
: m_data( data )
{
}
namespace
{
platformer::Aabb
objectAabb( const platformer::ObjectData& objectData )
{
return platformer::Aabb( objectData.coordinate, objectData.coordinate + objectData.size );
}
}
bool
platformer::Object::intersectsWith( const Aabb& aabb ) const
{
return objectAabb( m_data ).intersectsWith( aabb );
}
bool
platformer::Object::contains( const Coordinate& coordinate ) const
{
return objectAabb( m_data ).contains( coordinate );
}
bool
platformer::Object::isOnLayer( const Layer layer ) const
{
return m_data.layer == layer;
}
void
platformer::Object::registerUpdater( Updater* updater )
{
m_updaters.push_back( Updater::Ref( updater ) );
}
void
platformer::Object::update()
{
std::for_each( begin( m_updaters ), end( m_updaters ), std::mem_fn( &Updater::update ) );
}
platformer::Tile::Tile( const TileCoordinate& coordinate )
: Object(
{ Coordinate{ coordinate.x * TILEWIDTH * 1.0, coordinate.y * TILEHEIGHT * 1.0 },
Velocity{ 0.0, 0.0 },
Coordinate{ TILEWIDTH, TILEHEIGHT } } )
{
}
platformer::Tile::Tile( const TileCoordinate& coordinate, const Layer layer )
: Object(
{ Coordinate{ coordinate.x * TILEWIDTH * 1.0, coordinate.y * TILEHEIGHT * 1.0 },
Velocity{ 0.0, 0.0 },
Coordinate{ TILEWIDTH, TILEHEIGHT },
layer } )
{
}
#pragma once
#include <memory>
#include "BaseTypes.hpp"
#include "Aabb.hpp"
#include "Updater.hpp"
namespace platformer
{
typedef int Layer;
const Layer DEFAULT_LAYER = 0;
struct ObjectData
{
ObjectData( const Coordinate&, const Velocity&, const Coordinate& );
ObjectData( const Coordinate&, const Velocity&, const Coordinate&, const Layer );
Coordinate coordinate;
Velocity velocity;
Coordinate size;
Layer layer;
};
class Object
{
public:
typedef std::unique_ptr< Object > Ref;
Object( const ObjectData& );
virtual void update();
bool intersectsWith( const Aabb& aabb ) const;
bool contains( const Coordinate& coordinate ) const;
void registerUpdater( Updater* updater );
bool isOnLayer( const Layer layer ) const;
protected:
ObjectData m_data;
private:
Updater::Container m_updaters;
};
class Tile : public Object
{
public:
Tile( const TileCoordinate& );
Tile( const TileCoordinate&, const Layer );
};
}
#pragma once
#include <algorithm>
#include "Aabb.hpp"
#include "Object.hpp"
namespace platformer
{
class ObjectContainer
{
public:
ObjectContainer( const Aabb& aabb )
: m_aabb( aabb )
{
}
void insert( Object::Ref object )
{
if ( !object ||
!object->intersectsWith( m_aabb ) )
{
return;
}
m_objects.push_back( object.get() );
m_owningContainer.push_back( std::move( object ) );
}
template < class OutputIterator >
void collectIn( const Aabb& aabb, OutputIterator output ) const
{
if ( !aabb.intersectsWith( m_aabb ) )
{
return;
}
std::copy_if( begin( m_objects ), end( m_objects ),
output, IntersectsWith( aabb ) );
}
private:
struct IntersectsWith
{
IntersectsWith( const Aabb& aabb )
: m_aabb( aabb )
{
}
bool operator()( Object* object ) const
{
return object->intersectsWith( m_aabb );
}
Aabb m_aabb;
};
Aabb m_aabb;
std::vector< Object* > m_objects;
std::vector< Object::Ref > m_owningContainer;
};
}
#pragma once
#include "Object.hpp"
namespace platformer
{
class TileCoordinate;
class ObjectFactory
{
public:
virtual Object::Ref buildTopTile( const TileCoordinate& ) = 0;
virtual Object::Ref buildGroundTile( const TileCoordinate& ) = 0;
virtual Object::Ref buildCaveTile( const TileCoordinate& ) = 0;
};
}
#include "PerlinNoise.hpp"
#include <cmath>
#include <iostream>
namespace
{
double
pseudoNoise( int x )
{
x = (x << 13) ^ x;
return ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
}
double
pseudoNoise2d( int x, int y )
{
int n( x + y * 57 );
n = ( n <<13 ) ^ n;
int nn( (n*(n*n*60493+19990303)+1376312589)&0x7fffffff );
return 1.0 - ( (double) nn / 1073741824.0 );
}
double
smoothNoise( double x )
{
return pseudoNoise( x )*0.5 + ( pseudoNoise( x - 1 ) + pseudoNoise( x + 1 ) )*0.25;
}
double
cosineInterpolation( double a, double b, double x )
{
double ft( x * M_PI );
double f( (1 - cos( ft )) * 0.5 );
return a * (1 - f) + b * f;
}
double
interpolatedNoise( double x )
{
int intx( floor( x ) );
double FraqX( x - intx );
double v1( smoothNoise( intx ) );
double v2( smoothNoise( intx + 1 ) );
return cosineInterpolation( v1, v2, FraqX );
}
double
interpolatedNoise( double x, double y )
{
double floorx( floor( x ) );
double floory( floor( y ) );
double s ( pseudoNoise2d( floorx, floory ) );
double t ( pseudoNoise2d( floorx + 1, floory ) );
double u ( pseudoNoise2d( floorx, floory + 1 ) );
double v ( pseudoNoise2d( floorx + 1, floory + 1 ) );
double int1( cosineInterpolation( s, t, x-floorx ) );
double int2( cosineInterpolation( u, v, x-floorx ) );
return cosineInterpolation( int1, int2, y-floory );
}
}
platformer::PerlinNoise::PerlinNoise( double persistance, int octaveNumber )
: m_persistance( persistance )
, m_octaveNumber( octaveNumber )
{
}
double
platformer::PerlinNoise::noiseFor( double x ) const
{
double sum( 0.0 );
for( unsigned int i = 0; i < m_octaveNumber; ++i )
{
int frequency( 1<<i );
double amplitude( pow( m_persistance, i ) );
sum += interpolatedNoise( x * frequency ) * amplitude;
}
return sum;
}
double
platformer::PerlinNoise::noiseFor( double x, double y ) const
{
double sum( 0.0 );
for( unsigned int i = 0; i < m_octaveNumber; ++i )
{
int frequency( 1<<i );
double amplitude( pow( m_persistance, i ) );
sum += interpolatedNoise( x * frequency, y * frequency ) * amplitude;
}
return sum;
}
#pragma once
namespace platformer
{
class PerlinNoise
{
public:
PerlinNoise( double persistance, int octaveNumber );
double noiseFor( double x ) const;
double noiseFor( double x, double y ) const;
private:
double InterpolatedNoise( double x ) const;
double m_persistance;
unsigned int m_octaveNumber;
};
}
#pragma once
#include "Aabb.hpp"
Describe( axis_aligned_bounding_box )
{
It ( should_intersect_with_the_same_aabb )
{
platformer::Aabb sameAabb( platformer::TileCoordinate{ 0,0 }, platformer::TileCoordinate{ 10, 11 } );
AssertThat( aabb.intersectsWith( sameAabb ),
Equals( true ) );
AssertThat( sameAabb.intersectsWith( aabb ),
Equals( true ) );
}
It ( should_intersect_with_aabb_with_one_common_corner )
{
platformer::Aabb bottomLeft( platformer::TileCoordinate{ -10, -11 }, platformer::TileCoordinate{ 0, 0 } );
AssertThat( aabb.intersectsWith( bottomLeft ),
Equals( true ) );
AssertThat( bottomLeft.intersectsWith( aabb ),
Equals( true ) );
platformer::Aabb topLeft( platformer::TileCoordinate{ -10, 11 }, platformer::TileCoordinate{ 0, 21 } );
AssertThat( aabb.intersectsWith( topLeft ),
Equals( true ) );
AssertThat( topLeft.intersectsWith( aabb ),
Equals( true ) );
}
It ( should_not_intersect_with_not_intersecting_aabbs )
{
platformer::Aabb neighbour( platformer::TileCoordinate{ 11,10 }, platformer::TileCoordinate{ 12, 11 } );
AssertThat( aabb.intersectsWith( neighbour ),
Equals( false ) );
AssertThat( neighbour.intersectsWith( aabb ),
Equals( false ) );
}
It ( should_be_able_to_detect_if_it_contains_a_coordinate )
{
AssertThat( aabb.contains( platformer::Coordinate{ 0.0, 0.0 } ), Equals( true ) );
AssertThat( aabb.contains( platformer::Coordinate{ 352.0, 0.0 } ), Equals( true ) );
AssertThat( aabb.contains( platformer::Coordinate{ -0.1, 0.0 } ), Equals( false ) );
AssertThat( aabb.contains( platformer::Coordinate{ 352.1, 0.0 } ), Equals( false ) );
AssertThat( aabb.contains( platformer::Coordinate{ 0.0, 384.0 } ), Equals( true ) );
AssertThat( aabb.contains( platformer::Coordinate{ 0.0, 384.1 } ), Equals( false ) );
AssertThat( aabb.contains( platformer::Coordinate{ 0.0, -0.1 } ), Equals( false ) );
}
platformer::Aabb aabb{ platformer::TileCoordinate{ 0, 0 }, platformer::TileCoordinate{ 10, 11 } };
};
#pragma once
#include "BaseTypes.hpp"
Describe( tile_coordinates )
{
It( should_be_comparable )
{
platformer::TileCoordinate location{ 0, 0 };
AssertThat( location, Equals( platformer::TileCoordinate{ 0, 0 } ) );
AssertThat( location, Is().Not().EqualTo( platformer::TileCoordinate{ 1, 0 } ) );
}
It( should_have_operator_plusassignment )
{
platformer::TileCoordinate first{ 0, 0 };
first += { 1, 1 };
AssertThat( first, Equals( platformer::TileCoordinate{ 1, 1 } ) );
}
};
Describe( coordinates )
{
It( should_be_convertable_to_tile_coordinates )
{
AssertThat(
tileCoordinateOf( platformer::Coordinate{ platformer::TILEWIDTH, 0.0 } ),
Equals( platformer::TileCoordinate{ 1, 0 } ) );
AssertThat(
tileCoordinateOf( platformer::Coordinate{ platformer::TILEWIDTH, platformer::TILEHEIGHT } ),
Equals( platformer::TileCoordinate{ 1, 1 } ) );
}
It( should_have_operator_plusassignment )
{
platformer::Coordinate first{ 0.0, 0.0 };
first += { 1.0, 1.0 };
AssertThat( first.x, EqualsWithDelta( 1.0, 0.00001 ) );
AssertThat( first.y, EqualsWithDelta( 1.0, 0.00001 ) );
}
};
#pragma once
#include "Object.hpp"
#include "DummyObject.hpp"
Describe( object )
{
class DummyUpdater : public platformer::Updater
{
public:
DummyUpdater( platformer::test::DummyObject& object )
: m_object( object )
{
}
virtual void update()
{
++m_object.updateTimes;
}
private:
platformer::test::DummyObject& m_object;
};
It ( should_call_each_updater_if_the_object_is_asked_to_update_itself )
{
std::unique_ptr< platformer::test::DummyObject > dummy( new platformer::test::DummyObject );
dummy->registerUpdater( new DummyUpdater( *dummy.get() ) );
dummy->registerUpdater( new DummyUpdater( *dummy.get() ) );
dummy->update();
AssertThat( dummy->updateTimes, Equals( 2 ) );
}
It ( should_detect_intersecting_aabb )
{
platformer::test::DummyObject dummy;
AssertThat( dummy.intersectsWith( { platformer::TileCoordinate{ 0, 0 }, platformer::TileCoordinate{ 0, 0 } } ), Equals( true ) );
AssertThat( dummy.intersectsWith( { platformer::TileCoordinate{ 0, 1 }, platformer::TileCoordinate{ 0, 1 } } ), Equals( true ) );
AssertThat( dummy.intersectsWith( { platformer::TileCoordinate{ 1, 0 }, platformer::TileCoordinate{ 1, 0 } } ), Equals( true ) );
AssertThat( dummy.intersectsWith( { platformer::TileCoordinate{ 1, 1 }, platformer::TileCoordinate{ 1, 1 } } ), Equals( true ) );
AssertThat( dummy.intersectsWith( { platformer::TileCoordinate{ -1, -1 }, platformer::TileCoordinate{ -1, -1 } } ), Equals( false ) );
}
};
#include <igloo/igloo_alt.h>
#include "ObjectContainer.hpp"
#include "Aabb.hpp"
Describe( object_container )
{
typedef platformer::Aabb AabbType;
typedef platformer::Tile ObjectType;
typedef std::vector< platformer::Object* > ObjectVector;
void SetUp()
{
objects.reset( new platformer::ObjectContainer( aabb ) );
}
It( should_be_able_to_store_and_find_objects_in_subaabbs )
{
ObjectType* object( new ObjectType( { 10, 10 } ) );
ObjectType* object2( new ObjectType( { 19, 19 } ) );
objects->insert( std::unique_ptr<platformer::Object>( object ) );
objects->insert( std::unique_ptr<platformer::Object>( object2 ) );
ObjectVector collectedObjects;
objects->collectIn( AabbType{ platformer::TileCoordinate{ 5, 5 }, platformer::TileCoordinate{ 5, 5 } },
std::back_inserter< ObjectVector >( collectedObjects ) );
AssertThat( collectedObjects, IsEmpty() );
objects->collectIn( AabbType{ platformer::TileCoordinate{ 10, 10 }, platformer::TileCoordinate{ 19, 19 } },
std::back_inserter< ObjectVector >( collectedObjects) );
AssertThat( collectedObjects, HasLength( 2 ) );
AssertThat( collectedObjects, Contains( object ) );
AssertThat( collectedObjects, Contains( object2 ) );
}
It( should_not_store_objects_outside_the_aabb_of_the_objects )
{
ObjectType* object( new ObjectType( { 101, 101 } ) );
objects->insert( std::unique_ptr<platformer::Object>( object ) );
ObjectVector collectedObjects;
objects->collectIn( AabbType{ platformer::TileCoordinate{ 0, 0 }, platformer::TileCoordinate{ 110, 110 } },
std::back_inserter< ObjectVector >( collectedObjects) );
AssertThat( collectedObjects, Is().Not().Containing( object ) );
}
It( should_handle_nullptr )
{
objects->insert( std::unique_ptr<platformer::Object>( nullptr ) );
}
AabbType aabb{ platformer::TileCoordinate{ 0, 0 }, platformer::TileCoordinate{ 100, 100 } };
std::unique_ptr< platformer::ObjectContainer > objects;
};
#include <igloo/igloo_alt.h>
using namespace igloo;
#include "test_world_generator.hpp"
#include "test_coordinate.hpp"
#include "test_object.hpp"
#include "test_tile.hpp"
#include "test_aabb.hpp"
#include "test_object_container.hpp"
#include "test_updaters.hpp"
int main()
{
return TestRunner::RunAllTests();
}
#pragma once
#include "Object.hpp"
Describe( tile )
{
It ( should_detect_intersecting_aabb )
{
platformer::Tile tile( { 0, 0 } );
AssertThat( tile.intersectsWith( { platformer::TileCoordinate{ 0, 0 }, platformer::TileCoordinate{ 0, 0 } } ), Equals( true ) );
AssertThat( tile.intersectsWith( { platformer::TileCoordinate{ 0, 1 }, platformer::TileCoordinate{ 0, 1 } } ), Equals( false ) );
AssertThat( tile.intersectsWith( { platformer::TileCoordinate{ 0, -1 }, platformer::TileCoordinate{ 0, -1 } } ), Equals( false ) );
AssertThat( tile.intersectsWith( { platformer::TileCoordinate{ 1, 0 }, platformer::TileCoordinate{ 1, 0 } } ), Equals( false ) );
AssertThat( tile.intersectsWith( { platformer::TileCoordinate{ -1, 0 }, platformer::TileCoordinate{ -1, 0 } } ), Equals( false ) );
}
};
#pragma once
#include "Object.hpp"
#include "DummyObject.hpp"
#include "Updaters.hpp"
#include "ObjectContainer.hpp"
Describe( speed_limiter )
{
It ( should_not_allow_to_increase_the_object_speed_over_a_defined_limit )
{
platformer::ObjectData objectDataWithPositiveVelocity{ { 0, 0 }, { 10, 10 }, { 10, 10 } };
platformer::SpeedLimiter limiter( objectDataWithPositiveVelocity, { 5, 5 } );
limiter.update();
AssertThat( objectDataWithPositiveVelocity.velocity.x, EqualsWithDelta( 5, 0.00001 ) );
AssertThat( objectDataWithPositiveVelocity.velocity.y, EqualsWithDelta( 5, 0.00001 ) );
}
It ( should_not_allow_to_increase_the_abs_of_object_speed_over_a_defined_limit )
{
platformer::ObjectData objectDataWithPositiveVelocity{ { 0, 0 }, { -10, -10 }, { 10, 10 } };
platformer::SpeedLimiter limiter( objectDataWithPositiveVelocity, { 5, 5 } );
limiter.update();
AssertThat( objectDataWithPositiveVelocity.velocity.x, EqualsWithDelta( -5, 0.00001 ) );
AssertThat( objectDataWithPositiveVelocity.velocity.y, EqualsWithDelta( -5, 0.00001 ) );
}
};
Describe( speed_addition )
{
It ( should_add_defined_speed_amount_in_each_update_iteration )
{
platformer::ObjectData objectData{ { 0, 0 }, { -10, -10 }, { 10, 10 } };
platformer::SpeedAddition speedAddition( objectData, { 5, 5 } );
speedAddition.update();
AssertThat( objectData.velocity.x, EqualsWithDelta( -5, 0.00001 ) );
AssertThat( objectData.velocity.y, EqualsWithDelta( -5, 0.00001 ) );
}
};
Describe( mover_collider )
{
void SetUp()
{
objects.reset( new platformer::ObjectContainer(
{ platformer::TileCoordinate{ 0, 0 },
platformer::TileCoordinate{ 100, 100 } } ) );
}
It ( should_collide_with_tiles_from_all_directions )
{
objects->insert( platformer::Object::Ref(
new platformer::Tile( { 0, 0 } ) ) );
std::vector< platformer::ObjectData > objectDatas = {
//bottom colliders
{ { 5, platformer::TILEHEIGHT * 1.5 }, { 0, -platformer::TILEHEIGHT }, { 5, 5 } },
{ { -8, platformer::TILEHEIGHT * 1.5 }, { 0, -platformer::TILEHEIGHT }, { 10, 10 } },
{ { platformer::TILEWIDTH - 2, platformer::TILEHEIGHT * 1.5 }, { 0, -platformer::TILEHEIGHT }, { 10, 10 } },
//right colliders
{ { -15, 5 }, { platformer::TILEWIDTH, 0 }, { 10, 10 } },
{ { -15, platformer::TILEHEIGHT - 3 }, { platformer::TILEWIDTH, 0 }, { 10, 10 } },
{ { -15, -7 }, { platformer::TILEWIDTH, 0 }, { 10, 10 } },
//left colliders
{ { platformer::TILEWIDTH + 15, 5 }, { -platformer::TILEWIDTH, 0 }, { 20, 10 } },
{ { platformer::TILEWIDTH + 15, platformer::TILEHEIGHT - 3 }, { -platformer::TILEWIDTH, 0 }, { 20, 10 } },
{ { platformer::TILEWIDTH + 15, -7 }, { -platformer::TILEWIDTH, 0 }, { 20, 10 } },
//top colliders
{ { 5, -platformer::TILEHEIGHT - 2 }, { 0, platformer::TILEHEIGHT }, { 10, 10 } },
{ { -8, -platformer::TILEHEIGHT - 2 }, { 0, platformer::TILEHEIGHT }, { 10, 10 } },
{ { platformer::TILEWIDTH - 2, -platformer::TILEHEIGHT - 2 }, { 0, platformer::TILEHEIGHT }, { 10, 10 } },
};
std::for_each( begin( objectDatas ), end( objectDatas ),
[ this ]( platformer::ObjectData& data )
{
platformer::ObjectData old( data );
platformer::MoveAndCollide collider( nullptr, data, *objects );
collider.update();
AssertThat( data.coordinate.y, EqualsWithDelta( old.coordinate.y, 0.00001 ) );
AssertThat( data.coordinate.x, EqualsWithDelta( old.coordinate.x, 0.00001 ) );
AssertThat( data.velocity.y, EqualsWithDelta( 0, 0.00001 ) );
AssertThat( data.velocity.x, EqualsWithDelta( 0, 0.00001 ) );
} );
}
It ( should_be_able_to_jump_only_if_it_stands_on_ground )
{
platformer::ObjectData objectData{ { 5, platformer::TILEHEIGHT }, { 0, 0 }, { 5, 5 } };
platformer::MoveAndCollide mover( nullptr, objectData, *objects );
mover.update();
mover.jump();
AssertThat( objectData.velocity.y, EqualsWithDelta( 0, 0.00001 ) );
objects->insert( platformer::Object::Ref(
new platformer::Tile( { 0, 0 } ) ) );
mover.update();
mover.jump();
AssertThat( objectData.velocity.y, IsGreaterThan( platformer::MoveAndCollide::Acceleration ) );
}
It ( should_take_care_of_drag_on_ground_if_not_moving )
{
objects->insert( platformer::Object::Ref(
new platformer::Tile( { 0, 0 } ) ) );
platformer::ObjectData objectData{ { 0, platformer::TILEHEIGHT }, { 0, 0 }, { 5, 5 } };
platformer::MoveAndCollide mover( nullptr, objectData, *objects );
mover.update();
mover.moveRight();
mover.update();
AssertThat( objectData.velocity.x, EqualsWithDelta( platformer::MoveAndCollide::Acceleration, 0.00001 ) );
mover.update();
AssertThat( objectData.velocity.x, IsLessThan( platformer::MoveAndCollide::Acceleration / 2 ) );
}
It ( should_move_objects_with_its_velocity_if_no_collision_happens )
{
platformer::ObjectData objectData{ { 5, 5 }, { 2, 3 }, { 5, 5 } };
platformer::MoveAndCollide collider( nullptr, objectData, *objects );
collider.update();
AssertThat( objectData.coordinate.x, EqualsWithDelta( 5 + 2, 0.00001 ) );
AssertThat( objectData.coordinate.y, EqualsWithDelta( 5 + 3, 0.00001 ) );
}
It ( should_modify_x_velocity_if_moved_left_or_right )
{
platformer::ObjectData objectData{ { 5, 5 }, { 2, 3 }, { 5, 5 } };
platformer::MoveAndCollide mover( nullptr, objectData, *objects );
mover.moveLeft();
AssertThat( objectData.velocity.x, EqualsWithDelta( 2 - platformer::MoveAndCollide::Acceleration, 0.00001 ) );
mover.moveRight();
AssertThat( objectData.velocity.x, EqualsWithDelta( 2, 0.00001 ) );
}
It ( should_not_check_collision_with_owning_object )
{
platformer::Coordinate startPoint{ 0.0, 0.0 };
platformer::Coordinate velocity{ 0.3, 0.4 };
platformer::ObjectData objectData{ startPoint, velocity, { 5, 5 } };
platformer::test::DummyObject* object( new platformer::test::DummyObject( objectData ) );
objects->insert( platformer::Object::Ref( object ) );
platformer::MoveAndCollide mover( object, objectData, *objects );
mover.update();
AssertThat( objectData.coordinate.x, EqualsWithDelta( startPoint.x + velocity.x, 0.00001 ) );
AssertThat( objectData.coordinate.y, EqualsWithDelta( startPoint.y + velocity.y, 0.00001 ) );
}
It ( should_not_collide_with_objects_on_other_layers )
{
platformer::Coordinate startPoint{ 0.0, 0.0 };
platformer::Coordinate velocity{ 1.0, 0.0 };
platformer::ObjectData objectData{ startPoint, velocity, { 5, 5 } };
platformer::test::DummyObject* object( new platformer::test::DummyObject( objectData ) );
objects->insert( platformer::Object::Ref( object ) );
platformer::MoveAndCollide mover( object, objectData, *objects );
const platformer::Layer OTHER_LAYER( 1 );
objects->insert( platformer::Object::Ref( new platformer::test::DummyObject(
platformer::ObjectData{
{ 5.5, 0.0 },
{ 0.0, 0.0 },
{ 10.0, 10.0 },
OTHER_LAYER
}
)) );
mover.update();
AssertThat( objectData.coordinate.x, EqualsWithDelta( startPoint.x + velocity.x, 0.00001 ) );
AssertThat( objectData.coordinate.y, EqualsWithDelta( startPoint.y + velocity.y, 0.00001 ) );
}
std::unique_ptr< platformer::ObjectContainer > objects;
};
#pragma once
#include "ObjectFactory.hpp"
#include "BaseTypes.hpp"
#include "Aabb.hpp"
#include "WorldGenerator.hpp"
#include "ObjectContainer.hpp"
namespace
{
class FactoryDouble : public platformer::ObjectFactory
{
public:
bool tileWasBuilt{ false };
virtual platformer::Object::Ref buildTopTile( const platformer::TileCoordinate& coordinate )
{
tileWasBuilt = true;
return someObject( coordinate );
}
virtual platformer::Object::Ref buildGroundTile( const platformer::TileCoordinate& coordinate )
{
tileWasBuilt = true;
return someObject( coordinate );
}
virtual platformer::Object::Ref buildCaveTile( const platformer::TileCoordinate& coordinate )
{
tileWasBuilt = true;
return someObject( coordinate );
}
void reset()
{
tileWasBuilt = false;
}
private:
platformer::Object::Ref someObject( const platformer::TileCoordinate& coordinate ) const
{
return platformer::Object::Ref(
new platformer::Tile( coordinate ) );
}
};
}
Describe( world_generator )
{
It ( should_generate_necessary_chunks_for_simple_coordinate_aabb_only_once )
{
platformer::Aabb aabb(
platformer::Coordinate{ 100.0, 100.0 },
platformer::Coordinate{ 200.0, 200.0 } );
platformer::ObjectContainer objects( aabb );
FactoryDouble factory;
platformer::WorldGenerator worldGenerator( objects, factory );
worldGenerator.generate( aabb );
AssertThat( factory.tileWasBuilt, Equals( true ) );
std::vector< platformer::Object* > objectsInAabb;
objects.collectIn( aabb, std::back_inserter( objectsInAabb ) );
AssertThat( objectsInAabb, Is().Not().Empty() );
factory.reset();
worldGenerator.generate( aabb );
AssertThat( factory.tileWasBuilt, Equals( false ) );
}
};
1. clean up duplicates in test_updaters.hpp
#pragma once
#include <vector>
#include <memory>
namespace platformer
{
class Updater
{
public:
typedef std::unique_ptr< Updater > Ref;
typedef std::vector< Ref > Container;
virtual void update() = 0;
};
}
#include "Updaters.hpp"
#include "BaseTypes.hpp"
#include "Object.hpp"
#include <cstdlib>
namespace platformer
{
double MoveAndCollide::Acceleration = 1;
}
platformer::SpeedLimiter::SpeedLimiter( ObjectData& objectData, const Coordinate& speedLimit )
: Updater()
, m_speedLimit( speedLimit )
, m_objectData( objectData )
{
}
namespace
{
double absMax( const double& value, const double& limit )
{
return value > 0 ?
std::min( value, limit ) :
std::max( value, limit * -1.0 );
}
}
void
platformer::SpeedLimiter::update()
{
m_objectData.velocity.x = absMax( m_objectData.velocity.x, m_speedLimit.x );
m_objectData.velocity.y = absMax( m_objectData.velocity.y, m_speedLimit.y );
}
platformer::SpeedAddition::SpeedAddition( ObjectData& objectData, const Coordinate& speedDelta )
: Updater()
, m_speedDelta( speedDelta )
, m_objectData( objectData )
{
}
void
platformer::SpeedAddition::update()
{
m_objectData.velocity+=m_speedDelta;
}
platformer::MoveAndCollide::MoveAndCollide( Object* owner, ObjectData& objectData, const ObjectContainer& objects )
: Updater()
, m_ownerObject( owner )
, m_objects( objects )
, m_objectData( objectData )
, m_isOnGround( false )
, m_isInMove( false )
{
}
namespace
{
platformer::Aabb
collisionAreaFromObject( const platformer::ObjectData& data,
const platformer::Coordinate& coordinate )
{
const platformer::Coordinate bottomLeft(
coordinate + platformer::Coordinate{ -platformer::TILEWIDTH / 2, -platformer::TILEHEIGHT / 2 } );
const platformer::Coordinate topRight(
coordinate + data.size + platformer::Coordinate{ platformer::TILEWIDTH / 2, platformer::TILEHEIGHT / 2 } );
return platformer::Aabb( bottomLeft, topRight );
}
}
typedef std::vector<platformer::Coordinate> CoordinateContainer;
namespace
{
struct ObjectCollider
{
ObjectCollider( const platformer::Object* object )
: m_object( object )
{
}
bool operator()( const platformer::Coordinate& coordinate )
{
return m_object->contains( coordinate );
}
const platformer::Object* m_object;
};
}
void
platformer::MoveAndCollide::update()
{
Coordinate newCoordinate( m_objectData.coordinate + m_objectData.velocity );
std::vector< Object* > objectsInCollisionArea;
m_objects.collectIn(
collisionAreaFromObject( m_objectData, newCoordinate ),
std::back_inserter( objectsInCollisionArea ) );
CoordinateContainer bottomColliders{
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 1/2, 0 },
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 2/10, 0 },
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 8/10, 0 },
};
CoordinateContainer topColliders{
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 1/2, m_objectData.size.y * 9/10 },
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 1/5, m_objectData.size.y * 9/10 },
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 4/5, m_objectData.size.y * 9/10 },
};
CoordinateContainer leftColliders{
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 1/10, 2 },
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 1/10, m_objectData.size.y * 1/2 },
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 1/10, m_objectData.size.y * 1/4 },
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 1/10, m_objectData.size.y * 3/4 },
};
CoordinateContainer rightColliders{
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 9/10, 2 },
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 9/10, m_objectData.size.y * 1/2 },
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 9/10, m_objectData.size.y * 1/4 },
newCoordinate + platformer::Coordinate{ m_objectData.size.x * 9/10, m_objectData.size.y * 3/4 },
};
m_isOnGround = false;
std::for_each( begin( objectsInCollisionArea ), end( objectsInCollisionArea ),
[ this, &newCoordinate, &bottomColliders, &topColliders, &leftColliders, &rightColliders ] ( Object* object )
{
if ( object == m_ownerObject )
{
return;
}
if ( !object->isOnLayer( m_objectData.layer ) )
{
return;
}
ObjectCollider collidesWithObject( object );
if ( std::any_of( begin( bottomColliders ), end( bottomColliders ), collidesWithObject ) )
{
m_objectData.velocity.y = 0;
newCoordinate.y = m_objectData.coordinate.y;
m_isOnGround = true;
}
if ( std::any_of( begin( topColliders ), end( topColliders ), collidesWithObject ) )
{
m_objectData.velocity.y = 0;
newCoordinate.y = m_objectData.coordinate.y;
}
const bool movingLeftAndLeftSideCollides(
m_objectData.velocity.x < 0 &&
std::any_of( begin( leftColliders ), end( leftColliders ), collidesWithObject ) );
const bool movingRightAndRightSideCollides(
m_objectData.velocity.x > 0 &&
std::any_of( begin( rightColliders ), end( rightColliders ), collidesWithObject ) );
if ( movingLeftAndLeftSideCollides ||
movingRightAndRightSideCollides )
{
m_objectData.velocity.x = 0;
newCoordinate.x = m_objectData.coordinate.x;
}
} );
applyGroundDrag();
m_objectData.coordinate = newCoordinate;
m_isInMove = false;
}
void
platformer::MoveAndCollide::applyGroundDrag()
{
if ( m_isOnGround && !m_isInMove )
{
const double GroundDrag( 0.1 );
m_objectData.velocity.x *= GroundDrag;
}
}
void
platformer::MoveAndCollide::moveLeft()
{
m_isInMove = true;
m_objectData.velocity.x -= Acceleration;
}
void
platformer::MoveAndCollide::moveRight()
{
m_isInMove = true;
m_objectData.velocity.x += Acceleration;
}
void
platformer::MoveAndCollide::jump()
{
if ( !m_isOnGround )
{
return;
}
const double JumpMultiplier( 8 );
m_objectData.velocity.y+=Acceleration * JumpMultiplier;
}
#pragma once
#include "BaseTypes.hpp"
#include "Updater.hpp"
#include "ObjectContainer.hpp"
namespace platformer
{
class ObjectData;
class SpeedLimiter : public Updater
{
public:
SpeedLimiter( ObjectData& objectData, const Coordinate& speedLimit );
virtual void update();
private:
Coordinate m_speedLimit;
ObjectData& m_objectData;
};
class SpeedAddition : public Updater
{
public:
SpeedAddition( ObjectData& objectData, const Coordinate& speedDelta );
virtual void update();
private:
Coordinate m_speedDelta;
ObjectData& m_objectData;
};
class MoveAndCollide : public Updater
{
public:
static double Acceleration;
MoveAndCollide( Object* ownerObject, ObjectData& objectData, const ObjectContainer& objects );
virtual void update();
void moveLeft();
void moveRight();
void jump();
private:
void applyGroundDrag();
Object* m_ownerObject;
const ObjectContainer& m_objects;
ObjectData& m_objectData;
bool m_isOnGround;
bool m_isInMove;
};
}
#include "World.hpp"
#pragma once
#include "WorldGenerator.hpp"
#include "BaseTypes.hpp"
#include "Aabb.hpp"
#include "ObjectContainer.hpp"
#include "ObjectFactory.hpp"
namespace
{
const int TILENUM( 5 );
const int CHUNKWIDTH( platformer::TILEWIDTH * TILENUM );
const int CHUNKHEIGHT( platformer::TILEHEIGHT * TILENUM );
}
platformer::WorldGenerator::WorldGenerator( ObjectContainer& tiles, ObjectFactory& factory )
: m_tiles( tiles )
, m_factory( factory )
, m_generatedChunks()
{
}
void
platformer::WorldGenerator::generate( const Aabb& aabb )
{
ChunkCoordinate bottomLeft{
static_cast<int>( aabb.bottomLeft().x / CHUNKWIDTH ),
static_cast<int>( aabb.bottomLeft().y / CHUNKHEIGHT ) };
ChunkCoordinate topRight{
static_cast<int>( aabb.topRight().x / CHUNKWIDTH ),
static_cast<int>( aabb.topRight().y / CHUNKHEIGHT ) };
for ( int x( bottomLeft.x ); x <= topRight.x; ++x )
{
for ( int y( bottomLeft.y ); y <= topRight.y; ++y )
{
generateTilesInChunk( x, y );
}
}
}
namespace
{
template <class Iterator, typename Value >
bool contains( Iterator begin, Iterator end, const Value& value )
{
return std::any_of( begin, end,
[ &value ]( const Value& val )
{
return value == val;
} );
}
}
void
platformer::WorldGenerator::generateTilesInChunk( int chunkx, int chunky )
{
ChunkCoordinate chunkCoordinate{ chunkx, chunky };
if ( contains( begin( m_generatedChunks ), end( m_generatedChunks ), chunkCoordinate ) )
{
return;
}
for ( int tx( chunkx * TILENUM ); tx < ( chunkx + 1 ) * TILENUM; ++tx )
{
for ( int ty( chunky * TILENUM ); ty < ( chunky + 1 ) * TILENUM; ++ty )
{
m_tiles.insert( m_factory.buildGroundTile( { tx, ty } ) );
}
}
m_generatedChunks.push_back( chunkCoordinate );
}
#pragma once
#include "BaseTypes.hpp"
#include <vector>
namespace platformer
{
class Aabb;
class ObjectContainer;
class ObjectFactory;
class WorldGenerator
{
public:
typedef TileCoordinate ChunkCoordinate;
WorldGenerator( ObjectContainer& tiles, ObjectFactory& factory );
void generate( const Aabb& aabb );
private:
void generateTilesInChunk( int x, int y );
ObjectContainer& m_tiles;
ObjectFactory& m_factory;
std::vector< ChunkCoordinate > m_generatedChunks;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment