From aa6df65e97f0bcd38292312d798b2a3ae9d3aa1e Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Wed, 8 May 2013 00:00:05 +0200 Subject: [PATCH] Changed sprites to always render as rectangle. Also created Rectangle/Circle subclasses for sprite to handle collision detection and moved collision detection from World to CollisionModel (static methods). --- source/World.cpp | 118 ++------------------------- source/abstract/Character.cpp | 7 +- source/abstract/Character.h | 9 +- source/abstract/Circle.cpp | 46 +++++++++++ source/abstract/Circle.h | 30 +++++++ source/abstract/CollisionModel.cpp | 127 +++++++++++++++++++++++++++++ source/abstract/CollisionModel.h | 29 +++++++ source/abstract/Rectangle.cpp | 37 +++++++++ source/abstract/Rectangle.h | 29 +++++++ source/abstract/Sprite.cpp | 114 ++++++-------------------- source/abstract/Sprite.h | 39 ++------- source/effects/Bullet.cpp | 6 +- source/particle/Particle.cpp | 6 +- source/particle/Particle.h | 8 +- source/sprites/Corpse.cpp | 2 +- source/sprites/Corpse.h | 4 +- source/sprites/Enemy.cpp | 4 +- source/sprites/Player.cpp | 4 +- source/sprites/Tile.cpp | 4 +- source/sprites/Tile.h | 4 +- 20 files changed, 367 insertions(+), 260 deletions(-) create mode 100644 source/abstract/Circle.cpp create mode 100644 source/abstract/Circle.h create mode 100644 source/abstract/CollisionModel.cpp create mode 100644 source/abstract/CollisionModel.h create mode 100644 source/abstract/Rectangle.cpp create mode 100644 source/abstract/Rectangle.h diff --git a/source/World.cpp b/source/World.cpp index 2fdcb14..f7f622d 100755 --- a/source/World.cpp +++ b/source/World.cpp @@ -70,15 +70,15 @@ World::step(int elapsed) { for (auto v = mDrawables.begin(); v != mDrawables.end(); v++) { for (auto it = v->second.begin(); it != v->second.end(); it++) { auto& spriteA = *it; - sf::Vector2f speed = spriteA->getSpeed() * (elapsed / 1000.0f); if (spriteA->getDelete()) { v->second.erase(it); it--; } - // Apply movement for movable sprites. - else if (spriteA->getSpeed() != sf::Vector2f() && - !doesOverlap(spriteA, elapsed)) - spriteA->setPosition(spriteA->getPosition() + speed); + else if (spriteA->getSpeed() != sf::Vector2f()) { + sf::Vector2f speed = spriteA->getSpeed() * (elapsed / 1000.0f); + if (!doesOverlap(spriteA, elapsed)) + spriteA->setPosition(spriteA->getPosition() + speed); + } } } } @@ -97,7 +97,7 @@ World::doesOverlap(std::shared_ptr spriteA, int elapsed) { if (!spriteA->collisionEnabled(spriteB->getCategory()) || !spriteB->collisionEnabled(spriteA->getCategory())) continue; - if (testCollision(spriteA, spriteB, elapsed)) { + if (spriteA->testCollision(spriteB, elapsed)) { spriteA->onCollide(spriteB); spriteB->onCollide(spriteA); return true; @@ -128,112 +128,6 @@ World::think(int elapsed) { } } -/** - * Tests for collisions using Seperating Axis Theorem (SAT). - * - * http://www.metanetsoftware.com/technique/tutorialA.html - * - * @param spriteA, spriteB Pair of sprites which to test for collision/overlapping. - * @param elapsed Time elapsed in this step. - * @return True if both sprites will be overlapping after their current movement. - */ -bool -World::testCollision(std::shared_ptr spriteA, - std::shared_ptr spriteB, int elapsed) const { - // circle-circle collision - if ((spriteA->mShape.type == Sprite::Shape::Type::CIRCLE) && - (spriteB->mShape.type == Sprite::Shape::Type::CIRCLE)) { - sf::Vector2f axis = spriteA->getPosition() - spriteB->getPosition(); - // If both objects are at the exact same position, allow any movement for unstucking. - if (axis == sf::Vector2f()) - return true; - axis = thor::unitVector(axis); - float centerA = thor::dotProduct(axis, spriteA->getPosition()); - float radiusA = spriteA->getRadius(); - float movementA = thor::dotProduct(axis, spriteA->getSpeed() * (elapsed / 1000.0f)); - float centerB = thor::dotProduct(axis, spriteB->getPosition()); - float radiusB = spriteB->getRadius(); - float movementB = thor::dotProduct(axis, spriteB->getSpeed() * (elapsed / 1000.0f)); - - // Allow movement if sprites are moving apart. - return Interval::IntervalFromRadius(centerA, radiusA).getOverlap( - Interval::IntervalFromRadius(centerB, radiusB)).getLength() < - Interval::IntervalFromRadius(centerA + movementA, radiusA).getOverlap( - Interval::IntervalFromRadius(centerB + movementB, radiusB)).getLength(); - } - // circle-rect collision - if (((spriteA->mShape.type == Sprite::Shape::Type::CIRCLE) && - (spriteB->mShape.type == Sprite::Shape::Type::RECTANGLE)) || - ((spriteA->mShape.type == Sprite::Shape::Type::RECTANGLE) && - (spriteB->mShape.type == Sprite::Shape::Type::CIRCLE))) { - std::shared_ptr circle = spriteA; - std::shared_ptr rect = spriteB; - if (circle->mShape.type != Sprite::Shape::Type::CIRCLE) - std::swap(circle, rect); - float radius = circle->getRadius(); - sf::Vector2f halfsize = rect->getSize() / 2.0f; - sf::Vector2f circlePos = circle->getPosition(); - sf::Vector2f rectPos = rect->getPosition(); - // Only circle movement as rectangles don't move. - sf::Vector2f circleMovement = circle->getSpeed() * (elapsed / 1000.0f); - - // We assume that rectangles are always axis aligned. - float overlapNoMovementX = Interval::IntervalFromRadius(circlePos.x, radius) - .getOverlap(Interval::IntervalFromRadius(rectPos.x, halfsize.x)).getLength(); - float overlapMovementX = Interval::IntervalFromRadius(circlePos.x + circleMovement.x, radius) - .getOverlap(Interval::IntervalFromRadius(rectPos.x, halfsize.x)).getLength(); - float overlapNoMovementY = Interval::IntervalFromRadius(circlePos.y, radius) - .getOverlap(Interval::IntervalFromRadius(rectPos.y, halfsize.y)).getLength(); - float overlapMovementY = Interval::IntervalFromRadius(circlePos.y + circleMovement.y, radius) - .getOverlap(Interval::IntervalFromRadius(rectPos.y, halfsize.y)).getLength(); - - bool xyCollisionResult = (((overlapNoMovementX < overlapMovementX) && - (overlapNoMovementY > 0)) || - ((overlapNoMovementY < overlapMovementY) && (overlapNoMovementX > 0))); - // If circle center is overlapping rectangle on x or y axis, we can take xyCollisionResult. - if (Interval::IntervalFromRadius(rectPos.x, halfsize.x).isInside(circlePos.x) || - Interval::IntervalFromRadius(rectPos.y, halfsize.y).isInside(circlePos.y)) - return xyCollisionResult; - // Test if the circle is colliding with a corner of the rectangle. - else if (xyCollisionResult) { - // This is the same as circle-circle collision. - sf::Vector2f axis = circle->getPosition() - rect->getPosition(); - // If both objects are at the exact same position, allow any - // movement for unstucking. - if (axis == sf::Vector2f()) - return true; - axis = thor::unitVector(axis); - - float circlePosProjected = thor::dotProduct(axis, circlePos); - float movementProjected = thor::dotProduct(axis, circleMovement); - float rectPosProjected = thor::dotProduct(axis, rectPos); - // For corner projections, those on the same line with the rect - // center are equal by value, so we only need one on each axis - // and take the maximum. - float rectHalfWidthProjected = std::max( - abs(thor::dotProduct(axis, halfsize)), - abs(thor::dotProduct(axis, - sf::Vector2f(halfsize.x, -halfsize.y)))); - - // Allow movement if sprites are moving apart. - return Interval::IntervalFromRadius(circlePosProjected, radius) - .getOverlap(Interval::IntervalFromRadius(rectPosProjected, - rectHalfWidthProjected)) - .getLength() < - Interval::IntervalFromRadius(circlePosProjected + movementProjected, radius) - .getOverlap(Interval::IntervalFromRadius(rectPosProjected, - rectHalfWidthProjected)) - .getLength(); - } - // If there is no collision on x and y axis, there can't be one at all. - else { - return false; - } - } - // Rectangles can't move and thus not collide. - return false; -} - /** * Draws all elements in the group. */ diff --git a/source/abstract/Character.cpp b/source/abstract/Character.cpp index c8e58c2..f864862 100644 --- a/source/abstract/Character.cpp +++ b/source/abstract/Character.cpp @@ -21,9 +21,10 @@ const float Character::VISION_DISTANCE = 500.0f; /** * Saves pointer to this instance in static var for think(). */ -Character::Character(World& world, Pathfinder& pathfinder, const Data& data, - const Yaml& config) : - Sprite(data, config), +Character::Character(const sf::Vector2f& position, Category category, + unsigned short mask, const Yaml& config, World& world, + Pathfinder& pathfinder) : + Circle(position, category, mask, config), mWorld(world), mPathfinder(pathfinder), mMaxHealth(config.get(YAML_KEY::HEALTH, YAML_DEFAULT::HEALTH)), diff --git a/source/abstract/Character.h b/source/abstract/Character.h index bc583e7..02078fc 100644 --- a/source/abstract/Character.h +++ b/source/abstract/Character.h @@ -8,7 +8,7 @@ #ifndef DG_ACTOR_H_ #define DG_ACTOR_H_ -#include "Sprite.h" +#include "Circle.h" class Pathfinder; class World; @@ -18,10 +18,11 @@ class Yaml; /** * Provides think function for AI, manages health, drops body on death. */ -class Character : public Sprite { +class Character : public Circle { public: - explicit Character(World& world, Pathfinder& pathfinder, const Data& data, - const Yaml& config); + explicit Character(const sf::Vector2f& position, Category category, + unsigned short mask, const Yaml& config, World& world, + Pathfinder& pathfinder); virtual ~Character() = 0; void onDamage(int damage); diff --git a/source/abstract/Circle.cpp b/source/abstract/Circle.cpp new file mode 100644 index 0000000..92965e2 --- /dev/null +++ b/source/abstract/Circle.cpp @@ -0,0 +1,46 @@ +/* + * Circle.cpp + * + * Created on: 04.05.2013 + * Author: Felix + */ + +#include "Circle.h" + +#include "Rectangle.h" +#include "../util/Yaml.h" + +Circle::Circle(const sf::Vector2f& position, Category category, + unsigned short mask, const Yaml& config, + const sf::Vector2f& direction) : + Sprite(position, category, mask, + sf::Vector2f(config.get(YAML_KEY::RADIUS, 0.0f), + config.get(YAML_KEY::RADIUS, 0.0f)) * 2.0f, + config.get(YAML_KEY::TEXTURE, std::string()), direction) { +} + +/** + * Returns true if a collision between this and other occured. It does not + * matter which object is this or other. + */ +bool +Circle::testCollision(std::shared_ptr other, int elapsed) { + Rectangle* rect = dynamic_cast(other.get()); + Circle* circle = dynamic_cast(other.get()); + if (circle != nullptr) + return CollisionModel::testCollision(*this, *circle, elapsed); + else if (rect != nullptr) + return CollisionModel::testCollision(*this, *rect, elapsed); + else { + assert(false); + return false; + } +} + +/** + * Returns the radius of the circle used as a collision model for this object. + */ +float +Circle::getRadius() const { + return getSize().x / 2; +} diff --git a/source/abstract/Circle.h b/source/abstract/Circle.h new file mode 100644 index 0000000..0ebe2f4 --- /dev/null +++ b/source/abstract/Circle.h @@ -0,0 +1,30 @@ +/* + * Circle.h + * + * Created on: 04.05.2013 + * Author: Felix + */ + +#ifndef DG_CIRCLE_H_ +#define DG_CIRCLE_H_ + +#include "CollisionModel.h" +#include "Sprite.h" + +class Yaml; + +/** + * Shape that uses a circle as collision model. + */ +class Circle : public CollisionModel, public Sprite { +public: + explicit Circle(const sf::Vector2f& position, Category category, + unsigned short mask, const Yaml& config, + const sf::Vector2f& direction = sf::Vector2f(0, 0)); + virtual ~Circle() = default; + + bool testCollision(std::shared_ptr other, int elapsed); + float getRadius() const; +}; + +#endif /* DG_CIRCLE_H_ */ diff --git a/source/abstract/CollisionModel.cpp b/source/abstract/CollisionModel.cpp new file mode 100644 index 0000000..d9797bb --- /dev/null +++ b/source/abstract/CollisionModel.cpp @@ -0,0 +1,127 @@ +/* + * CollisionModel.cpp + * + * Created on: 07.05.2013 + * Author: Felix + */ + +#include "CollisionModel.h" + +#include + +#include + +#include "Circle.h" +#include "Rectangle.h" +#include "../util/Interval.h" + +CollisionModel::~CollisionModel() { +} +/** + * Tests for collision between rectangle and circle. + * + * @return True if a collision occured. + */ +bool +CollisionModel::testCollision(const Circle& circle, const Rectangle& rect, + int elapsed) { + sf::Vector2f halfsize = rect.getSize() / 2.0f; + sf::Vector2f circlePos = circle.getPosition(); + sf::Vector2f rectPos = rect.getPosition(); + // Only circle movement as rectangles don't move. + sf::Vector2f circleMovement = circle.getSpeed() * (elapsed / 1000.0f); + + // We assume that rectangles are always axis aligned. + float overlapNoMovementX = Interval::IntervalFromRadius(circlePos.x, circle.getRadius()) + .getOverlap(Interval::IntervalFromRadius(rectPos.x, halfsize.x)).getLength(); + float overlapMovementX = Interval::IntervalFromRadius(circlePos.x + circleMovement.x, circle.getRadius()) + .getOverlap(Interval::IntervalFromRadius(rectPos.x, halfsize.x)).getLength(); + float overlapNoMovementY = Interval::IntervalFromRadius(circlePos.y, circle.getRadius()) + .getOverlap(Interval::IntervalFromRadius(rectPos.y, halfsize.y)).getLength(); + float overlapMovementY = Interval::IntervalFromRadius(circlePos.y + circleMovement.y, circle.getRadius()) + .getOverlap(Interval::IntervalFromRadius(rectPos.y, halfsize.y)).getLength(); + + bool xyCollisionResult = (((overlapNoMovementX < overlapMovementX) && + (overlapNoMovementY > 0)) || + ((overlapNoMovementY < overlapMovementY) && (overlapNoMovementX > 0))); + // If circle center is overlapping rectangle on x or y axis, we can take xyCollisionResult. + if (Interval::IntervalFromRadius(rectPos.x, halfsize.x).isInside(circlePos.x) || + Interval::IntervalFromRadius(rectPos.y, halfsize.y).isInside(circlePos.y)) + return xyCollisionResult; + // Test if the circle is colliding with a corner of the rectangle. + else if (xyCollisionResult) { + // This is the same as circle-circle collision. + sf::Vector2f axis = circlePos - rectPos; + // If both objects are at the exact same position, allow any + // movement for unstucking. + if (axis == sf::Vector2f()) + return true; + axis = thor::unitVector(axis); + + float circlePosProjected = thor::dotProduct(axis, circlePos); + float movementProjected = thor::dotProduct(axis, circleMovement); + float rectPosProjected = thor::dotProduct(axis, rectPos); + // For corner projections, those on the same line with the rect + // center are equal by value, so we only need one on each axis + // and take the maximum. + float rectHalfWidthProjected = std::max( + abs(thor::dotProduct(axis, halfsize)), + abs(thor::dotProduct(axis, + sf::Vector2f(halfsize.x, -halfsize.y)))); + + // Allow movement if sprites are moving apart. + return Interval::IntervalFromRadius(circlePosProjected, circle.getRadius()) + .getOverlap(Interval::IntervalFromRadius(rectPosProjected, + rectHalfWidthProjected)) + .getLength() < + Interval::IntervalFromRadius(circlePosProjected + movementProjected, circle.getRadius()) + .getOverlap(Interval::IntervalFromRadius(rectPosProjected, + rectHalfWidthProjected)) + .getLength(); + } + // If there is no collision on x and y axis, there can't be one at all. + else { + return false; + } + +} + +/** + * Tests for collision between two circles. + * + * @return True if a collision occured. + */ +bool +CollisionModel::testCollision(const Circle& first, const Circle& second, + int elapsed) { + sf::Vector2f axis = first.getPosition() - second.getPosition(); + // If both objects are at the exact same position, allow any movement for unstucking. + if (axis == sf::Vector2f()) + return true; + axis = thor::unitVector(axis); + float centerA = thor::dotProduct(axis, first.getPosition()); + float radiusA = first.getRadius(); + float movementA = thor::dotProduct(axis, first.getSpeed() * (elapsed / 1000.0f)); + float centerB = thor::dotProduct(axis, second.getPosition()); + float radiusB = second.getRadius(); + float movementB = thor::dotProduct(axis, second.getSpeed() * (elapsed / 1000.0f)); + + // Allow movement if sprites are moving apart. + return Interval::IntervalFromRadius(centerA, radiusA).getOverlap( + Interval::IntervalFromRadius(centerB, radiusB)).getLength() < + Interval::IntervalFromRadius(centerA + movementA, radiusA).getOverlap( + Interval::IntervalFromRadius(centerB + movementB, radiusB)).getLength(); + +} + +/** + * Tests for collision between two rectangles. Not implemented as these can't + * occur (rectangles can't move). + * + * @return True if a collision occured. + */ +bool +CollisionModel::testCollision(const Rectangle& first, const Rectangle& second, + int elapsed) { + return false; +} diff --git a/source/abstract/CollisionModel.h b/source/abstract/CollisionModel.h new file mode 100644 index 0000000..9af6c96 --- /dev/null +++ b/source/abstract/CollisionModel.h @@ -0,0 +1,29 @@ +/* + * CollisionModel.h + * + * Created on: 07.05.2013 + * Author: Felix + */ + +#ifndef DG_COLLISIONMODEL_H_ +#define DG_COLLISIONMODEL_H_ + +class Circle; +class Rectangle; + +/** + * Abstract class providing helper functions to test for collisions between shapes. + */ +class CollisionModel { +public: + virtual ~CollisionModel() = 0; + + static bool testCollision(const Circle& circle, const Rectangle& rect, + int elapsed); + static bool testCollision(const Circle& first, const Circle& second, + int elapsed); + static bool testCollision(const Rectangle& first, const Rectangle& second, + int elapsed); +}; + +#endif /* DG_COLLISIONMODEL_H_ */ diff --git a/source/abstract/Rectangle.cpp b/source/abstract/Rectangle.cpp new file mode 100644 index 0000000..9d6649e --- /dev/null +++ b/source/abstract/Rectangle.cpp @@ -0,0 +1,37 @@ +/* + * Rectangle.cpp + * + * Created on: 04.05.2013 + * Author: Felix + */ + +#include "Rectangle.h" + +#include "Circle.h" +#include "../util/Yaml.h" + +Rectangle::Rectangle(const sf::Vector2f& position, Category category, + unsigned short mask, const Yaml& config, + const sf::Vector2f& direction) : + Sprite(position, category, mask, config.get(YAML_KEY::SIZE, sf::Vector2f()), + config.get(YAML_KEY::TEXTURE, std::string()), direction) { +} + +/** + * Returns true if a collision between this and other occured. It does not + * matter which object is this or other. + */ +bool +Rectangle::testCollision(std::shared_ptr other, int elapsed) { + Rectangle* rect = dynamic_cast(other.get()); + Circle* circle = dynamic_cast(other.get()); + if (circle != nullptr) + return CollisionModel::testCollision(*circle, *this, elapsed); + else if (rect != nullptr) + return CollisionModel::testCollision(*rect, *this, elapsed); + else { + assert(false); + return false; + } +} + diff --git a/source/abstract/Rectangle.h b/source/abstract/Rectangle.h new file mode 100644 index 0000000..6baa8aa --- /dev/null +++ b/source/abstract/Rectangle.h @@ -0,0 +1,29 @@ +/* + * Rectangle.h + * + * Created on: 04.05.2013 + * Author: Felix + */ + +#ifndef DG_RECTANGLE_H_ +#define DG_RECTANGLE_H_ + +#include "CollisionModel.h" +#include "Sprite.h" + +class Yaml; + +/** + * Shape that uses an axis aligned Rectangle as a collision model. + */ +class Rectangle : public CollisionModel, public Sprite { +public: + explicit Rectangle(const sf::Vector2f& position, Category category, + unsigned short mask, const Yaml& config, + const sf::Vector2f& direction = sf::Vector2f(0, 0)); + virtual ~Rectangle() = default; + + bool testCollision(std::shared_ptr other, int elapsed); +}; + +#endif /* DG_RECTANGLE_H_ */ diff --git a/source/abstract/Sprite.cpp b/source/abstract/Sprite.cpp index 18c5d95..b122828 100755 --- a/source/abstract/Sprite.cpp +++ b/source/abstract/Sprite.cpp @@ -12,78 +12,27 @@ #include "../util/Loader.h" #include "../util/Log.h" #include "../util/ResourceManager.h" -#include "../util/Yaml.h" -/** - * Initializes sprite data. - * - * @param data Container holding construction parameters. - * @param config Additional construction parameters - */ -Sprite::Sprite(const Data& data, const Yaml& config) : - mCategory(data.category), - mMask(data.mask), - mDelete(false) { - // Init shape - float radius = config.get(YAML_KEY::RADIUS, 0.0f); - sf::Vector2f size = config.get(YAML_KEY::SIZE, sf::Vector2f()); - if (radius != 0.0f) { - mShape.type = Shape::Type::CIRCLE; - mShape.shape = std::unique_ptr(new sf::CircleShape(radius)); - mShape.shape->setOrigin(radius, radius); - mShape.shape->setTextureRect(sf::IntRect(sf::Vector2i(0, 0), - sf::Vector2i(radius * 2, radius * 2))); +Sprite::Sprite(const sf::Vector2f& position, Category category, + unsigned short mask, const sf::Vector2f& size, + const std::string& texture, const sf::Vector2f& direction) : + mCategory(category), + mMask(mask), + mDelete(false) { + mShape.setSize(size); + mShape.setOrigin(size / 2.0f); + mShape.setTextureRect(sf::IntRect(sf::Vector2i(), sf::Vector2i(size))); + setPosition(position); + setDirection(direction); + try { + mTexture = ResourceManager::i().acquire(Loader::i() + .fromFile(texture)); + mShape.setTexture(&*mTexture, false); } - else if (size == sf::Vector2f()) { - LOG_E("Failed to read size or radius from " << config.getFilename() << - ", using texture size."); - size = sf::Vector2f(mTexture->getSize()); + catch (thor::ResourceLoadingException&) { + LOG_W("Failed to load texture " << texture << ", coloring red."); + mShape.setFillColor(sf::Color(255, 0, 0)); } - else if (size != sf::Vector2f()) { - mShape.type = Shape::Type::RECTANGLE; - mShape.shape = std::unique_ptr(new sf::RectangleShape(size)); - mShape.shape->setOrigin(size / 2.0f); - mShape.shape->setTextureRect(sf::IntRect(sf::Vector2i(0, 0), sf::Vector2i(size))); - } - - // Init texture - std::string texture = config.get(YAML_KEY::TEXTURE, ""); - if (texture != "") { - try { - mTexture = ResourceManager::i().acquire(Loader::i() - .fromFile(texture)); - mShape.shape->setTexture(&*mTexture, false); - } - catch (thor::ResourceLoadingException&) { - LOG_W("Failed to load texture " << texture << ", coloring red."); - mShape.shape->setFillColor(sf::Color(255, 0, 0)); - } - } - else { - LOG_W("Failed to read texture file name from YAML file " << - config.getFilename() << ", coloring red."); - mShape.shape->setFillColor(sf::Color(255, 0, 0)); - } - - setPosition(data.position); - setDirection(data.direction); -} - -/** - * Used to make this class pure virtual without any pure virtual function. - */ -Sprite::~Sprite() { -} - -/** - * Initializes container. - */ -Sprite::Data::Data(const sf::Vector2f& position, Category category, - unsigned short mask, const sf::Vector2f& direction) : - position(position), - direction(direction), - category(category), - mask(mask) { } /** @@ -91,7 +40,7 @@ Sprite::Data::Data(const sf::Vector2f& position, Category category, */ sf::Vector2f Sprite::getPosition() const { - return mShape.shape->getPosition(); + return mShape.getPosition(); } /** @@ -107,7 +56,7 @@ Sprite::getSpeed() const { */ sf::Vector2f Sprite::getDirection() const { - return thor::rotatedVector(sf::Vector2f(1, 0), mShape.shape->getRotation()); + return thor::rotatedVector(sf::Vector2f(1, 0), mShape.getRotation()); } /** @@ -132,13 +81,13 @@ Sprite::getCategory() const { */ sf::Vector2f Sprite::getSize() const { - sf::FloatRect bounds = mShape.shape->getLocalBounds(); + sf::FloatRect bounds = mShape.getLocalBounds(); return sf::Vector2f(bounds.width, bounds.height); } void Sprite::draw(sf::RenderTarget& target, sf::RenderStates states) const { - target.draw(*mShape.shape, states); + target.draw(mShape, states); } /** @@ -151,7 +100,7 @@ Sprite::collisionEnabled(Category category) const { bool Sprite::isInside(const sf::FloatRect& rect) const { - return rect.intersects(mShape.shape->getGlobalBounds()); + return rect.intersects(mShape.getGlobalBounds()); } /** * Called when a collision with another Sprite occured. Override this method @@ -187,7 +136,7 @@ Sprite::setSpeed(sf::Vector2f direction, float speed) { void Sprite::setDirection(const sf::Vector2f& direction) { if (direction != sf::Vector2f()) - mShape.shape->setRotation(thor::polarAngle(direction) + 90); + mShape.setRotation(thor::polarAngle(direction) + 90); } /** @@ -195,18 +144,5 @@ Sprite::setDirection(const sf::Vector2f& direction) { */ void Sprite::setPosition(const sf::Vector2f& position) { - mShape.shape->setPosition(position); -} - -/** - * Returns the radius of this sprite. Will fail if this is not a circle. - * - * @return The radius of this sprite. - */ -float -Sprite::getRadius() const { - std::shared_ptr circleShape = - std::dynamic_pointer_cast(mShape.shape); - assert(circleShape); - return circleShape->getRadius(); + mShape.setPosition(position); } diff --git a/source/abstract/Sprite.h b/source/abstract/Sprite.h index 6d3450b..e2d86b8 100755 --- a/source/abstract/Sprite.h +++ b/source/abstract/Sprite.h @@ -12,8 +12,6 @@ #include -class Yaml; - /** * An sprite that is rendered in the world. */ @@ -30,21 +28,6 @@ public: CATEGORY_ACTOR = 1 << 4 }; - /** - * Container that carries all data required to construct an object of - * this class. - */ - class Data { - public: - explicit Data(const sf::Vector2f& position, - Category category, unsigned short mask, - const sf::Vector2f& direction = sf::Vector2f(0, 0)); - const sf::Vector2f& position; - const sf::Vector2f& direction; - Category category; - unsigned short mask; - }; - /** * Common collision masking values. */ @@ -55,8 +38,10 @@ public: // Public functions. public: - explicit Sprite(const Data& data, const Yaml& config); - virtual ~Sprite() = 0; + explicit Sprite(const sf::Vector2f& position, Category category, + unsigned short mask, const sf::Vector2f& size, + const std::string& texture, const sf::Vector2f& direction); + virtual ~Sprite() = default; sf::Vector2f getPosition() const; sf::Vector2f getSpeed() const; @@ -68,6 +53,7 @@ public: bool collisionEnabled(Category category) const; bool isInside(const sf::FloatRect& rect) const; + virtual bool testCollision(std::shared_ptr other, int elapsed) = 0; virtual void onCollide(std::shared_ptr other); protected: @@ -75,24 +61,11 @@ protected: void setSpeed(sf::Vector2f direction, float speed); void setDirection(const sf::Vector2f& direction); void setPosition(const sf::Vector2f& position); - float getRadius() const; - -private: - class Shape { - public: - enum class Type { - CIRCLE, - RECTANGLE - }; - - Type type; - std::shared_ptr shape; - }; private: friend class World; - Shape mShape; + sf::RectangleShape mShape; std::shared_ptr mTexture; sf::Vector2f mSpeed; Category mCategory; diff --git a/source/effects/Bullet.cpp b/source/effects/Bullet.cpp index 6c3212f..a87cf97 100755 --- a/source/effects/Bullet.cpp +++ b/source/effects/Bullet.cpp @@ -22,12 +22,12 @@ */ Bullet::Bullet(const sf::Vector2f& position, Sprite& shooter, sf::Vector2f direction, const Yaml& config) : - Particle(config, Data(position, CATEGORY_PARTICLE, ~CATEGORY_PARTICLE, - thor::rotatedVector(direction, -90.0f))), + Particle(position, CATEGORY_PARTICLE, ~CATEGORY_PARTICLE, + config, thor::rotatedVector(direction, -90.0f)), mShooter(shooter), mDamage(config.get(YAML_KEY::DAMAGE, YAML_DEFAULT::DAMAGE)), mSpeed(config.get(YAML_KEY::SPEED, YAML_DEFAULT::SPEED)) { - setSpeed(direction, mSpeed); + setSpeed(thor::rotatedVector(direction, -90.0f), mSpeed); } /** diff --git a/source/particle/Particle.cpp b/source/particle/Particle.cpp index 5809d19..09db468 100755 --- a/source/particle/Particle.cpp +++ b/source/particle/Particle.cpp @@ -7,8 +7,10 @@ #include "Particle.h" -Particle::Particle(const Yaml& config, const Data& data) : - Sprite(data, config) { +Particle::Particle(const sf::Vector2f& position, Category category, + unsigned short mask, const Yaml& config, + const sf::Vector2f& direction) : + Circle(position, category, mask, config, direction) { } Particle::~Particle() { diff --git a/source/particle/Particle.h b/source/particle/Particle.h index 1a91b72..d22e071 100755 --- a/source/particle/Particle.h +++ b/source/particle/Particle.h @@ -8,16 +8,18 @@ #ifndef DG_PARTICLE_H_ #define DG_PARTICLE_H_ -#include "../abstract/Sprite.h" +#include "../abstract/Circle.h" class Yaml; /** * Prototype for a particle. */ -class Particle : public Sprite { +class Particle : public Circle { public: - explicit Particle(const Yaml& config, const Data& data); + explicit Particle(const sf::Vector2f& position, Category category, + unsigned short mask, const Yaml& config, + const sf::Vector2f& direction); virtual ~Particle(); }; diff --git a/source/sprites/Corpse.cpp b/source/sprites/Corpse.cpp index 5a6505d..6710d1e 100644 --- a/source/sprites/Corpse.cpp +++ b/source/sprites/Corpse.cpp @@ -12,6 +12,6 @@ const std::string Corpse::CONFIG = "corpse.yaml"; Corpse::Corpse(const sf::Vector2f& position) : - Sprite(Data(position, CATEGORY_NONSOLID, MASK_NONE), Yaml(CONFIG)) { + Circle(position, CATEGORY_NONSOLID, MASK_NONE, Yaml(CONFIG)) { } diff --git a/source/sprites/Corpse.h b/source/sprites/Corpse.h index 5435dd1..a11b260 100644 --- a/source/sprites/Corpse.h +++ b/source/sprites/Corpse.h @@ -8,9 +8,9 @@ #ifndef DG_CORPSE_H_ #define DG_CORPSE_H_ -#include "../abstract/Sprite.h" +#include "../abstract/Circle.h" -class Corpse : public Sprite { +class Corpse : public Circle { public: explicit Corpse(const sf::Vector2f& position); diff --git a/source/sprites/Enemy.cpp b/source/sprites/Enemy.cpp index 173000b..e3653ae 100644 --- a/source/sprites/Enemy.cpp +++ b/source/sprites/Enemy.cpp @@ -15,8 +15,8 @@ const std::string Enemy::CONFIG = "enemy.yaml"; Enemy::Enemy(World& world, Pathfinder& pathfinder, const sf::Vector2f& position) : - Character(world, pathfinder, Data(position, CATEGORY_ACTOR, MASK_ALL), - Yaml(CONFIG)) { + Character(position, CATEGORY_ACTOR, MASK_ALL, Yaml(CONFIG), world, + pathfinder) { } void diff --git a/source/sprites/Player.cpp b/source/sprites/Player.cpp index 3de5249..4ce3b33 100644 --- a/source/sprites/Player.cpp +++ b/source/sprites/Player.cpp @@ -18,8 +18,8 @@ const std::string Player::CONFIG = "player.yaml"; */ Player::Player(World& world, Pathfinder& pathfinder, const sf::Vector2f& position) : - Character(world, pathfinder, - Data(position, CATEGORY_ACTOR, MASK_ALL), Yaml(CONFIG)), + Character(position, CATEGORY_ACTOR, MASK_ALL, Yaml(CONFIG), world, + pathfinder), mDirection(0) { } diff --git a/source/sprites/Tile.cpp b/source/sprites/Tile.cpp index 1f4b05f..f5d30d7 100644 --- a/source/sprites/Tile.cpp +++ b/source/sprites/Tile.cpp @@ -24,8 +24,8 @@ const sf::Vector2i Tile::TILE_SIZE = sf::Vector2i(75, 75); * @param world Box2D world object. */ Tile::Tile(Type type, int x, int y) : - Sprite(Data(sf::Vector2f(x * TILE_SIZE.x, y * TILE_SIZE.y), - CATEGORY_WORLD, (isSolid(type)) ? 0xffff : 0), + Rectangle(sf::Vector2f(x * TILE_SIZE.x, y * TILE_SIZE.y), + CATEGORY_WORLD, (isSolid(type)) ? 0xffff : 0, Yaml(getConfig(type))), mType(type) { } diff --git a/source/sprites/Tile.h b/source/sprites/Tile.h index bd8b56c..262641f 100644 --- a/source/sprites/Tile.h +++ b/source/sprites/Tile.h @@ -8,12 +8,12 @@ #ifndef DG_TILE_H_ #define DG_TILE_H_ -#include "../abstract/Sprite.h" +#include "../abstract/Rectangle.h" /** * Holds information about a single tile. */ -class Tile : public Sprite { +class Tile : public Rectangle { public: enum class Type : char { FLOOR,