initial commit
This commit is contained in:
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
.vscode/
|
||||
270
animatedsprite.cpp
Normal file
270
animatedsprite.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#include "animatedsprite.hpp"
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace Animation
|
||||
{
|
||||
sf::IntRect Animation::GetFrame(unsigned frameNumber, bool const bounced) const
|
||||
{
|
||||
assert(frameNumber < frameCount);
|
||||
|
||||
if(reverse != bounced)
|
||||
frameNumber = frameCount - (frameNumber + 1);
|
||||
unsigned const x = frameOrigin.x + (frameNumber * frameMargin.x) + (frameNumber * frameStep.x);
|
||||
unsigned const y = frameOrigin.y + (frameNumber * frameMargin.y) + (frameNumber * frameStep.y);
|
||||
|
||||
return sf::IntRect(x, y, frameSize.x, frameSize.y);
|
||||
}
|
||||
|
||||
Animation::Animation()
|
||||
: frameOrigin(0, 0),
|
||||
frameSize(0, 0),
|
||||
frameStep(0, 0),
|
||||
frameMargin(0, 0),
|
||||
frameCount(0),
|
||||
frameTime(0),
|
||||
reverse(false),
|
||||
loop(false),
|
||||
bounce(false)
|
||||
{}
|
||||
|
||||
std::unordered_map<std::string, Animation> LoadFromFile(std::string const & filepath)
|
||||
{
|
||||
std::array<std::string, 9> const properties =
|
||||
{
|
||||
"frame-origin",
|
||||
"frame-size",
|
||||
"frame-step",
|
||||
"frame-margin",
|
||||
"frame-count",
|
||||
"frame-time",
|
||||
"reverse",
|
||||
"loop",
|
||||
"bounce"
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, Animation> animations;
|
||||
|
||||
std::ifstream file(filepath);
|
||||
if(!file.is_open())
|
||||
return animations;
|
||||
|
||||
Animation candidate;
|
||||
std::string candidateName;
|
||||
while(file.good())
|
||||
{
|
||||
std::string lineRaw;
|
||||
std::getline(file, lineRaw);
|
||||
if(lineRaw.size() == 0 || lineRaw[0] == '#')
|
||||
continue;
|
||||
|
||||
std::stringstream line(lineRaw);
|
||||
std::string key;
|
||||
line >> key;
|
||||
|
||||
if(lineRaw[0] == '\t' && candidateName.size() > 0)
|
||||
{
|
||||
for(std::size_t i = 0; i < properties.size(); ++i)
|
||||
{
|
||||
if(properties[i].compare(key) == 0)
|
||||
{
|
||||
switch(i)
|
||||
{
|
||||
case 0:
|
||||
line >> candidate.frameOrigin.x;
|
||||
line >> candidate.frameOrigin.y;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
line >> candidate.frameSize.x;
|
||||
line >> candidate.frameSize.y;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
line >> candidate.frameStep.x;
|
||||
line >> candidate.frameStep.y;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
line >> candidate.frameMargin.x;
|
||||
line >> candidate.frameMargin.y;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
line >> candidate.frameCount;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
line >> candidate.frameTime;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
candidate.reverse = true;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
candidate.loop = true;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
candidate.loop = true;
|
||||
candidate.bounce = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(key.compare("animation") == 0)
|
||||
{
|
||||
if(candidateName.size() > 0)
|
||||
{
|
||||
animations[candidateName] = candidate;
|
||||
}
|
||||
candidate = Animation();
|
||||
line >> candidateName;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(candidateName.size() > 0)
|
||||
{
|
||||
animations[candidateName] = candidate;
|
||||
}
|
||||
candidateName.resize(0);
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
|
||||
if(candidateName.size() > 0)
|
||||
{
|
||||
animations[candidateName] = candidate;
|
||||
candidateName.resize(0);
|
||||
}
|
||||
|
||||
return animations;
|
||||
}
|
||||
|
||||
void Sprite::SetFrame()
|
||||
{
|
||||
assert(currentAnimation != nullptr);
|
||||
|
||||
sf::IntRect textureRect = currentAnimation->GetFrame(currentFrame, bouncing);
|
||||
if(isFlippedHorizontally)
|
||||
{
|
||||
textureRect.left += textureRect.width;
|
||||
textureRect.width *= -1;
|
||||
}
|
||||
if(isFlippedVertically)
|
||||
{
|
||||
textureRect.top += textureRect.height;
|
||||
textureRect.height *= -1;
|
||||
}
|
||||
setTextureRect(textureRect);
|
||||
}
|
||||
|
||||
bool Sprite::IsAnimationFinished() const
|
||||
{
|
||||
assert(currentAnimation != nullptr);
|
||||
return currentFrame == (currentAnimation->frameCount - 1) && !currentAnimation->loop;
|
||||
}
|
||||
|
||||
void Sprite::SetHorizontalFlip(const bool v)
|
||||
{
|
||||
if(v != isFlippedHorizontally)
|
||||
{
|
||||
isFlippedHorizontally = v;
|
||||
SetFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite::SetVerticalFlip(const bool v)
|
||||
{
|
||||
if(v != isFlippedVertically)
|
||||
{
|
||||
isFlippedVertically = v;
|
||||
SetFrame();
|
||||
}
|
||||
}
|
||||
|
||||
bool Sprite::IsFlippedHorizontally() const
|
||||
{
|
||||
return isFlippedHorizontally;
|
||||
}
|
||||
|
||||
bool Sprite::IsFlippedVertically() const
|
||||
{
|
||||
return isFlippedVertically;
|
||||
}
|
||||
|
||||
void Sprite::ResetAnimation()
|
||||
{
|
||||
currentFrame = 0U;
|
||||
currentFrameTime = 0U;
|
||||
bouncing = false;
|
||||
SetFrame();
|
||||
}
|
||||
|
||||
bool Sprite::SetAnimation(const std::string & name)
|
||||
{
|
||||
auto const candidate = animations.find(name);
|
||||
if(candidate == animations.cend())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
currentAnimation = &(candidate->second);
|
||||
ResetAnimation();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sprite::Update(const sf::Time & elapsed)
|
||||
{
|
||||
assert(currentAnimation != nullptr);
|
||||
|
||||
if(IsAnimationFinished())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentFrameTime += elapsed.asMilliseconds();
|
||||
if(currentFrameTime < currentAnimation->frameTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
unsigned const steps = currentFrameTime / currentAnimation->frameTime;
|
||||
currentFrameTime -= currentAnimation->frameTime * steps;
|
||||
|
||||
currentFrame += steps;
|
||||
if(currentFrame >= currentAnimation->frameCount)
|
||||
{
|
||||
currentFrame = currentFrame % currentAnimation->frameCount;
|
||||
if(currentAnimation->bounce)
|
||||
bouncing = !bouncing;
|
||||
}
|
||||
|
||||
SetFrame();
|
||||
}
|
||||
|
||||
void Sprite::UpdateFromOther(const Sprite & parent)
|
||||
{
|
||||
if(currentFrame != parent.currentFrame)
|
||||
{
|
||||
currentFrame = parent.currentFrame;
|
||||
bouncing = parent.bouncing;
|
||||
SetFrame();
|
||||
}
|
||||
}
|
||||
|
||||
Sprite::Sprite(std::unordered_map<std::string, Animation> const & _animations,
|
||||
std::string const & animation,
|
||||
sf::Texture const & texture)
|
||||
: sf::Sprite(texture),
|
||||
animations(_animations),
|
||||
currentAnimation(nullptr)
|
||||
{
|
||||
SetAnimation(animation);
|
||||
}
|
||||
}
|
||||
65
animatedsprite.hpp
Normal file
65
animatedsprite.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include <SFML/Graphics/Sprite.hpp>
|
||||
#include <SFML/System/Time.hpp>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Animation
|
||||
{
|
||||
struct Animation
|
||||
{
|
||||
sf::Vector2u frameOrigin; // offset in pixels
|
||||
sf::Vector2u frameSize; // in pixels
|
||||
sf::Vector2u frameStep; // in pixels
|
||||
sf::Vector2u frameMargin; // in pixels
|
||||
unsigned frameCount;
|
||||
unsigned frameTime; // in milliseconds
|
||||
bool reverse;
|
||||
bool loop;
|
||||
bool bounce;
|
||||
|
||||
sf::IntRect GetFrame(unsigned const frameNumber, bool const bounced = false) const;
|
||||
|
||||
Animation();
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, Animation> LoadFromFile(std::string const & filepath);
|
||||
|
||||
class Sprite : public sf::Sprite
|
||||
{
|
||||
protected:
|
||||
std::unordered_map<std::string, Animation> const & animations;
|
||||
Animation const * currentAnimation;
|
||||
|
||||
unsigned currentFrame;
|
||||
unsigned currentFrameTime;
|
||||
bool bouncing;
|
||||
bool isFlippedHorizontally;
|
||||
bool isFlippedVertically;
|
||||
|
||||
void SetFrame();
|
||||
|
||||
public:
|
||||
virtual bool IsAnimationFinished() const;
|
||||
|
||||
// This flip call is not free, so be careful with lots
|
||||
// of repeated calls to this function
|
||||
virtual void SetHorizontalFlip(bool const v);
|
||||
// This flip call is not free, so be careful with lots
|
||||
// of repeated calls to this function
|
||||
virtual void SetVerticalFlip(bool const v);
|
||||
|
||||
bool IsFlippedHorizontally() const;
|
||||
bool IsFlippedVertically() const;
|
||||
|
||||
void ResetAnimation();
|
||||
bool SetAnimation(const std::string & name);
|
||||
|
||||
virtual void Update(sf::Time const & elapsed);
|
||||
void UpdateFromOther(Sprite const & other);
|
||||
|
||||
Sprite(std::unordered_map<std::string, Animation> const & _animations,
|
||||
std::string const & animation,
|
||||
sf::Texture const & texture);
|
||||
};
|
||||
}
|
||||
6
test/makefile
Normal file
6
test/makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
test.out: test.cpp ../animatedsprite.cpp
|
||||
g++ $^ -lsfml-graphics -lsfml-window -lsfml-system -o $@
|
||||
|
||||
.PHONY: check
|
||||
check: test.out
|
||||
./test.out
|
||||
105
test/test.cpp
Normal file
105
test/test.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "../animatedsprite.hpp"
|
||||
#include <cstdio>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
sf::RenderWindow window(sf::VideoMode(800, 600), "AnimatedSprite Test");
|
||||
window.setVerticalSyncEnabled(true);
|
||||
|
||||
sf::Texture texture;
|
||||
if(!texture.loadFromFile("test.png"))
|
||||
{
|
||||
std::puts("Failed to load <test.png> texture.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto const animations = Animation::LoadFromFile("testanimations.txt");
|
||||
if(animations.size() == 0)
|
||||
{
|
||||
std::puts("Test animations definition file was empty or read incorrectly.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Animation::Sprite sprite(animations, "default", texture);
|
||||
std::puts("Use the numerical keys to cycle through animations (1-7)");
|
||||
|
||||
sf::Clock sfclock;
|
||||
bool good = true;
|
||||
while(good)
|
||||
{
|
||||
sf::Event e;
|
||||
while(window.pollEvent(e))
|
||||
{
|
||||
switch(e.type)
|
||||
{
|
||||
case sf::Event::Closed:
|
||||
good = false;
|
||||
break;
|
||||
|
||||
case sf::Event::KeyReleased:
|
||||
switch(e.key.code)
|
||||
{
|
||||
case sf::Keyboard::Escape:
|
||||
good = false;
|
||||
break;
|
||||
|
||||
case sf::Keyboard::Up:
|
||||
sprite.SetHorizontalFlip(!sprite.IsFlippedHorizontally());
|
||||
break;
|
||||
|
||||
case sf::Keyboard::Down:
|
||||
sprite.SetVerticalFlip(!sprite.IsFlippedVertically());
|
||||
break;
|
||||
|
||||
case sf::Keyboard::R:
|
||||
sprite.ResetAnimation();
|
||||
break;
|
||||
|
||||
case sf::Keyboard::Num1:
|
||||
sprite.SetAnimation("default");
|
||||
break;
|
||||
|
||||
case sf::Keyboard::Num2:
|
||||
sprite.SetAnimation("default-reverse");
|
||||
break;
|
||||
|
||||
case sf::Keyboard::Num3:
|
||||
sprite.SetAnimation("default-fast");
|
||||
break;
|
||||
|
||||
case sf::Keyboard::Num4:
|
||||
sprite.SetAnimation("carousel");
|
||||
break;
|
||||
|
||||
case sf::Keyboard::Num5:
|
||||
sprite.SetAnimation("carousel-margin");
|
||||
break;
|
||||
|
||||
case sf::Keyboard::Num6:
|
||||
sprite.SetAnimation("carousel-reverse");
|
||||
break;
|
||||
|
||||
case sf::Keyboard::Num7:
|
||||
sprite.SetAnimation("carousel-bounce");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sprite.Update(sfclock.restart());
|
||||
|
||||
window.clear();
|
||||
window.draw(sprite);
|
||||
window.display();
|
||||
}
|
||||
|
||||
window.close();
|
||||
return 0;
|
||||
}
|
||||
BIN
test/test.png
Normal file
BIN
test/test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 289 B |
73
test/testanimations.txt
Normal file
73
test/testanimations.txt
Normal file
@@ -0,0 +1,73 @@
|
||||
# ALL NUMBERS ARE >= 0 !!!
|
||||
# animation name
|
||||
# frame-origin x y
|
||||
# frame-size x y
|
||||
# frame-step x y
|
||||
# frame-margin x y
|
||||
# frame-count n
|
||||
# frame-time n
|
||||
# reverse
|
||||
# loop
|
||||
# bounce
|
||||
|
||||
animation default
|
||||
frame-origin 0 15
|
||||
frame-size 16 32
|
||||
frame-step 16 0
|
||||
frame-margin 0 0
|
||||
frame-count 4
|
||||
frame-time 32
|
||||
loop
|
||||
|
||||
animation default-reverse
|
||||
frame-origin 0 15
|
||||
frame-size 16 32
|
||||
frame-step 16 0
|
||||
frame-margin 0 0
|
||||
frame-count 4
|
||||
frame-time 32
|
||||
loop
|
||||
reverse
|
||||
|
||||
animation default-fast
|
||||
frame-origin 0 15
|
||||
frame-size 16 32
|
||||
frame-step 16 0
|
||||
frame-margin 0 0
|
||||
frame-count 4
|
||||
frame-time 16
|
||||
loop
|
||||
|
||||
animation carousel
|
||||
frame-origin 0 15
|
||||
frame-size 16 32
|
||||
frame-step 2 0
|
||||
frame-margin 0 0
|
||||
frame-count 25
|
||||
frame-time 16
|
||||
|
||||
animation carousel-reverse
|
||||
frame-origin 0 15
|
||||
frame-size 16 32
|
||||
frame-step 2 0
|
||||
frame-margin 0 0
|
||||
frame-count 25
|
||||
frame-time 16
|
||||
reverse
|
||||
|
||||
animation carousel-margin
|
||||
frame-origin 0 15
|
||||
frame-size 16 32
|
||||
frame-step 1 0
|
||||
frame-margin 1 0
|
||||
frame-count 25
|
||||
frame-time 16
|
||||
|
||||
animation carousel-bounce
|
||||
frame-origin 0 15
|
||||
frame-size 16 32
|
||||
frame-step 2 0
|
||||
frame-margin 0 0
|
||||
frame-count 25
|
||||
frame-time 32
|
||||
bounce
|
||||
Reference in New Issue
Block a user