使用 AnimatedSprite 进行 SFML 碰撞测试
SFML Collision tests with AnimatedSprite
我是 SFML 的新手,如果我能就此问题获得任何帮助,我将不胜感激。在 game.cpp 中,我尝试使用 collision.cpp PixelPerfectTest() 方法在我的背景精灵和我的玩家精灵(当前不存在)之间执行第 261 行的碰撞测试。但是我使用动画 class 在我的精灵 sheet 中加载的方式本质上是一个 std::vector 纹理矩形 (sf::IntRect) 和一个 sf::Texture 参考。所以你必须提供一个 spritesheet 并推回你的纹理 reactangles。然后创建一个 AnimatedSprite 对象并为其提供动画。 AnimatedSprite class 继承自 sf::Drawable 和 sf::Transfomable,所以它的行为很像一个普通的精灵。无需创建 Sprite 对象。我需要能够加载正在使用的当前帧并加载到 sprite obj 中,然后使用该 sprite 进行碰撞测试。
谢谢,
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;
}
}
您可以向 AnimatedSprite 添加一个函数 class 来创建和 returns 一个精灵对象。像
sf::Sprite getCurrentSprite()
{
sf::Sprite temp;
temp.setTexture(<insert your texture>);
temp.setTextureRect(<the current frame>);
return temp;
}
一种更有效的方法是使用 sf::Sprite::setTextureRect() 函数 change/loop 通过帧,而不是将它们存储到 std::vector 中。这样你只需要有一个你的碰撞检测函数使用的 sf::Sprite 对象。
我假设您没有创建那个碰撞检测器,因为它使用 sf::Sprite 并且您正在使用您创建的 AnimatedSprite class(它似乎与您的 CD 不兼容)。因此,您也可以轻松修改碰撞检测算法以与动画精灵一起使用 class.
我是 SFML 的新手,如果我能就此问题获得任何帮助,我将不胜感激。在 game.cpp 中,我尝试使用 collision.cpp PixelPerfectTest() 方法在我的背景精灵和我的玩家精灵(当前不存在)之间执行第 261 行的碰撞测试。但是我使用动画 class 在我的精灵 sheet 中加载的方式本质上是一个 std::vector 纹理矩形 (sf::IntRect) 和一个 sf::Texture 参考。所以你必须提供一个 spritesheet 并推回你的纹理 reactangles。然后创建一个 AnimatedSprite 对象并为其提供动画。 AnimatedSprite class 继承自 sf::Drawable 和 sf::Transfomable,所以它的行为很像一个普通的精灵。无需创建 Sprite 对象。我需要能够加载正在使用的当前帧并加载到 sprite obj 中,然后使用该 sprite 进行碰撞测试。
谢谢,
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;
}
}
您可以向 AnimatedSprite 添加一个函数 class 来创建和 returns 一个精灵对象。像
sf::Sprite getCurrentSprite()
{
sf::Sprite temp;
temp.setTexture(<insert your texture>);
temp.setTextureRect(<the current frame>);
return temp;
}
一种更有效的方法是使用 sf::Sprite::setTextureRect() 函数 change/loop 通过帧,而不是将它们存储到 std::vector 中。这样你只需要有一个你的碰撞检测函数使用的 sf::Sprite 对象。
我假设您没有创建那个碰撞检测器,因为它使用 sf::Sprite 并且您正在使用您创建的 AnimatedSprite class(它似乎与您的 CD 不兼容)。因此,您也可以轻松修改碰撞检测算法以与动画精灵一起使用 class.