From: Daniel Karbach Date: Mon, 7 May 2012 20:38:46 +0000 (+0000) Subject: added collision engine, more or less stole gameplay from sdl-test7 X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=8b4877fe48d21d7e789cf52f81c1d6a87b06bcbc;p=sdl-test8.git added collision engine, more or less stole gameplay from sdl-test7 --- diff --git a/data/font/Magra-Regular.ttf b/data/font/Magra-Regular.ttf new file mode 100644 index 0000000..6e252b0 Binary files /dev/null and b/data/font/Magra-Regular.ttf differ diff --git a/src/app/Application.cpp b/src/app/Application.cpp new file mode 100644 index 0000000..bcf4f29 --- /dev/null +++ b/src/app/Application.cpp @@ -0,0 +1,119 @@ +/* + * Application.cpp + * + * Created on: Apr 8, 2012 + * Author: holy + */ + +#include "Application.h" + +#include "State.h" + +#include + +namespace app { + +Application::Application(sdl::InitScreen *screen, State *initialState) +: screen(screen) +, states() +, timer() +, last(SDL_GetTicks()) { + assert(screen && "cannot create application without screen"); + assert(initialState && "cannot create application without initial state"); + RealPushState(initialState); +} + +Application::~Application(void) { + PopAllStates(); +} + + +State *Application::CurrentState(void) { + return states.top(); +} + +void Application::ChangeState(State *s) { + RealPopState(); + RealPushState(s); +} + +void Application::PushState(State *s) { + RealPushState(s); +} + +void Application::RealPushState(State *s) { + states.push(s); + s->EnterState(this, screen->Screen()); +} + +void Application::PopState(void) { + RealPopState(); +} + +void Application::RealPopState(void) { + if (states.empty()) return; + states.top()->ExitState(); + delete states.top(); + states.pop(); +} + +void Application::Quit(void) { + PopAllStates(); +} + +void Application::PopAllStates(void) { + while (!states.empty()) { + RealPopState(); + } +} + + +void Application::Run(void) { + while (CurrentState()) { + Loop(); + } +} + +void Application::Loop(void) { + Uint32 now(SDL_GetTicks()); + Uint32 deltaT(now - last); + if (deltaT > 34) deltaT = 34; + + HandleEvents(); + UpdateWorld(deltaT); + Render(); + + last = now; +} + + +void Application::HandleEvents(void) { + if (!CurrentState()) return; + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + PopAllStates(); + break; + default: + CurrentState()->HandleEvent(event); + break; + } + } +} + +void Application::UpdateWorld(Uint32 deltaT) { + if (!CurrentState()) return; + for (Uint32 i(0); i < deltaT; ++i) { + timer.Update(0.001); + CurrentState()->UpdateWorld(timer); + } +} + +void Application::Render(void) { + if (!CurrentState()) return; + CurrentState()->Render(screen->Screen()); + screen->Flip(); +} + +} diff --git a/src/app/Application.h b/src/app/Application.h new file mode 100644 index 0000000..73e6400 --- /dev/null +++ b/src/app/Application.h @@ -0,0 +1,64 @@ +/* + * Application.h + * + * Created on: Apr 8, 2012 + * Author: holy + */ + +#ifndef APP_APPLICATION_H_ +#define APP_APPLICATION_H_ + +#include "Control.h" +#include "Timer.h" +#include "../sdl/InitScreen.h" + +#include +#include + + +namespace app { + +class State; + +class Application +: public Control { + + public: + explicit Application(sdl::InitScreen *screen, State *initialState); + virtual ~Application(void); + private: + Application(const Application &); + Application &operator =(const Application &); + + public: + void Run(void); + void Loop(void); + + public: + virtual void ChangeState(State *); + virtual void PushState(State *); + virtual void PopState(void); + virtual void Quit(void); + + private: + State *CurrentState(void); + void RealPushState(State *); + void RealPopState(void); + void PopAllStates(void); + + private: + void HandleEvents(void); + void UpdateWorld(Uint32 deltaT); + void Render(void); + + private: + sdl::InitScreen *screen; + std::stack states; + Timer timer; + Uint32 last; + +}; + +} + +#endif /* APP_APPLICATION_H_ */ diff --git a/src/app/Control.h b/src/app/Control.h new file mode 100644 index 0000000..b6cd742 --- /dev/null +++ b/src/app/Control.h @@ -0,0 +1,29 @@ +/* + * Control.h + * + * Created on: Apr 19, 2012 + * Author: holy + */ + +#ifndef APP_CONTROL_H_ +#define APP_CONTROL_H_ + +namespace app { + +class State; + +class Control { + + public: + virtual ~Control(void) { }; + + public: + virtual void ChangeState(State *) = 0; + virtual void PushState(State *) = 0; + virtual void PopState(void) = 0; + virtual void Quit(void) = 0; +}; + +} + +#endif /* APP_CONTROL_H_ */ diff --git a/src/app/State.h b/src/app/State.h new file mode 100644 index 0000000..548fc94 --- /dev/null +++ b/src/app/State.h @@ -0,0 +1,35 @@ +/* + * State.h + * + * Created on: Apr 8, 2012 + * Author: holy + */ + +#ifndef APP_APPLICATIONSTATE_H_ +#define APP_APPLICATIONSTATE_H_ + +#include + +namespace app { + +class Control; +class Timer; + +class State { + + public: + virtual ~State(void) { }; + + public: + virtual void EnterState(Control *ctrl, SDL_Surface *screen) = 0; + virtual void ExitState(void) = 0; + + virtual void HandleEvent(const SDL_Event &) = 0; + virtual void UpdateWorld(const Timer &) = 0; + virtual void Render(SDL_Surface *) = 0; + +}; + +} + +#endif /* APP_STATE_H_ */ diff --git a/src/app/Timer.cpp b/src/app/Timer.cpp new file mode 100644 index 0000000..48a5eba --- /dev/null +++ b/src/app/Timer.cpp @@ -0,0 +1,21 @@ +/* + * Timer.cpp + * + * Created on: Apr 25, 2012 + * Author: holy + */ + +#include "Timer.h" + +namespace app { + +void Timer::Update(double dt) { + delta = dt * scale; + elapsed += dt; +} + +double Timer::Elapsed(void) const { + return elapsed * delta; +} + +} /* namespace app */ diff --git a/src/app/Timer.h b/src/app/Timer.h new file mode 100644 index 0000000..997d92c --- /dev/null +++ b/src/app/Timer.h @@ -0,0 +1,33 @@ +/* + * Timer.h + * + * Created on: Apr 25, 2012 + * Author: holy + */ + +#ifndef APP_TIMER_H_ +#define APP_TIMER_H_ + +namespace app { + +class Timer { + + public: + explicit Timer(double scale = 1.0, double initialTime = 0.0) + : scale(scale), elapsed(initialTime), delta(0.0) { }; + + public: + void Update(double dt); + + public: + double DeltaT(void) const { return delta; }; + double Elapsed(void) const; + + private: + double scale, elapsed, delta; + +}; + +} + +#endif /* APP_TIMER_H_ */ diff --git a/src/game/Collision.h b/src/game/Collision.h new file mode 100644 index 0000000..e2ecb01 --- /dev/null +++ b/src/game/Collision.h @@ -0,0 +1,30 @@ +/* + * Collision.h + * + * Created on: Apr 22, 2012 + * Author: holy + */ + +#ifndef GAME_COLLISION_H_ +#define GAME_COLLISION_H_ + +#include "Entity.h" + + +namespace game { + +class Collision { + + public: + Collision(Entity *left, Entity *right, const Entity::Ray &normal) : left(left), right(right), normal(normal) { }; + ~Collision(void) { }; + + public: + Entity *left, *right; + Entity::Ray normal; + +}; + +} + +#endif /* GAME_COLLISION_H_ */ diff --git a/src/game/Entity.cpp b/src/game/Entity.cpp new file mode 100644 index 0000000..1c3f785 --- /dev/null +++ b/src/game/Entity.cpp @@ -0,0 +1,136 @@ +/* + * Entity.cpp + * + * Created on: Apr 25, 2012 + * Author: holy + */ + +#include "Entity.h" + +#include "../app/Timer.h" +#include "../geometry/constants.h" + +#include + +using geometry::PI; +using std::numeric_limits; + +namespace game { + + +void Entity::Update(const app::Timer &timer) { + translation += linearVelocity * timer.DeltaT(); + rotation += angularVelocity * timer.DeltaT(); + if (rotation > 2.0 * PI) { + rotation -= 2.0 * PI; + } else if (rotation < -(2.0 * PI)) { + rotation += 2.0 * PI; + } + shape->Translate(linearVelocity * timer.DeltaT()); + shape->Rotate(angularVelocity * timer.DeltaT()); +} + +bool Entity::CheckCollision(const Entity &other, Ray &ray) { + return shape->CheckCollision(*other.shape, ray); +} + + +Entity::Vector Entity::VelocityAt(const Vector &p) const { + if (angularVelocity == 0 || p == translation) return linearVelocity; + + Vector v(linearVelocity); + Vector pRelative(p - translation); + v += pRelative.Rotate90() * angularVelocity; + return v; +} + + +void Entity::Rotate(Scalar d) { + rotation += d; + shape->Rotate(d); +} + +void Entity::Translate(const Vector &d) { + translation += d; + shape->Translate(d); +} + +void Entity::Accelerate(const Vector &dvl, Scalar dva) { + linearVelocity += dvl; + angularVelocity += dva; +} + + +void Entity::CollisionResponse(Entity &a, const Ray &na, Entity &b) { + if (a.mass == numeric_limits::infinity() + && b.mass == numeric_limits::infinity()) { + // special case: collision of two infinitely heavy entities + InfInfCollisionResponse(a, na, b); + return; + } + + const Scalar e(1); + + const Vector va(a.LinearVelocity()); + const Vector vb(b.LinearVelocity()); + const Scalar wa(a.AngularVelocity()); + const Scalar wb(b.AngularVelocity()); + + const Vector vap(a.VelocityAt(na.Origin())); + const Vector vbp(b.VelocityAt(na.Origin())); + const Vector vab = vap - vbp; + + const Vector rap90((na.Origin() - a.Origin()).Rotate90()); + const Vector rbp90((na.Origin() - b.Origin()).Rotate90()); + + const Scalar ia(a.Mass()); + const Scalar ib(b.Mass()); + + const Scalar rap90dotN(rap90.Dot(na.Direction())); + const Scalar rbp90dotN(rbp90.Dot(na.Direction())); + + const Scalar j( + (((-(1 + e)) * vab).Dot(na.Direction())) + / (na.Direction().Dot(na.Direction() * ((1.0 / a.Mass()) + (1.0 / b.Mass()))) + + ((rap90dotN * rap90dotN) / ia) + + ((rbp90dotN * rbp90dotN) / ib) )); + + const Vector va2(va + ((j / a.Mass()) * na.Direction())); + const Vector vb2(vb - ((j / b.Mass()) * na.Direction())); + const Scalar wa2(wa + ((rap90.Dot(j * na.Direction())) / ia)); + const Scalar wb2(wb - ((rbp90.Dot(j * na.Direction())) / ib)); + + a.linearVelocity = va2; + a.angularVelocity = wa2; + b.linearVelocity = vb2; + b.angularVelocity = wb2; +} + +void Entity::InfInfCollisionResponse(Entity &a, const Ray &na, Entity &b) { + // If both elements are standing still or both are moving, move them away + // from each other. Otherwise, move the moving one. + if ((a.linearVelocity == Vector() && b.linearVelocity == Vector()) + || (a.linearVelocity != Vector() && b.linearVelocity != Vector())) { + a.Translate(na.Direction()); + b.Translate(-na.Direction()); + Ray n; + while (a.shape->CheckCollision(*b.shape, n)) { + a.Translate(n.Direction()); + b.Translate(-n.Direction()); + } + } else if (a.linearVelocity == Vector()) { + b.Translate(-na.Direction()); + Ray n; + while (b.shape->CheckCollision(*a.shape, n)) { + b.Translate(n.Direction()); + } + } else { + a.Translate(na.Direction()); + Ray n; + while (a.shape->CheckCollision(*b.shape, n)) { + a.Translate(n.Direction()); + } + } +} + +} diff --git a/src/game/Entity.h b/src/game/Entity.h new file mode 100644 index 0000000..22d46bd --- /dev/null +++ b/src/game/Entity.h @@ -0,0 +1,74 @@ +/* + * Entity.h + * + * Created on: Apr 25, 2012 + * Author: holy + */ + +#ifndef GAME_ENTITY_H_ +#define GAME_ENTITY_H_ + +#include "../shape/Shape.h" + +#include + +namespace app { class Timer; } + + +namespace game { + +class Entity { + + friend std::ostream &operator <<(std::ostream &, const Entity &); + + public: + typedef shape::Shape::Scalar Scalar; + typedef shape::Shape::Vector Vector; + typedef shape::Shape::Ray Ray; + + public: + static void CollisionResponse(Entity &a, const Entity::Ray &na, Entity &b); + + public: + explicit Entity(shape::Shape *s, Scalar mass = 1.0) : shape(s), mass(mass) { }; + virtual ~Entity(void) { }; + + public: + void Update(const app::Timer &); + bool CheckCollision(const Entity &, Ray &); + + public: + virtual void Collide(Entity &other, const Ray &) { }; + virtual void Render(SDL_Surface *dest) const { }; + + public: + const Vector &Origin(void) const { return translation; }; + const Vector &LinearVelocity(void) const { return linearVelocity; }; + Scalar Angle(void) const { return rotation; }; + Scalar AngularVelocity(void) const { return angularVelocity; }; + Vector VelocityAt(const Vector &p) const; + const Scalar Mass(void) const { return mass; }; + + public: + void Rotate(Scalar); + void Translate(const Vector &); + void Accelerate(const Vector &linear, Scalar angular = 0); + + private: + static void InfInfCollisionResponse(Entity &a, const Entity::Ray &na, Entity &b); + + private: + shape::Shape *shape; + Vector translation, linearVelocity; + Scalar rotation, angularVelocity, mass; + +}; + + +inline std::ostream &operator <<(std::ostream &out, const Entity &e) { + return out << *e.shape; +} + +} + +#endif /* GAME_ENTITY_H_ */ diff --git a/src/geometry/Ray2D.h b/src/geometry/Ray2D.h new file mode 100644 index 0000000..408a061 --- /dev/null +++ b/src/geometry/Ray2D.h @@ -0,0 +1,35 @@ +/* + * Ray2D.h + * + * Created on: Apr 25, 2012 + * Author: holy + */ + +#ifndef GEOMETRY_RAY2D_H_ +#define GEOMETRY_RAY2D_H_ + +#include "Vector2D.h" + +namespace geometry { + +template +class Ray2D { + + public: + explicit Ray2D(const Vector2D &directionUnit = Vector2D(), const Vector2D &origin = Vector2D()) + : direction(directionUnit), origin(origin) { }; + + public: + Vector2D &Direction(void) { return direction; }; + Vector2D &Origin(void) { return origin; }; + const Vector2D &Direction(void) const { return direction; }; + const Vector2D &Origin(void) const { return origin; }; + + private: + Vector2D direction, origin; + +}; + +} + +#endif /* GEOMETRY_RAY2D_H_ */ diff --git a/src/geometry/Vector2D.h b/src/geometry/Vector2D.h new file mode 100644 index 0000000..1134b38 --- /dev/null +++ b/src/geometry/Vector2D.h @@ -0,0 +1,177 @@ +/* + * Vector.h + * + * Created on: Apr 23, 2012 + * Author: holy + */ + +#ifndef GEOMETRY_VECTOR2D_H_ +#define GEOMETRY_VECTOR2D_H_ + +#include +#include + + +namespace geometry { + +template +class Vector2D { + + public: + Vector2D(void) : x(), y() { }; + Vector2D(Scalar x, Scalar y) : x(x), y(y) { }; + + public: + Scalar X(void) const { return x; }; + Scalar Y(void) const { return y; }; + + public: + Scalar Dot(const Vector2D &o) const { return (X() * o.X()) + (Y() * o.Y()); }; + Scalar LengthSquared(void) const { return Dot(*this); }; + Scalar Length(void) const { return std::sqrt(LengthSquared()); }; + + public: + Vector2D &Unify(void) { + if (LengthSquared() == Scalar()) return *this; + *this /= Length(); + return *this; + }; + Vector2D Unit(void) const { + if (LengthSquared() == Scalar()) return *this; + return *this / Length(); + }; + Vector2D &Reflect(const Vector2D &normal); + Vector2D &Rotate90(void) { + return *this = Vector2D(-Y(), X()); + }; + Vector2D &Rotate(Scalar by) { + Scalar sine(std::sin(by)), cosine(std::cos(by)); + return *this = Vector2D(X() * cosine - Y() * sine, X() * sine + y * cosine); + }; + + public: + Vector2D &operator +=(const Vector2D &o) { + x += o.X(); + y += o.Y(); + return *this; + }; + Vector2D &operator -=(const Vector2D &o) { + x -= o.X(); + y -= o.Y(); + return *this; + }; + template + Vector2D &operator +=(T s) { x += s; y += s; return *this; }; + template + Vector2D &operator -=(T s) { x -= s; y -= s; return *this; }; + template + Vector2D &operator *=(T s) { x *= s; y *= s; return *this; }; + template + Vector2D &operator /=(T s) { x /= s; y /= s; return *this; }; + + private: + Scalar x, y; + +}; + +template +inline Vector2D &Vector2D::Reflect(const Vector2D &normal) { + Scalar doubleDot(2 * Dot(normal)); + x -= doubleDot * normal.X(); + y -= doubleDot * normal.Y(); + return *this; +}; + + +template +inline bool operator ==(const Vector2D &lhs, const Vector2D &rhs) { + return lhs.X() == rhs.X() && lhs.Y() == rhs.Y(); +}; + +template +inline bool operator !=(const Vector2D &lhs, const Vector2D &rhs) { + return !(lhs == rhs); +}; + +template +inline Vector2D operator +(const Vector2D &v) { + return v; +}; +template +inline Vector2D operator -(const Vector2D &v) { + return Vector2D(-v.X(), -v.Y()); +}; + +template +inline Vector2D operator +(const Vector2D &lhs, const Vector2D &rhs) { + Vector2D temp(lhs); + temp += rhs; + return temp; +}; +template +inline Vector2D operator -(const Vector2D &lhs, const Vector2D &rhs) { + Vector2D temp(lhs); + temp -= rhs; + return temp; +}; + +template +inline Vector2D operator +(const Vector2D &v, SScalar s) { + Vector2D temp(v); + temp += s; + return temp; +}; +template +inline Vector2D operator -(const Vector2D &v, SScalar s) { + Vector2D temp(v); + temp -= s; + return temp; +}; + +template +inline Vector2D operator +(SScalar s, const Vector2D &v) { + Vector2D temp(v); + temp += s; + return temp; +}; +template +inline Vector2D operator -(SScalar s, const Vector2D &v) { + Vector2D temp(v); + temp -= s; + return temp; +}; + +template +inline Vector2D operator *(const Vector2D &v, SScalar s) { + Vector2D temp(v); + temp *= s; + return temp; +}; +template +inline Vector2D operator *(SScalar s, const Vector2D &v) { + Vector2D temp(v); + temp *= s; + return temp; +}; +template +inline Vector2D operator /(const Vector2D &v, SScalar s) { + Vector2D temp(v); + temp /= s; + return temp; +}; +template +inline Vector2D operator /(SScalar s, const Vector2D &v) { + Vector2D temp(v); + temp /= s; + return temp; +}; + + +template +std::ostream &operator <<(std::ostream &out, const Vector2D &v) { + return out << '<' << v.X() << '|' << v.Y() << '>'; +} + +} + +#endif /* GEOMETRY_VECTOR2D_H_ */ diff --git a/src/geometry/constants.h b/src/geometry/constants.h new file mode 100644 index 0000000..a33ea2a --- /dev/null +++ b/src/geometry/constants.h @@ -0,0 +1,21 @@ +/* + * constants.h + * + * Created on: Apr 26, 2012 + * Author: holy + */ + +#ifndef GEOMETRY_CONSTANTS_H_ +#define GEOMETRY_CONSTANTS_H_ + +#include + + +namespace geometry { + +const double PI(std::atan(1.0) * 4.0); + +} + + +#endif /* GEOMETRY_CONSTANTS_H_ */ diff --git a/src/pong/Ball.cpp b/src/pong/Ball.cpp new file mode 100644 index 0000000..97248f3 --- /dev/null +++ b/src/pong/Ball.cpp @@ -0,0 +1,35 @@ +/* + * Ball.cpp + * + * Created on: Apr 9, 2012 + * Author: holy + */ + +#include "Ball.h" + +#include "Paddle.h" +#include "../geometry/constants.h" + +#include +#include + +using game::Entity; +using geometry::PI; + + +namespace pong { + +Ball::Ball(Scalar r) +: Entity(&shape, 4 * PI * r * r * r / 3) +, shape(r) { + +} + + +void Ball::Render(SDL_Surface *screen) const { + circleRGBA(screen, shape.Center().X(), shape.Center().Y(), shape.Radius(), 0xFF, 0xFF, 0xFF, 0xFF); + Vector angle(Vector(0, 1).Rotate(Angle()) * shape.Radius() + Origin()); + lineRGBA(screen, shape.Center().X(), shape.Center().Y(), angle.X(), angle.Y(), 0xFF, 0xFF, 0xFF, 0xFF); +} + +} diff --git a/src/pong/Ball.h b/src/pong/Ball.h new file mode 100644 index 0000000..2e54a8c --- /dev/null +++ b/src/pong/Ball.h @@ -0,0 +1,34 @@ +/* + * Ball.h + * + * Created on: Apr 9, 2012 + * Author: holy + */ + +#ifndef PONG_BALL_H_ +#define PONG_BALL_H_ + +#include "../game/Entity.h" +#include "../shape/Circle.h" + + +namespace pong { + +class Ball +: public game::Entity { + + public: + explicit Ball(Scalar radius); + virtual ~Ball(void) { }; + + public: + virtual void Render(SDL_Surface *dest) const; + + private: + shape::Circle shape; + +}; + +} + +#endif /* PONG_BALL_H_ */ diff --git a/src/pong/CountingWall.cpp b/src/pong/CountingWall.cpp new file mode 100644 index 0000000..4b044a7 --- /dev/null +++ b/src/pong/CountingWall.cpp @@ -0,0 +1,37 @@ +/* + * CountingWall.cpp + * + * Created on: Apr 17, 2012 + * Author: holy + */ + +#include "CountingWall.h" + +#include + +using game::Entity; + + +namespace pong { + +CountingWall::CountingWall(Scalar width, Scalar height) +: Entity(&shape, std::numeric_limits::infinity()) +, shape(width, height) +, count(0) { + +} + +CountingWall::~CountingWall(void) { + +} + + +void CountingWall::Collide(Entity &, const Ray &) { + ++count; +} + +void CountingWall::Render(SDL_Surface *dest) const { + +} + +} diff --git a/src/pong/CountingWall.h b/src/pong/CountingWall.h new file mode 100644 index 0000000..c14820d --- /dev/null +++ b/src/pong/CountingWall.h @@ -0,0 +1,40 @@ +/* + * CountingWall.h + * + * Created on: Apr 17, 2012 + * Author: holy + */ + +#ifndef PONG_COUNTINGWALL_H_ +#define PONG_COUNTINGWALL_H_ + +#include "../game/Entity.h" +#include "../shape/AABB.h" + +#include + +namespace pong { + +class CountingWall +: public game::Entity { + + public: + CountingWall(Scalar width, Scalar height); + virtual ~CountingWall(void); + + public: + int HitCount(void) { return count; }; + + public: + virtual void Collide(Entity &other, const Ray &); + virtual void Render(SDL_Surface *dest) const; + + private: + shape::AABB shape; + int count; + +}; + +} + +#endif /* PONG_COUNTINGWALL_H_ */ diff --git a/src/pong/Match.cpp b/src/pong/Match.cpp new file mode 100644 index 0000000..5754b36 --- /dev/null +++ b/src/pong/Match.cpp @@ -0,0 +1,213 @@ +/* + * Match.cpp + * + * Created on: Apr 9, 2012 + * Author: holy + */ + +#include "Match.h" + +#include "../app/Control.h" + +#include +#include +#include +#include + +using app::Control; +using app::Timer; +using game::Collision; +using game::Entity; +using std::runtime_error; +using std::stringstream; +using std::vector; + + +namespace pong { + +Match::Match(void) +: ctrl(0) +, scoreFont(TTF_OpenFont("data/font/Magra-Regular.ttf", 30)) +, leftScoreText(0) +, rightScoreText(0) +, paddleSpeed(150) +, worldWidth(800) +, worldHeight(480) +, ball(10) +, secondBall(7) +, thirdBall(5) +, leftPaddle(10, 100) +, rightPaddle(10, 100) +, topWall(800, 10) +, bottomWall(800, 10) +, leftWall(10, 480) +, rightWall(10, 480) +, entities() +, collisions() +, updateScore(true) { + + if (!scoreFont) { + throw runtime_error("failed to load score font"); + } + + ball.Translate(Entity::Vector(400, 240)); + ball.Accelerate(Entity::Vector(180, 180)); +// ball.SetMaxVelocity(500.0f); + + secondBall.Translate(Entity::Vector(300, 240)); + secondBall.Accelerate(Entity::Vector(-50, -50), 1); +// secondBall.SetMaxVelocity(600.0f); + + thirdBall.Translate(Entity::Vector(200, 440)); + thirdBall.Accelerate(Entity::Vector(60, 40)); + + leftPaddle.Translate(Entity::Vector(5, 200)); + rightPaddle.Translate(Entity::Vector(795, 280)); + + leftPaddle.SetMovementSpeed(Entity::Vector(0, paddleSpeed)); + rightPaddle.SetMovementSpeed(Entity::Vector(0, paddleSpeed)); + + topWall.Translate(Entity::Vector(400, -5)); + rightWall.Translate(Entity::Vector(805, 240)); + bottomWall.Translate(Entity::Vector(400, 485)); + leftWall.Translate(Entity::Vector(-5, 240)); + + entities.reserve(9); + entities.push_back(&ball); + entities.push_back(&secondBall); + entities.push_back(&thirdBall); + entities.push_back(&leftPaddle); + entities.push_back(&rightPaddle); + entities.push_back(&topWall); + entities.push_back(&bottomWall); + entities.push_back(&leftWall); + entities.push_back(&rightWall); + + collisions.reserve(2 * entities.size()); +} + +Match::~Match(void) { + +} + + +void Match::EnterState(Control *c, SDL_Surface *screen) { + ctrl = c; +} + +void Match::ExitState(void) { + if (scoreFont) { + TTF_CloseFont(scoreFont); + scoreFont = 0; + } +} + +void Match::HandleEvent(const SDL_Event &e) { + if (e.type == SDL_KEYDOWN) { + if (e.key.keysym.sym == SDLK_w) { + leftPaddle.StartMovingUp(); + } else if (e.key.keysym.sym == SDLK_s) { + leftPaddle.StartMovingDown(); + } else if (e.key.keysym.sym == SDLK_UP) { + rightPaddle.StartMovingUp(); + } else if (e.key.keysym.sym == SDLK_DOWN) { + rightPaddle.StartMovingDown(); + } + } else if (e.type == SDL_KEYUP) { + if (e.key.keysym.sym == SDLK_w) { + leftPaddle.StopMovingUp(); + } else if (e.key.keysym.sym == SDLK_s) { + leftPaddle.StopMovingDown(); + } else if (e.key.keysym.sym == SDLK_UP) { + rightPaddle.StopMovingUp(); + } else if (e.key.keysym.sym == SDLK_DOWN) { + rightPaddle.StopMovingDown(); + } else if (e.key.keysym.sym == SDLK_q) { + if (ctrl) ctrl->Quit(); + } + } +} + +void Match::UpdateWorld(const Timer &timer) { + UpdateEntities(timer); + CheckCollisions(timer); + HandleCollisions(timer); +} + +void Match::UpdateEntities(const Timer &timer) { + for (vector::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) { + (*i)->Update(timer); + } +} + +void Match::CheckCollisions(const Timer &timer) { + Entity::Ray normal; + for (vector::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) { + for (vector::const_iterator j(i + 1); j != end; ++j) { + if ((*i)->CheckCollision(**j, normal)) { + collisions.push_back(Collision(*i, *j, normal)); + } + } + } +} + +void Match::HandleCollisions(const Timer &timer) { + for (vector::const_iterator i(collisions.begin()), end(collisions.end()); i != end; ++i) { + Entity::CollisionResponse(*(i->left), i->normal, *(i->right)); + } + // TODO: resolve collisions of static objects + for (vector::const_iterator i(collisions.begin()), end(collisions.end()); i != end; ++i) { + i->left->Collide(*i->right, i->normal); + i->right->Collide(*i->left, i->normal); + } + if (!collisions.empty()) { + updateScore = true; + } + collisions.clear(); +} + + +void Match::Render(SDL_Surface *screen) { + SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0)); + for (vector::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) { + (*i)->Render(screen); + } + RenderHUD(screen); +} + +void Match::RenderHUD(SDL_Surface *screen) { + if (updateScore) { + UpdateScore(screen); + updateScore = false; + } + RenderScore(screen); +} + +void Match::RenderScore(SDL_Surface *screen) { + SDL_Rect dest = { 2, 2, 0, 0 }; + SDL_BlitSurface(leftScoreText, 0, screen, &dest); + + dest.x = screen->w - rightScoreText->w - 2; + SDL_BlitSurface(rightScoreText, 0, screen, &dest); +} + +void Match::UpdateScore(SDL_Surface *screen) { + if (!scoreFont) return; + if (leftScoreText) SDL_FreeSurface(leftScoreText); + if (rightScoreText) SDL_FreeSurface(rightScoreText); + + SDL_Color color; + color.r = 0xFF; + color.g = 0xFF; + color.b = 0xFF; + + stringstream s; + s << rightWall.HitCount(); + leftScoreText = TTF_RenderUTF8_Blended(scoreFont, s.str().c_str(), color); + + s.str(""); + s << leftWall.HitCount(); + rightScoreText = TTF_RenderUTF8_Blended(scoreFont, s.str().c_str(), color); +} + +} diff --git a/src/pong/Match.h b/src/pong/Match.h new file mode 100644 index 0000000..a838ebe --- /dev/null +++ b/src/pong/Match.h @@ -0,0 +1,66 @@ +/* + * Match.h + * + * Created on: Apr 9, 2012 + * Author: holy + */ + +#ifndef PONG_MATCH_H_ +#define PONG_MATCH_H_ + +#include "Ball.h" +#include "CountingWall.h" +#include "Paddle.h" +#include "../app/State.h" +#include "../game/Collision.h" +#include "../game/Entity.h" + +#include +#include +#include + + +namespace pong { + +class Match +: public app::State { + + public: + Match(void); + virtual ~Match(void); + + public: + virtual void EnterState(app::Control *ctrl, SDL_Surface *screen); + virtual void ExitState(void); + + virtual void HandleEvent(const SDL_Event &); + virtual void UpdateWorld(const app::Timer &); + virtual void Render(SDL_Surface *); + + private: + void UpdateEntities(const app::Timer &); + void CheckCollisions(const app::Timer &); + void CheckWorldCollisions(const app::Timer &); + void HandleCollisions(const app::Timer &); + void UpdateScore(SDL_Surface *); + void RenderHUD(SDL_Surface *); + void RenderScore(SDL_Surface *); + + private: + app::Control *ctrl; + TTF_Font *scoreFont; + SDL_Surface *leftScoreText, *rightScoreText; + Sint32 paddleSpeed; + Uint32 worldWidth, worldHeight; + Ball ball, secondBall, thirdBall; + Paddle leftPaddle, rightPaddle; + CountingWall topWall, bottomWall, leftWall, rightWall; + std::vector entities; + std::vector collisions; + bool updateScore; + +}; + +} + +#endif /* PONG_MATCH_H_ */ diff --git a/src/pong/Paddle.cpp b/src/pong/Paddle.cpp new file mode 100644 index 0000000..ef922cc --- /dev/null +++ b/src/pong/Paddle.cpp @@ -0,0 +1,64 @@ +/* + * Paddle.cpp + * + * Created on: Apr 10, 2012 + * Author: holy + */ + +#include "Paddle.h" + +#include "Ball.h" + +#include + + +namespace pong { + +Paddle::Paddle(Scalar width, Scalar height) +: Entity(&shape, std::numeric_limits::infinity()) +, shape(width, height) { + +} + + +void Paddle::Collide(Entity &other, const Ray &) { + if (dynamic_cast(&other)) { + Vector distance(other.Origin() - Origin()); + other.Accelerate(distance.Unit() * 5); + } +} + +void Paddle::Render(SDL_Surface *screen) const { + SDL_Rect destRect; + destRect.x = shape.Left(); + destRect.y = shape.Top(); + destRect.w = shape.Width(); + destRect.h = shape.Height(); + SDL_FillRect(screen, &destRect, SDL_MapRGB(screen->format, 0xFF, 0xFF, 0xFF)); +} + + +void Paddle::SetMovementSpeed(Vector s) { +// Vector d(s - movementSpeed); + movementSpeed = s; +// Accelerate(d); +} + + +void Paddle::StartMovingUp(void) { + Accelerate(-movementSpeed); +} + +void Paddle::StopMovingUp(void) { + Accelerate(movementSpeed); +} + +void Paddle::StartMovingDown(void) { + Accelerate(movementSpeed); +} + +void Paddle::StopMovingDown(void) { + Accelerate(-movementSpeed); +} + +} diff --git a/src/pong/Paddle.h b/src/pong/Paddle.h new file mode 100644 index 0000000..85da2af --- /dev/null +++ b/src/pong/Paddle.h @@ -0,0 +1,44 @@ +/* + * Paddle.h + * + * Created on: Apr 10, 2012 + * Author: holy + */ + +#ifndef PONG_PADDLE_H_ +#define PONG_PADDLE_H_ + +#include "../game/Entity.h" +#include "../shape/AABB.h" + + +namespace pong { + +class Paddle +: public game::Entity { + + public: + Paddle(Scalar width, Scalar height); + virtual ~Paddle(void) { }; + + public: + virtual void Collide(Entity &other, const Ray &); + virtual void Render(SDL_Surface *dest) const; + + public: + void SetMovementSpeed(Vector); + + void StartMovingUp(void); + void StopMovingUp(void); + void StartMovingDown(void); + void StopMovingDown(void); + + private: + shape::AABB shape; + Vector movementSpeed; + +}; + +} + +#endif /* PONG_PADDLE_H_ */ diff --git a/src/sdl-test8.cpp b/src/sdl-test8.cpp new file mode 100644 index 0000000..ad1e891 --- /dev/null +++ b/src/sdl-test8.cpp @@ -0,0 +1,26 @@ +//============================================================================ +// Name : sdl-test8.cpp +// Author : HolySmoke +// Version : .3 +// Copyright : MINE, ALL MINE! +// Description : Some pong crap supposed to have somewhat realistic physics. +//============================================================================ + +#include "app/Application.h" +#include "pong/Match.h" +#include "sdl/InitScreen.h" +#include "sdl/InitSDL.h" +#include "sdl/InitTTF.h" + +int main(int argc, char *argv[]) { + const int width(800); + const int height(480); + + sdl::InitSDL initSDL; + sdl::InitTTF initTTF; + sdl::InitScreen initScreen(width, height); + + app::Application a(&initScreen, new pong::Match); + a.Run(); + return 0; +} diff --git a/src/sdl/InitSDL.cpp b/src/sdl/InitSDL.cpp new file mode 100644 index 0000000..0f2eacd --- /dev/null +++ b/src/sdl/InitSDL.cpp @@ -0,0 +1,27 @@ +/* + * InitSDL.cpp + * + * Created on: Apr 22, 2012 + * Author: holy + */ + +#include "InitSDL.h" + +#include + +using std::runtime_error; + + +namespace sdl { + +InitSDL::InitSDL(Uint32 flags) { + if (SDL_Init(flags) != 0) { + throw runtime_error("failed to initialize SDL"); + } +} + +InitSDL::~InitSDL(void) { + SDL_Quit(); +} + +} diff --git a/src/sdl/InitSDL.h b/src/sdl/InitSDL.h new file mode 100644 index 0000000..016c0b4 --- /dev/null +++ b/src/sdl/InitSDL.h @@ -0,0 +1,29 @@ +/* + * InitSDL.h + * + * Created on: Apr 22, 2012 + * Author: holy + */ + +#ifndef SDL_INITSDL_H_ +#define SDL_INITSDL_H_ + +#include + + +namespace sdl { + +class InitSDL { + + public: + explicit InitSDL(Uint32 flags = SDL_INIT_EVERYTHING); + virtual ~InitSDL(void); + private: + InitSDL(const InitSDL &); + InitSDL &operator =(const InitSDL &); + +}; + +} + +#endif /* SDL_INITSDL_H_ */ diff --git a/src/sdl/InitScreen.cpp b/src/sdl/InitScreen.cpp new file mode 100644 index 0000000..c9b275f --- /dev/null +++ b/src/sdl/InitScreen.cpp @@ -0,0 +1,28 @@ +/* + * InitScreen.cpp + * + * Created on: Apr 22, 2012 + * Author: holy + */ + +#include "InitScreen.h" + +#include + +using std::runtime_error; + + +namespace sdl { + +InitScreen::InitScreen(int width, int height, int bpp, Sint32 flags) +: screen(SDL_SetVideoMode(width, height, bpp, flags)) { + if (!screen) { + throw runtime_error("failed to open screen"); + } +} + +InitScreen::~InitScreen(void) { + +} + +} diff --git a/src/sdl/InitScreen.h b/src/sdl/InitScreen.h new file mode 100644 index 0000000..ba731da --- /dev/null +++ b/src/sdl/InitScreen.h @@ -0,0 +1,37 @@ +/* + * InitScreen.h + * + * Created on: Apr 22, 2012 + * Author: holy + */ + +#ifndef SDL_INITSCREEN_H_ +#define SDL_INITSCREEN_H_ + +#include + +namespace sdl { + +class InitScreen { + + public: + InitScreen(int width, int height, int bpp = 32, Sint32 flags = SDL_HWSURFACE | SDL_DOUBLEBUF); + virtual ~InitScreen(void); + private: + InitScreen(const InitScreen &); + InitScreen &operator =(const InitScreen &); + + public: + SDL_Surface *Screen(void) { return screen; }; + const SDL_Surface *Screen(void) const { return screen; }; + + void Flip(void) { SDL_Flip(screen); }; + + private: + SDL_Surface *screen; + +}; + +} + +#endif /* SDL_INITSCREEN_H_ */ diff --git a/src/sdl/InitTTF.cpp b/src/sdl/InitTTF.cpp new file mode 100644 index 0000000..5818a73 --- /dev/null +++ b/src/sdl/InitTTF.cpp @@ -0,0 +1,28 @@ +/* + * InitTTF.cpp + * + * Created on: Apr 22, 2012 + * Author: holy + */ + +#include "InitTTF.h" + +#include +#include + +using std::runtime_error; + + +namespace sdl { + +InitTTF::InitTTF(void) { + if (TTF_Init() != 0) { + throw runtime_error("failed to initialize SDL TTF"); + } +} + +InitTTF::~InitTTF(void) { + TTF_Quit(); +} + +} diff --git a/src/sdl/InitTTF.h b/src/sdl/InitTTF.h new file mode 100644 index 0000000..6f0306c --- /dev/null +++ b/src/sdl/InitTTF.h @@ -0,0 +1,26 @@ +/* + * InitTTF.h + * + * Created on: Apr 22, 2012 + * Author: holy + */ + +#ifndef SDL_INITTTF_H_ +#define SDL_INITTTF_H_ + +namespace sdl { + +class InitTTF { + + public: + InitTTF(void); + virtual ~InitTTF(void); + private: + InitTTF(const InitTTF &); + InitTTF &operator =(const InitTTF &); + +}; + +} + +#endif /* SDL_INITTTF_H_ */ diff --git a/src/shape/AABB.cpp b/src/shape/AABB.cpp new file mode 100644 index 0000000..75007ec --- /dev/null +++ b/src/shape/AABB.cpp @@ -0,0 +1,73 @@ +/* + * AABB.cpp + * + * Created on: Apr 30, 2012 + * Author: holy + */ + +#include "AABB.h" + +#include "Circle.h" + +namespace shape { + +void AABB::Translate(const Vector &delta) { + leftTop += delta; + rightBottom += delta; +} + +void AABB::Rotate(Scalar delta) { + +} + + +bool AABB::CheckCollision(const Shape &other, Ray &na) const { + if (other.CheckCollision(*this, na)) { + na.Direction() *= -1; + return true; + } else { + return false; + } +} + +bool AABB::CheckCollision(const AABB &other, Ray &na) const { + if (Bottom() <= other.Top()) return false; + if (other.Bottom() <= Top()) return false; + if (Right() <= other.Left()) return false; + if (other.Right() <= Left()) return false; + + if (Bottom() <= other.Top() || other.Bottom() <= Top()) { + if (Left() < other.Left()) { + na.Origin() = Vector(((Top() + Bottom()) / 2 + (other.Top() + other.Bottom()) / 2) / 2, Right()); + na.Direction() = Vector(-1, 0); + } else { + na.Origin() = Vector(((Top() + Bottom()) / 2 + (other.Top() + other.Bottom()) / 2) / 2, Left()); + na.Direction() = Vector(1, 0); + } + } else { + if (Top() < other.Top()) { + na.Origin() = Vector(Bottom(), ((Left() + Right()) / 2 + (other.Left() + other.Right()) / 2) / 2); + na.Direction() = Vector(0, -1); + } else { + na.Origin() = Vector(Top(), ((Left() + Right()) / 2 + (other.Left() + other.Right()) / 2) / 2); + na.Direction() = Vector(0, 1); + } + } + return true; +} + +bool AABB::CheckCollision(const Circle &other, Ray &na) const { + if (other.CheckCollision(*this, na)) { + na.Direction() *= -1; + return true; + } else { + return false; + } +} + + +std::ostream &AABB::Write(std::ostream &out) const { + return out << "AABB(" << Width() << 'x' << Height() << ", " << Center() << ')'; +} + +} diff --git a/src/shape/AABB.h b/src/shape/AABB.h new file mode 100644 index 0000000..956114a --- /dev/null +++ b/src/shape/AABB.h @@ -0,0 +1,52 @@ +/* + * AABB.h + * + * Created on: Apr 30, 2012 + * Author: holy + */ + +#ifndef SHAPE_AABB_H_ +#define SHAPE_AABB_H_ + +#include "Shape.h" + +namespace shape { + +class AABB +: public Shape { + + public: + AABB(void) { }; + AABB(Scalar width, Scalar height) : leftTop(-width/2, -height/2), rightBottom(width/2, height/2) { }; + virtual ~AABB(void) { }; + + public: + Scalar Left(void) const { return leftTop.X(); }; + Scalar Top(void) const { return leftTop.Y(); }; + Scalar Right(void) const { return rightBottom.X(); }; + Scalar Bottom(void) const { return rightBottom.Y(); }; + + Vector Center(void) const { return leftTop + (rightBottom - leftTop) / 2; }; + Scalar Width(void) const { return Right() - Left(); }; + Scalar Height(void) const { return Bottom() - Top(); }; + + public: + virtual void Translate(const Vector &delta); + virtual void Rotate(Scalar delta); + + public: + virtual bool CheckCollision(const Shape &, Ray &) const; + virtual bool CheckCollision(const AABB &, Ray &) const; + virtual bool CheckCollision(const Circle &, Ray &) const; + + public: + virtual std::ostream &Write(std::ostream &) const; + + private: + Vector leftTop, rightBottom; + +}; + +} + +#endif /* SHAPE_AABB_H_ */ diff --git a/src/shape/Circle.cpp b/src/shape/Circle.cpp new file mode 100644 index 0000000..3549db9 --- /dev/null +++ b/src/shape/Circle.cpp @@ -0,0 +1,110 @@ +/* + * Circle.cpp + * + * Created on: Apr 29, 2012 + * Author: holy + */ + +#include "Circle.h" + +#include "AABB.h" + +namespace shape { + +void Circle::Translate(const Vector &delta) { + center += delta; +} + +void Circle::Rotate(Scalar delta) { + +} + + +bool Circle::CheckCollision(const Shape &other, Ray &na) const { + if (other.CheckCollision(*this, na)) { + na.Direction() *= -1; + return true; + } else { + return false; + } +} + +bool Circle::CheckCollision(const AABB &other, Ray &na) const { + int xZone( + center.X() < other.Left() ? 0 : ( + other.Right() < center.X() ? 2 : 1)); + int yZone( + center.Y() < other.Top() ? 0 : ( + other.Bottom() < center.Y() ? 2 : 1)); + int zone(xZone + 3 * yZone); + + if (zone == 4) { + na.Origin() = center; + na.Direction() = (center - other.Center()).Unit(); + return true; + } + + if (zone % 2) { + if (xZone == 1) { // vertical + if (Bottom() < other.Top()) return false; + if (other.Bottom() < Top()) return false; + + if (Bottom() < other.Bottom()) { + na.Origin() = Vector(center.X(), Bottom()); + na.Direction() = Vector(0, -1); + } else { + na.Origin() = Vector(center.X(), Top()); + na.Direction() = Vector(0, 1); + } + + return true; + } else { + if (Right() < other.Left()) return false; + if (other.Right() < Left()) return false; + + if (Left() < other.Left()) { + na.Origin() = Vector(Left(), center.Y()); + na.Direction() = Vector(-1, 0); + } else { + na.Origin() = Vector(Right(), center.Y()); + na.Direction() = Vector(1, 0); + } + + return true; + } + } else { + Vector near( + yZone ? other.Right() : other.Left(), + xZone ? other.Bottom() : other.Top()); + Vector distance(Center() - near); + Scalar distanceSquared(distance.LengthSquared()); + if (distanceSquared < (Radius() * Radius())) { + na.Origin() = near; + na.Direction() = distance.Unit(); + return true; + } else { + return false; + } + } +} + +bool Circle::CheckCollision(const Circle &other, Ray &na) const { + const Vector distance(center - other.center); + const Scalar distanceSquared(distance.LengthSquared()); + const Scalar radii(radius + other.radius); + + if (distanceSquared < (radii * radii)) { + na.Direction() = distance.Unit(); + na.Origin() = center + (na.Direction() * radius); + return true; + } else { + return false; + } +} + + +std::ostream &Circle::Write(std::ostream &out) const { + return out << "Circle(" << Radius() << ", " << Center() << ')'; +} + +} diff --git a/src/shape/Circle.h b/src/shape/Circle.h new file mode 100644 index 0000000..ff0363d --- /dev/null +++ b/src/shape/Circle.h @@ -0,0 +1,52 @@ +/* + * Circle.h + * + * Created on: Apr 29, 2012 + * Author: holy + */ + +#ifndef SHAPE_CIRCLE_H_ +#define SHAPE_CIRCLE_H_ + +#include "Shape.h" + +namespace shape { + +class Circle +: public Shape { + + public: + Circle(void) { }; + explicit Circle(Scalar r, Vector pos = Vector()) : center(pos), radius(r) { }; + virtual ~Circle(void) { }; + + public: + const Vector &Center(void) const { return center; }; + Scalar Radius(void) const { return radius; }; + + Scalar Left(void) const { return center.X() - radius; }; + Scalar Top(void) const { return center.Y() - radius; }; + Scalar Right(void) const { return center.X() + radius; }; + Scalar Bottom(void) const { return center.Y() + radius; }; + + public: + virtual void Translate(const Vector &delta); + virtual void Rotate(Scalar delta); + + public: + virtual bool CheckCollision(const Shape &, Ray &) const; + virtual bool CheckCollision(const AABB &, Ray &) const; + virtual bool CheckCollision(const Circle &, Ray &) const; + + public: + virtual std::ostream &Write(std::ostream &) const; + + private: + Vector center; + Scalar radius; + +}; + +} + +#endif /* SHAPE_CIRCLE_H_ */ diff --git a/src/shape/Shape.h b/src/shape/Shape.h new file mode 100644 index 0000000..69eedc2 --- /dev/null +++ b/src/shape/Shape.h @@ -0,0 +1,53 @@ +/* + * Shape.h + * + * Created on: Apr 29, 2012 + * Author: holy + */ + +#ifndef SHAPE_SHAPE_H_ +#define SHAPE_SHAPE_H_ + +#include "../geometry/Ray2D.h" +#include "../geometry/Vector2D.h" + +#include + + +namespace shape { + +class AABB; +class Circle; + +class Shape { + + public: + typedef float Scalar; + typedef geometry::Vector2D Vector; + typedef geometry::Ray2D Ray; + + public: + virtual ~Shape(void) { }; + + public: + virtual void Translate(const Vector &delta) = 0; + virtual void Rotate(Scalar delta) = 0; + + public: + virtual bool CheckCollision(const Shape &, Ray &) const = 0; + virtual bool CheckCollision(const AABB &, Ray &) const = 0; + virtual bool CheckCollision(const Circle &, Ray &) const = 0; + + public: + virtual std::ostream &Write(std::ostream &) const = 0; + +}; + + +inline std::ostream &operator <<(std::ostream &out, const Shape &s) { + return s.Write(out); +}; + +} + +#endif /* SHAPE_SHAPE_H_ */