Skip to content

Commit

Permalink
Initial Release of olcPGEX_AnimatedSprite
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-hayward committed Jan 18, 2020
1 parent 19ea022 commit 725ad3e
Show file tree
Hide file tree
Showing 2 changed files with 341 additions and 1 deletion.
111 changes: 110 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,111 @@
# olcPGEX_AnimatedSprite
Easily use animated sprites in the OLC Pixel Game Engine

This extension is designed for use with the [olcPixelGameEngine](https://github.com/OneLoneCoder/olcPixelGameEngine) by [Javidx9](https://github.com/OneLoneCoder). It allows you to easily use animated sprites that utilise either single-file spritesheets or multiple image files for each frame.

---

## Requirements

The extension requires the [olcPGEX_Graphics2D](https://github.com/OneLoneCoder/olcPixelGameEngine/blob/master/Extensions/olcPGEX_Graphics2D.h) extension to work, but should require no additional libraries beyond that and the olcPixelGameEngine itself.

---

## Usage

To use the olcPGEX_AnimatedSprite extension, it needs to be included in your application. This is done like so:

```cpp
#define OLC_PGEX_ANIMSPR
#include "olcPGEX_AnimatedSprite.h"
```

### Single Spritesheet

```cpp
// define sprite in your PGE program
olc::AnimatedSprite sprite;

bool OnUserCreate()
{
// configure the sprite:
sprite.mode = olc::AnimatedSprite::SPRITE_MODE::SINGLE; // set sprite to use a single spritesheet
sprite.spriteSheet = new olc::Sprite("spritesheet.png"); // define image to use for the spritesheet
sprite.SetSpriteSize({50, 50}); // define size of each sprite with an olc::vi2d
sprite.SetSpriteScale(2.0f); // define scale of sprite; 1.0f is original size. Must be above 0 and defaults to 1.0f

// define states - state name and vector of olc::vi2d to define the top-left position of each frame in the spritesheet
sprite.AddState("idle", {
// let's assume a sprite sheet with 8 rows and columns, using the 50x50 sprite size defined above
{0, 0}, // row 1, column 1 (top left of entire image)
{0, 50}, // row 1, column 2
{400, 200}, // row 8, column 4
});

sprite.AddState("walking", {
{50, 0},
{50, 50},
{50, 100},
});

// set initial state
sprite.SetState("idle")

return true;
}

bool OnUserUpdate(float fElapsedTime)
{
sprite.Draw(felapsedTime, {20.0f, 20.0f}); // draws the sprite at location x:20, y:20 and animates it

return true;
}
```
### Multiple Sprite Files
```cpp
// define sprite in your PGE program
olc::AnimatedSprite sprite;
bool OnUserCreate()
{
// configure the sprite
sprite.mode = olc::AnimatedSprite::SPRITE_MODE::MULTI;
sprite.SetSpriteSize({50.0f, 50.0f);
sprite.SetSpriteScale(2.0f);
// define states - state name and a vector of std::strings that define the location of each image file
sprite.AddState("idle", {
"frame1.png",
"frame2.png",
"frame3.png",
"frame4.png",
});
// set default state
sprite.SetState("idle");
return true;
}
bool OnUserUpdate(float fElapsedTime)
{
sprite.Draw(fElapsedTime, {20.0f, 20.0f});
}
```

### Flipping Sprites

You may want to flip a sprite vertically or horizontally - for example, if your spritesheet only has right-facing images. This can be achieved by changing the flip mode, like so:

```cpp
sprite.flip = olc::AnimatedSprite::FLIP_MODE::HORIZONTAL; // flip horizontally (e.g. make right-facing image face the left)
sprite.flip = olc::AnimatedSprite::FLIP_MODE::VERTICAL; // flip vertically (e.g. make image upside down)
sprite.flip = olc::AnimatedSprite::FLIP_MODE::NONE; // display original image
```

---

## Contributions

Contributions are more than welcome. They can be in the form of reporting bugs and making feature requests (use GitHub issues for both of these), or PRs for changes.
231 changes: 231 additions & 0 deletions olcPGEX_AnimatedSprite.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
/*
olcPGEX_AnimatedSprite.h
+-------------------------------------------------------------+
| OneLoneCoder Pixel Game Engine Extension |
| AnimatedSprites - v1.0 |
+-------------------------------------------------------------+
What is this?
~~~~~~~~~~~~~
This is an extension to the olcPixelGameEngine, which provides
the ability to easily animate sprites with either a single
spritesheets or individual image files for each frame.
Use of this extension requires the olcPGEX_Graphics2D extension.
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2018 - 2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Links
~~~~~
Homepage: https://matthewhayward.co.uk
Author
~~~~~~
Matt Hayward aka SaladinAkara
*/

#include "olcPGEX_Graphics2D.h"

#ifndef OLC_PGEX_ANIMATEDSPRITE
#define OLC_PGEX_ANIMATEDSPRITE

namespace olc
{
class AnimatedSprite : public olc::PGEX
{
public:
void SetState(std::string newState);
std::string GetState();
void Draw(float fElapsedTime, olc::vf2d position);
void AddState(std::string stateName, std::vector<std::string> imagePaths);
void AddState(std::string stateName, std::vector<olc::vi2d> spriteLocations);
void SetSpriteSize(olc::vi2d size);
olc::vi2d GetSpriteSize();
void SetSpriteScale(float scale);

protected:
olc::Sprite* GetMultiFrame(float fElapsedTime);
olc::vi2d GetSingleFrame(float fElapsedTime);

public:
bool flipped = false;
enum class FLIP_MODE {
NONE = 0,
HORIZONTAL = 1,
VERTICAL = 2
};
enum class SPRITE_MODE {
MULTI = 0,
SINGLE = 1
};
FLIP_MODE flip = FLIP_MODE::NONE;
SPRITE_MODE mode = SPRITE_MODE::MULTI;
olc::Sprite* spriteSheet = nullptr;

protected:
std::string state;
std::map<std::string, std::vector<olc::Sprite*>> multiFrames;
std::map<std::string, std::vector<olc::vi2d>> singleFrames;
float frameTimer = 0.0f;
float frameDuration = 0.1f;
unsigned int currentFrame;
olc::vi2d spriteSize;
float spriteScale = 1.0f;
};
}

#ifdef OLC_PGEX_ANIMSPR
#undef OLC_PGEX_ANIMSPR

namespace olc
{
olc::Sprite* AnimatedSprite::GetMultiFrame(float fElapsedTime)
{
frameTimer += fElapsedTime;

if (frameTimer >= frameDuration) {
currentFrame++;
frameTimer = 0.0f;

if (currentFrame >= multiFrames[state].size()) {
currentFrame = 0;
}
}

return multiFrames[state][currentFrame];
}

olc::vi2d AnimatedSprite::GetSingleFrame(float fElapsedTime)
{
frameTimer += fElapsedTime;

if (frameTimer >= frameDuration) {
currentFrame++;
frameTimer = 0.0f;

if (currentFrame >= singleFrames[state].size()) {
currentFrame = 0;
}
}

return singleFrames[state][currentFrame];
}

void AnimatedSprite::SetState(std::string newState)
{
if ((mode == SPRITE_MODE::MULTI && multiFrames.find(newState) == multiFrames.end())
|| (mode == SPRITE_MODE::SINGLE && singleFrames.find(newState) == singleFrames.end())) {

std::cout << "Error: State " << newState << " does not exist." << std::endl;
return;
}

if (newState != state) {
state = newState;
currentFrame = 0;
}
}

std::string AnimatedSprite::GetState()
{
return state;
}

void AnimatedSprite::AddState(std::string stateName, std::vector<std::string> imgPaths)
{
for (std::string& path : imgPaths) {
multiFrames[stateName].push_back(new olc::Sprite(path));
}
}

void AnimatedSprite::AddState(std::string stateName, std::vector<olc::vi2d> spriteLocations)
{
for (olc::vi2d& location : spriteLocations) {
singleFrames[stateName].push_back(location);
}
}

void AnimatedSprite::SetSpriteSize(olc::vi2d size)
{
spriteSize = size;
}

olc::vi2d AnimatedSprite::GetSpriteSize()
{
return spriteSize;
}

void AnimatedSprite::SetSpriteScale(float scale)
{
if (scale <= 0.0f) {
spriteScale = 1.0f;
} else {
spriteScale = scale;
}
}

void AnimatedSprite::Draw(float fElapsedTime, olc::vf2d position)
{
olc::GFX2D::Transform2D t;

if (flip == FLIP_MODE::HORIZONTAL) {
t.Translate(-((spriteSize.x / 2) * spriteScale), 0);
t.Scale(-spriteScale, spriteScale);
} else if (flip == FLIP_MODE::VERTICAL) {
t.Translate(0, -((spriteSize.y / 2) * spriteScale));
t.Scale(spriteScale, -spriteScale);
}
else {
t.Scale(spriteScale, spriteScale);
}

t.Translate(position.x, position.y);

if (mode == SPRITE_MODE::MULTI) {
olc::GFX2D::DrawSprite(GetMultiFrame(fElapsedTime), t);
}
else {
olc::Sprite* sprite = new olc::Sprite(spriteSize.x, spriteSize.y);
pge->SetDrawTarget(sprite);
pge->DrawPartialSprite({ 0, 0 }, spriteSheet, GetSingleFrame(fElapsedTime), spriteSize);
pge->SetDrawTarget(nullptr);
olc::GFX2D::DrawSprite(sprite, t);
delete sprite;
}
}
}

#endif
#endif

0 comments on commit 725ad3e

Please sign in to comment.