I am pretty new to SFML, any help I can get with this issue would be greatly appreciated. In game.cpp I am trying to perform a collision test on line 261 between my background sprite and my player sprite(currently does not exist) using the collision.cpp PixelPerfectTest() method. But the way I'm loading in my sprite sheet using the Animation class which is essentially a std::vector of texture rectangles (sf::IntRect) and a sf::Texture reference. So you have to provide a spritesheet and push back your texture reactangles. Then you create a AnimatedSprite object and provide it with an Animation. The AnimatedSprite class inherits from both sf::Drawable and sf::Transfomable, so it behaves much like a regular sprite. With out ever creating a Sprite object. I need to be able to load the current frame that is being used and load into a sprite obj then use that sprite for my collision test.
Thanks,
game.cpp
#include "iostream"
#include <SFML/Graphics.hpp>
//#include "Player.h"
#include "Animation.h"
#include "AnimatedSprite.h"
//#include "TextureHolder.h"
#include "Collision.h"
using namespace sf;
using namespace std;
using namespace Collision;
int main()
{
//******************************************************
//game screen & resolution & View
sf::Vector2i screenDimensions(1920, 1080);
sf::RenderWindow window(sf::VideoMode(screenDimensions.x, screenDimensions.y), "Lost In the Rocks!");
window.setFramerateLimit(60);
//******************************************************
//game states
//the game will always be on one of the four states
enum class State
{
PAUSED, LEVELING_UP, GAME_OVER, PLAYING
};
//starts game with GAME_OVER State
State state = State::PLAYING;
//******************************************************
//clock & Time
//clock object to store time
Clock frameClock;
//time object to store how long the PLAYING state have been active
Time gameTimeTotal;
//******************************************************
//Starting background Objects, Variables & Images
//loads cave1 texture into gpu one time
Texture start1Layer1;
start1Layer1.loadFromFile("graphics/Start1Layer1.png");
Texture start1Layer2;
//start1Layer2.loadFromFile("graphics/Start1Layer2.png");
Texture start1Layer3;
//start1Layer3.loadFromFile("graphics/Start1Layer3.png");
//loads image into collision method and creates bitmask
CreateTextureAndBitmask(start1Layer2, "graphics/Start1Layer2.png");
CreateTextureAndBitmask(start1Layer3, "graphics/Start1Layer3.png");
//creates sprite obj to store each texure
Sprite spriteStart1L1;
Sprite spriteStart1L2;
Sprite spriteStart1L3;
//loads each image into each sprite obj
spriteStart1L1.setTexture(start1Layer1);
spriteStart1L2.setTexture(start1Layer2);
spriteStart1L3.setTexture(start1Layer3);
//sets starting point for each sprite
spriteStart1L1.setPosition(25, 25);
spriteStart1L2.setPosition(25, 25);
spriteStart1L3.setPosition(25, 25);
//******************************************************
//Player Objects, Variables & Images
Texture tplayer;
CreateTextureAndBitmask(tplayer, "graphics/Player1.png");
//walking up frames
Animation walkingAnimationUp;
walkingAnimationUp.setSpriteSheet(tplayer);
walkingAnimationUp.addFrame(sf::IntRect(0, 518, 64, 63));
walkingAnimationUp.addFrame(sf::IntRect(63, 518, 64, 63));
walkingAnimationUp.addFrame(sf::IntRect(126, 518, 64, 63));
walkingAnimationUp.addFrame(sf::IntRect(189, 518, 64, 63));
walkingAnimationUp.addFrame(sf::IntRect(252, 518, 64, 63));
walkingAnimationUp.addFrame(sf::IntRect(315, 518, 64, 63));
walkingAnimationUp.addFrame(sf::IntRect(378, 518, 64, 63));
walkingAnimationUp.addFrame(sf::IntRect(441, 518, 64, 63));
walkingAnimationUp.addFrame(sf::IntRect(504, 518, 64, 63));
//walking down frames
Animation walkingAnimationDown;
walkingAnimationDown.setSpriteSheet(tplayer);
walkingAnimationDown.addFrame(sf::IntRect(0, 646, 64, 63));
walkingAnimationDown.addFrame(sf::IntRect(63, 646, 64, 63));
walkingAnimationDown.addFrame(sf::IntRect(126, 646, 64, 63));
walkingAnimationDown.addFrame(sf::IntRect(189, 646, 64, 63));
walkingAnimationDown.addFrame(sf::IntRect(252, 646, 64, 63));
walkingAnimationDown.addFrame(sf::IntRect(315, 646, 64, 63));
walkingAnimationDown.addFrame(sf::IntRect(378, 646, 64, 63));
walkingAnimationDown.addFrame(sf::IntRect(441, 646, 64, 63));
walkingAnimationDown.addFrame(sf::IntRect(504, 646, 64, 63));
//walking left frames
Animation walkingAnimationLeft;
walkingAnimationLeft.setSpriteSheet(tplayer);
walkingAnimationLeft.addFrame(sf::IntRect(0, 582, 64, 63));
walkingAnimationLeft.addFrame(sf::IntRect(63, 582, 64, 63));
walkingAnimationLeft.addFrame(sf::IntRect(126, 582, 64, 63));
walkingAnimationLeft.addFrame(sf::IntRect(189, 582, 64, 63));
walkingAnimationLeft.addFrame(sf::IntRect(252, 582, 64, 63));
walkingAnimationLeft.addFrame(sf::IntRect(315, 582, 64, 63));
walkingAnimationLeft.addFrame(sf::IntRect(378, 582, 64, 63));
walkingAnimationLeft.addFrame(sf::IntRect(441, 582, 64, 63));
walkingAnimationLeft.addFrame(sf::IntRect(504, 582, 64, 63));
//walking right frames
Animation walkingAnimationRight;
walkingAnimationRight.setSpriteSheet(tplayer);
walkingAnimationRight.addFrame(sf::IntRect(0, 710, 64, 63));
walkingAnimationRight.addFrame(sf::IntRect(63, 710, 64, 63));
walkingAnimationRight.addFrame(sf::IntRect(126, 710, 64, 63));
walkingAnimationRight.addFrame(sf::IntRect(189, 710, 64, 63));
walkingAnimationRight.addFrame(sf::IntRect(252, 710, 64, 63));
walkingAnimationRight.addFrame(sf::IntRect(315, 710, 64, 63));
walkingAnimationRight.addFrame(sf::IntRect(378, 710, 64, 63));
walkingAnimationRight.addFrame(sf::IntRect(441, 710, 64, 63));
walkingAnimationRight.addFrame(sf::IntRect(504, 710, 64, 63));
//attacking lefet with sword frames
Animation AttackingAnimationRight;
AttackingAnimationRight.setSpriteSheet(tplayer);
AttackingAnimationRight.addFrame(sf::IntRect(0, 1926, 192, 186));
AttackingAnimationRight.addFrame(sf::IntRect(192, 1926, 192, 186));
AttackingAnimationRight.addFrame(sf::IntRect(384, 1926, 192, 186));
AttackingAnimationRight.addFrame(sf::IntRect(576, 1926, 192, 186));
AttackingAnimationRight.addFrame(sf::IntRect(768, 1926, 192, 186));
AttackingAnimationRight.addFrame(sf::IntRect(960, 1926, 192, 186));
Animation* currentAnimation = &walkingAnimationUp;
// set up AnimatedSprite
AnimatedSprite animatedSprite(sf::seconds(0.2), true, false);
animatedSprite.setPosition(sf::Vector2f(screenDimensions / 2));
float speed = 100.f;
bool noKeyWasPressed = true;
int CurrentImage = 0;
///////////////////////////////////////////////////////////////////////////////////////////////////
//Game Loop
while (window.isOpen())
{
//*********************************************************************************************
//player Input
//event object
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::KeyPressed)
{
if (event.key.code == Keyboard::Escape)
{
window.close();
}
}
}
Vector2f movement(0.f, 0.f);
sf::Time frameTime = frameClock.restart();
if (state == State::PLAYING)
{
//if statments for pressing and releasing (WASD) Keys
if (Keyboard::isKeyPressed(Keyboard::W))
{
currentAnimation = &walkingAnimationUp;
movement.y -= speed;
noKeyWasPressed = false;
CurrentImage = 1;
}
if (Keyboard::isKeyPressed(Keyboard::S))
{
currentAnimation = &walkingAnimationDown;
movement.y += speed;
noKeyWasPressed = false;
CurrentImage = 2;
}
if (Keyboard::isKeyPressed(Keyboard::D))
{
currentAnimation = &walkingAnimationRight;
movement.x += speed;
noKeyWasPressed = false;
CurrentImage = 3;
}
if (Keyboard::isKeyPressed(Keyboard::Num1) && CurrentImage == 3)
{
currentAnimation = &AttackingAnimationRight;
movement.x += 0.1f;
noKeyWasPressed = false;
}
if (Keyboard::isKeyPressed(Keyboard::A))
{
currentAnimation = &walkingAnimationLeft;
movement.x -= speed;
noKeyWasPressed = false;
CurrentImage = 4;
}
animatedSprite.play(*currentAnimation);
animatedSprite.move(movement * frameTime.asSeconds());
// if no key was pressed stop the animation
if (noKeyWasPressed)
{
animatedSprite.stop();
}
noKeyWasPressed = true;
}//end if State playing:: Player Input
//*********************************************************************************************
//updating the game/frame
//collision tests
if (state == State::PLAYING)
{
if (PixelPerfectTest(/*CurrentSpriteImageGoesHere*/, spriteStart1L2))
{
std::cout << "collision";
}
// update AnimatedSprite
animatedSprite.update(frameTime);
}
//*********************************************************************************************
//DRAW the Game
if (state == State::PLAYING)
{
//clears the previus frame
window.clear();
//updates player with time for movement and collision
//window.setView(mainView);
//draws everything anew.
window.draw(spriteStart1L1);
window.draw(spriteStart1L2);
window.draw(spriteStart1L3);
//draws the player
window.draw(animatedSprite);
}
window.display();
}
return 0;
}
AnimatedSprite.cpp
#include "AnimatedSprite.h"
AnimatedSprite::AnimatedSprite(sf::Time frameTime, bool paused, bool looped) :
m_animation(NULL), m_frameTime(frameTime), m_currentFrame(0), m_isPaused(paused), m_isLooped(looped), m_texture(NULL)
{
}
void AnimatedSprite::setAnimation(const Animation& animation)
{
m_animation = &animation;
m_texture = m_animation->getSpriteSheet();
m_currentFrame = 0;
setFrame(m_currentFrame);
}
void AnimatedSprite::setFrameTime(sf::Time time)
{
m_frameTime = time;
}
void AnimatedSprite::play()
{
m_isPaused = false;
}
void AnimatedSprite::play(const Animation& animation)
{
if (getAnimation() != &animation)
setAnimation(animation);
play();
}
void AnimatedSprite::pause()
{
m_isPaused = true;
}
void AnimatedSprite::stop()
{
m_isPaused = true;
m_currentFrame = 0;
setFrame(m_currentFrame);
}
void AnimatedSprite::setLooped(bool looped)
{
m_isLooped = looped;
}
void AnimatedSprite::setColor(const sf::Color& color)
{
// Update the vertices' color
m_vertices[0].color = color;
m_vertices[1].color = color;
m_vertices[2].color = color;
m_vertices[3].color = color;
}
const Animation* AnimatedSprite::getAnimation() const
{
return m_animation;
}
sf::FloatRect AnimatedSprite::getLocalBounds() const
{
sf::IntRect rect = m_animation->getFrame(m_currentFrame);
float width = static_cast<float>(std::abs(rect.width));
float height = static_cast<float>(std::abs(rect.height));
return sf::FloatRect(0.f, 0.f, width, height);
}
sf::FloatRect AnimatedSprite::getGlobalBounds() const
{
return getTransform().transformRect(getLocalBounds());
}
bool AnimatedSprite::isLooped() const
{
return m_isLooped;
}
bool AnimatedSprite::isPlaying() const
{
return !m_isPaused;
}
sf::Time AnimatedSprite::getFrameTime() const
{
return m_frameTime;
}
void AnimatedSprite::setFrame(std::size_t newFrame, bool resetTime)
{
if (m_animation)
{
//calculate new vertex positions and texture coordiantes
sf::IntRect rect = m_animation->getFrame(newFrame);
m_vertices[0].position = sf::Vector2f(0.f, 0.f);
m_vertices[1].position = sf::Vector2f(0.f, static_cast<float>(rect.height));
m_vertices[2].position = sf::Vector2f(static_cast<float>(rect.width), static_cast<float>(rect.height));
m_vertices[3].position = sf::Vector2f(static_cast<float>(rect.width), 0.f);
float left = static_cast<float>(rect.left) + 0.0001f;
float right = left + static_cast<float>(rect.width);
float top = static_cast<float>(rect.top);
float bottom = top + static_cast<float>(rect.height);
m_vertices[0].texCoords = sf::Vector2f(left, top);
m_vertices[1].texCoords = sf::Vector2f(left, bottom);
m_vertices[2].texCoords = sf::Vector2f(right, bottom);
m_vertices[3].texCoords = sf::Vector2f(right, top);
}
if (resetTime)
m_currentTime = sf::Time::Zero;
}
void AnimatedSprite::update(sf::Time deltaTime)
{
// if not paused and we have a valid animation
if (!m_isPaused && m_animation)
{
// add delta time
m_currentTime += deltaTime;
// if current time is bigger then the frame time advance one frame
if (m_currentTime >= m_frameTime)
{
// reset time, but keep the remainder
m_currentTime = sf::microseconds(m_currentTime.asMicroseconds() % m_frameTime.asMicroseconds());
// get next Frame index
if (m_currentFrame + 1 < m_animation->getSize())
m_currentFrame++;
else
{
// animation has ended
if (m_isLooped)
{
m_isPaused = true;
}
else
{
m_currentFrame = 0; // reset to start
}
}
// set the current frame, not reseting the time
setFrame(m_currentFrame, false);
}
}
}
void AnimatedSprite::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
if (m_animation && m_texture)
{
states.transform *= getTransform();
states.texture = m_texture;
target.draw(m_vertices, 4, sf::Quads, states);
}
}
Animation.cpp
#include "Animation.h"
Animation::Animation() : m_texture(NULL)
{
}
void Animation::addFrame(sf::IntRect rect)
{
m_frames.push_back(rect);
}
void Animation::setSpriteSheet(const sf::Texture& texture)
{
m_texture = &texture;
}
const sf::Texture* Animation::getSpriteSheet() const
{
return m_texture;
}
std::size_t Animation::getSize() const
{
return m_frames.size();
}
const sf::IntRect& Animation::getFrame(std::size_t n) const
{
return m_frames[n];
}
Collision.cpp
#include <SFML/Graphics.hpp>
#include <map>
#include "Collision.h"
namespace Collision
{
class BitmaskManager
{
public:
~BitmaskManager() {
std::map<const sf::Texture*, sf::Uint8*>::const_iterator end = Bitmasks.end();
for (std::map<const sf::Texture*, sf::Uint8*>::const_iterator iter = Bitmasks.begin(); iter != end; iter++)
delete[] iter->second;
}
sf::Uint8 GetPixel(const sf::Uint8* mask, const sf::Texture* tex, unsigned int x, unsigned int y) {
if (x > tex->getSize().x || y > tex->getSize().y)
return 0;
return mask[x + y * tex->getSize().x];
}
sf::Uint8* GetMask(const sf::Texture* tex) {
sf::Uint8* mask;
std::map<const sf::Texture*, sf::Uint8*>::iterator pair = Bitmasks.find(tex);
if (pair == Bitmasks.end())
{
sf::Image img = tex->copyToImage();
mask = CreateMask(tex, img);
}
else
mask = pair->second;
return mask;
}
sf::Uint8* CreateMask(const sf::Texture* tex, const sf::Image& img) {
sf::Uint8* mask = new sf::Uint8[tex->getSize().y * tex->getSize().x];
for (unsigned int y = 0; y < tex->getSize().y; y++)
{
for (unsigned int x = 0; x < tex->getSize().x; x++)
mask[x + y * tex->getSize().x] = img.getPixel(x, y).a;
}
Bitmasks.insert(std::pair<const sf::Texture*, sf::Uint8*>(tex, mask));
return mask;
}
private:
std::map<const sf::Texture*, sf::Uint8*> Bitmasks;
};
BitmaskManager Bitmasks;
bool PixelPerfectTest(const sf::Sprite& Object1, const sf::Sprite& Object2, sf::Uint8 AlphaLimit) {
sf::FloatRect Intersection;
if (Object1.getGlobalBounds().intersects(Object2.getGlobalBounds(), Intersection)) {
sf::IntRect O1SubRect = Object1.getTextureRect();
sf::IntRect O2SubRect = Object2.getTextureRect();
sf::Uint8* mask1 = Bitmasks.GetMask(Object1.getTexture());
sf::Uint8* mask2 = Bitmasks.GetMask(Object2.getTexture());
// Loop through our pixels
for (int i = Intersection.left; i < Intersection.left + Intersection.width; i++) {
for (int j = Intersection.top; j < Intersection.top + Intersection.height; j++) {
sf::Vector2f o1v = Object1.getInverseTransform().transformPoint(i, j);
sf::Vector2f o2v = Object2.getInverseTransform().transformPoint(i, j);
// Make sure pixels fall within the sprite's subrect
if (o1v.x > 0 && o1v.y > 0 && o2v.x > 0 && o2v.y > 0 &&
o1v.x < O1SubRect.width && o1v.y < O1SubRect.height &&
o2v.x < O2SubRect.width && o2v.y < O2SubRect.height) {
if (Bitmasks.GetPixel(mask1, Object1.getTexture(), (int)(o1v.x) + O1SubRect.left, (int)(o1v.y) + O1SubRect.top) > AlphaLimit &&
Bitmasks.GetPixel(mask2, Object2.getTexture(), (int)(o2v.x) + O2SubRect.left, (int)(o2v.y) + O2SubRect.top) > AlphaLimit)
return true;
}
}
}
}
return false;
}
bool CreateTextureAndBitmask(sf::Texture& LoadInto, const std::string& Filename)
{
sf::Image img;
if (!img.loadFromFile(Filename))
return false;
if (!LoadInto.loadFromImage(img))
return false;
Bitmasks.CreateMask(&LoadInto, img);
return true;
}
sf::Vector2f GetSpriteCenter(const sf::Sprite& Object)
{
sf::FloatRect AABB = Object.getGlobalBounds();
return sf::Vector2f(AABB.left + AABB.width / 2.f, AABB.top + AABB.height / 2.f);
}
sf::Vector2f GetSpriteSize(const sf::Sprite& Object)
{
sf::IntRect OriginalSize = Object.getTextureRect();
sf::Vector2f Scale = Object.getScale();
return sf::Vector2f(OriginalSize.width * Scale.x, OriginalSize.height * Scale.y);
}
bool CircleTest(const sf::Sprite& Object1, const sf::Sprite& Object2) {
sf::Vector2f Obj1Size = GetSpriteSize(Object1);
sf::Vector2f Obj2Size = GetSpriteSize(Object2);
float Radius1 = (Obj1Size.x + Obj1Size.y) / 4;
float Radius2 = (Obj2Size.x + Obj2Size.y) / 4;
sf::Vector2f Distance = GetSpriteCenter(Object1) - GetSpriteCenter(Object2);
return (Distance.x * Distance.x + Distance.y * Distance.y <= (Radius1 + Radius2) * (Radius1 + Radius2));
}
class OrientedBoundingBox // Used in the BoundingBoxTest
{
public:
OrientedBoundingBox(const sf::Sprite& Object) // Calculate the four points of the OBB from a transformed (scaled, rotated...) sprite
{
sf::Transform trans = Object.getTransform();
sf::IntRect local = Object.getTextureRect();
Points[0] = trans.transformPoint(0.f, 0.f);
Points[1] = trans.transformPoint(local.width, 0.f);
Points[2] = trans.transformPoint(local.width, local.height);
Points[3] = trans.transformPoint(0.f, local.height);
}
sf::Vector2f Points[4];
void ProjectOntoAxis(const sf::Vector2f& Axis, float& Min, float& Max) // Project all four points of the OBB onto the given axis and return the dotproducts of the two outermost points
{
Min = (Points[0].x * Axis.x + Points[0].y * Axis.y);
Max = Min;
for (int j = 1; j < 4; j++)
{
float Projection = (Points[j].x * Axis.x + Points[j].y * Axis.y);
if (Projection < Min)
Min = Projection;
if (Projection > Max)
Max = Projection;
}
}
};
bool BoundingBoxTest(const sf::Sprite& Object1, const sf::Sprite& Object2) {
OrientedBoundingBox OBB1(Object1);
OrientedBoundingBox OBB2(Object2);
// Create the four distinct axes that are perpendicular to the edges of the two rectangles
sf::Vector2f Axes[4] = {
sf::Vector2f(OBB1.Points[1].x - OBB1.Points[0].x,
OBB1.Points[1].y - OBB1.Points[0].y),
sf::Vector2f(OBB1.Points[1].x - OBB1.Points[2].x,
OBB1.Points[1].y - OBB1.Points[2].y),
sf::Vector2f(OBB2.Points[0].x - OBB2.Points[3].x,
OBB2.Points[0].y - OBB2.Points[3].y),
sf::Vector2f(OBB2.Points[0].x - OBB2.Points[1].x,
OBB2.Points[0].y - OBB2.Points[1].y)
};
for (int i = 0; i < 4; i++) // For each axis...
{
float MinOBB1, MaxOBB1, MinOBB2, MaxOBB2;
// ... project the points of both OBBs onto the axis ...
OBB1.ProjectOntoAxis(Axes[i], MinOBB1, MaxOBB1);
OBB2.ProjectOntoAxis(Axes[i], MinOBB2, MaxOBB2);
// ... and check whether the outermost projected points of both OBBs overlap.
// If this is not the case, the Separating Axis Theorem states that there can be no collision between the rectangles
if (!((MinOBB2 <= MaxOBB1) && (MaxOBB2 >= MinOBB1)))
return false;
}
return true;
}
}