pull/300/head
parent
c29acc74cc
commit
f467e7dce2
@ -0,0 +1,278 @@ |
|||||||
|
/*
|
||||||
|
Example file for olcUTIL_Animate2D.h |
||||||
|
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2018 - 2022 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 |
||||||
|
~~~~~ |
||||||
|
YouTube: https://www.youtube.com/javidx9
|
||||||
|
Discord: https://discord.gg/WhwHUMV
|
||||||
|
Twitter: https://www.twitter.com/javidx9
|
||||||
|
Twitch: https://www.twitch.tv/javidx9
|
||||||
|
GitHub: https://www.github.com/onelonecoder
|
||||||
|
Homepage: https://www.onelonecoder.com
|
||||||
|
|
||||||
|
Author |
||||||
|
~~~~~~ |
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022 |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#define OLC_PGE_APPLICATION |
||||||
|
#include "olcPixelGameEngine.h" |
||||||
|
|
||||||
|
#include "utilities/olcUTIL_Animate2D.h" |
||||||
|
|
||||||
|
class TEST_Animate2D : public olc::PixelGameEngine |
||||||
|
{ |
||||||
|
public: |
||||||
|
TEST_Animate2D() |
||||||
|
{
|
||||||
|
sAppName = "Animate2D Utility Test"; |
||||||
|
} |
||||||
|
|
||||||
|
// These are the states the dude can exist in, we'll need these for physics
|
||||||
|
// and animation. The physics side of things moves the dude around according to state,
|
||||||
|
// while the animator chooses the frames based upon state and time
|
||||||
|
enum class DudeState: uint8_t |
||||||
|
{ |
||||||
|
WALK_N, WALK_S, WALK_E, WALK_W, IDLE_STAND, LAUGH, CHEER, YES, NO |
||||||
|
}; |
||||||
|
|
||||||
|
// !! - IMPORTANT - !!
|
||||||
|
// The animation set
|
||||||
|
olc::utils::Animate2D::Animation<DudeState> animDude; |
||||||
|
|
||||||
|
// One big sprite containing all the graphics
|
||||||
|
olc::Renderable gfxAll; |
||||||
|
|
||||||
|
// Small object to reprsent a dude walking around doing things
|
||||||
|
struct sWalkingDude |
||||||
|
{ |
||||||
|
// Which dude overall out of graophic?
|
||||||
|
int32_t id = 0; |
||||||
|
// Where are they?
|
||||||
|
olc::vf2d pos; |
||||||
|
// What are they doing?
|
||||||
|
DudeState UserState = DudeState::IDLE_STAND; |
||||||
|
// For how long should they do it?
|
||||||
|
float fTick = 0.0f; |
||||||
|
|
||||||
|
// !! - IMPORTANT - !!
|
||||||
|
// Animation Token - links this object's state to animator. Note
|
||||||
|
// there is no 'ownership' or memory issues here, this is a token
|
||||||
|
// that the animator can use to quickly get to where it needs
|
||||||
|
// in order to return frames upon request for this object.
|
||||||
|
olc::utils::Animate2D::AnimationState animstate; |
||||||
|
}; |
||||||
|
|
||||||
|
// Introducing.... The dudes!
|
||||||
|
size_t nDudes = 500; |
||||||
|
std::vector<sWalkingDude> vDudes; |
||||||
|
|
||||||
|
public: |
||||||
|
bool OnUserCreate() override |
||||||
|
{
|
||||||
|
// For this appliaction I have a single image that contains
|
||||||
|
// 28x2 unique characters, each character contains 8 animations of 3
|
||||||
|
// frames each. Each frame is 26x36 pixels
|
||||||
|
gfxAll.Load("./assets/MegaSprite1.png"); |
||||||
|
|
||||||
|
// Thats A LOT of individual graphics, but they all follow a similar pattern
|
||||||
|
// because the asset was created usefully (take note certain popular asset creators)
|
||||||
|
// which means we can reuse the animation without needing to define it
|
||||||
|
// individually for all the "dudes" - the "cookie cutter" approach
|
||||||
|
|
||||||
|
// Manually construct sequences - gfxAll could in fact be nullptr for this
|
||||||
|
// application, but ive kept it here for convenience
|
||||||
|
olc::utils::Animate2D::FrameSequence anim_fs_walk_s; |
||||||
|
anim_fs_walk_s.AddFrame({ &gfxAll, {{0,0}, {26,36}} }); |
||||||
|
anim_fs_walk_s.AddFrame({ &gfxAll, {{26,0}, {26,36}} }); |
||||||
|
anim_fs_walk_s.AddFrame({ &gfxAll, {{52,0}, {26,36}} }); |
||||||
|
|
||||||
|
olc::utils::Animate2D::FrameSequence anim_fs_walk_w; |
||||||
|
anim_fs_walk_w.AddFrame({ &gfxAll, {{ 0,36}, {26,36}} }); |
||||||
|
anim_fs_walk_w.AddFrame({ &gfxAll, {{26,36}, {26,36}} }); |
||||||
|
anim_fs_walk_w.AddFrame({ &gfxAll, {{52,36}, {26,36}} }); |
||||||
|
|
||||||
|
olc::utils::Animate2D::FrameSequence anim_fs_walk_e; |
||||||
|
anim_fs_walk_e.AddFrame({ &gfxAll, {{ 0,72}, {26,36}} }); |
||||||
|
anim_fs_walk_e.AddFrame({ &gfxAll, {{26,72}, {26,36}} }); |
||||||
|
anim_fs_walk_e.AddFrame({ &gfxAll, {{52,72}, {26,36}} }); |
||||||
|
|
||||||
|
olc::utils::Animate2D::FrameSequence anim_fs_walk_n; |
||||||
|
anim_fs_walk_n.AddFrame({ &gfxAll, {{ 0,108}, {26,36}} }); |
||||||
|
anim_fs_walk_n.AddFrame({ &gfxAll, {{26,108}, {26,36}} }); |
||||||
|
anim_fs_walk_n.AddFrame({ &gfxAll, {{52,108}, {26,36}} }); |
||||||
|
|
||||||
|
olc::utils::Animate2D::FrameSequence anim_fs_yes; |
||||||
|
anim_fs_yes.AddFrame({ &gfxAll, {{ 0,144}, {26,36}} }); |
||||||
|
anim_fs_yes.AddFrame({ &gfxAll, {{26,144}, {26,36}} }); |
||||||
|
anim_fs_yes.AddFrame({ &gfxAll, {{52,144}, {26,36}} }); |
||||||
|
|
||||||
|
olc::utils::Animate2D::FrameSequence anim_fs_no; |
||||||
|
anim_fs_no.AddFrame({ &gfxAll, {{ 0,180}, {26,36}} }); |
||||||
|
anim_fs_no.AddFrame({ &gfxAll, {{26,180}, {26,36}} }); |
||||||
|
anim_fs_no.AddFrame({ &gfxAll, {{52,180}, {26,36}} }); |
||||||
|
|
||||||
|
olc::utils::Animate2D::FrameSequence anim_fs_laugh; |
||||||
|
anim_fs_laugh.AddFrame({ &gfxAll, {{ 0,216}, {26,36}} }); |
||||||
|
anim_fs_laugh.AddFrame({ &gfxAll, {{26,216}, {26,36}} }); |
||||||
|
anim_fs_laugh.AddFrame({ &gfxAll, {{52,216}, {26,36}} }); |
||||||
|
|
||||||
|
olc::utils::Animate2D::FrameSequence anim_fs_cheer; |
||||||
|
anim_fs_cheer.AddFrame({ &gfxAll, {{ 0,252}, {26,36}} }); |
||||||
|
anim_fs_cheer.AddFrame({ &gfxAll, {{26,252}, {26,36}} }); |
||||||
|
anim_fs_cheer.AddFrame({ &gfxAll, {{52,252}, {26,36}} }); |
||||||
|
|
||||||
|
// A special "idle" animation just consists of a single frame
|
||||||
|
olc::utils::Animate2D::FrameSequence anim_fs_idle; |
||||||
|
anim_fs_idle.AddFrame({ &gfxAll, {{26,0}, {26,36}} }); |
||||||
|
|
||||||
|
// We have constructed teh individual sequences, now its time
|
||||||
|
// to add them to an animation, along with a state name/enum.
|
||||||
|
//
|
||||||
|
// I have chosen to use the enum shown earlier. You could use
|
||||||
|
// std::string too, which is conveninent if you need to display
|
||||||
|
// the states, though potentially far less performant
|
||||||
|
|
||||||
|
animDude.AddState(DudeState::WALK_S, anim_fs_walk_s); |
||||||
|
animDude.AddState(DudeState::WALK_W, anim_fs_walk_w); |
||||||
|
animDude.AddState(DudeState::WALK_E, anim_fs_walk_e); |
||||||
|
animDude.AddState(DudeState::WALK_N, anim_fs_walk_n); |
||||||
|
animDude.AddState(DudeState::IDLE_STAND, anim_fs_idle); |
||||||
|
animDude.AddState(DudeState::YES, anim_fs_yes); |
||||||
|
animDude.AddState(DudeState::NO, anim_fs_no); |
||||||
|
animDude.AddState(DudeState::LAUGH, anim_fs_laugh); |
||||||
|
animDude.AddState(DudeState::CHEER, anim_fs_cheer); |
||||||
|
|
||||||
|
// Initialise the dudes
|
||||||
|
for (size_t n = 0; n < nDudes; n++) |
||||||
|
{ |
||||||
|
sWalkingDude dude; |
||||||
|
|
||||||
|
// Random dude
|
||||||
|
dude.id = rand() % (28 * 2); |
||||||
|
|
||||||
|
// Begin in idle state, at random location
|
||||||
|
dude.UserState = DudeState::IDLE_STAND; |
||||||
|
dude.pos = { float(rand() % ScreenWidth()), float(rand() % ScreenHeight()) }; |
||||||
|
|
||||||
|
// The animation token needs to be updated too
|
||||||
|
animDude.ChangeState(dude.animstate, dude.UserState); |
||||||
|
|
||||||
|
vDudes.push_back(dude); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool OnUserUpdate(float fElapsedTime) override |
||||||
|
{
|
||||||
|
// Update Dudes
|
||||||
|
float fSpeed = 32.0f; |
||||||
|
|
||||||
|
for (auto& dude : vDudes) |
||||||
|
{ |
||||||
|
// If a dude's tick reaches 0, it will select a new state
|
||||||
|
dude.fTick -= fElapsedTime; |
||||||
|
if (dude.fTick < 0.0f) |
||||||
|
{ |
||||||
|
// Choose one out of the 9 randomly
|
||||||
|
int nAction = rand() % 9; |
||||||
|
|
||||||
|
// Choose for how long it should do it in seconds
|
||||||
|
dude.fTick = (float(rand()) / float(RAND_MAX)) * 5.0f; |
||||||
|
|
||||||
|
// Assign the state depending on the dice roll - (since enum
|
||||||
|
// we could cast here too, but, well, meh...)
|
||||||
|
if (nAction == 0) dude.UserState = DudeState::IDLE_STAND; |
||||||
|
if (nAction == 1) dude.UserState = DudeState::WALK_S; |
||||||
|
if (nAction == 2) dude.UserState = DudeState::WALK_N; |
||||||
|
if (nAction == 3) dude.UserState = DudeState::WALK_E; |
||||||
|
if (nAction == 4) dude.UserState = DudeState::WALK_W; |
||||||
|
if (nAction == 5) dude.UserState = DudeState::YES; |
||||||
|
if (nAction == 6) dude.UserState = DudeState::NO; |
||||||
|
if (nAction == 7) dude.UserState = DudeState::LAUGH; |
||||||
|
if (nAction == 8) dude.UserState = DudeState::CHEER; |
||||||
|
|
||||||
|
// State has changed, so update animation token
|
||||||
|
// !! - IMPORTANT - !!
|
||||||
|
animDude.ChangeState(dude.animstate, dude.UserState); |
||||||
|
} |
||||||
|
|
||||||
|
// Update "physics", if walking move in that direction at speed
|
||||||
|
if (dude.UserState == DudeState::WALK_S) dude.pos += olc::vf2d(0, +1) * fSpeed * fElapsedTime; |
||||||
|
if (dude.UserState == DudeState::WALK_N) dude.pos += olc::vf2d(0, -1) * fSpeed * fElapsedTime; |
||||||
|
if (dude.UserState == DudeState::WALK_E) dude.pos += olc::vf2d(+1, 0) * fSpeed * fElapsedTime; |
||||||
|
if (dude.UserState == DudeState::WALK_W) dude.pos += olc::vf2d(-1, 0) * fSpeed * fElapsedTime; |
||||||
|
|
||||||
|
// If walk off screen, wrap around to other side
|
||||||
|
if (dude.pos.x > ScreenWidth()) dude.pos.x -= ScreenWidth(); |
||||||
|
if (dude.pos.y > ScreenHeight()) dude.pos.x -= ScreenHeight(); |
||||||
|
if (dude.pos.x < 0) dude.pos.x += ScreenWidth(); |
||||||
|
if (dude.pos.y < 0) dude.pos.y += ScreenHeight(); |
||||||
|
|
||||||
|
// Update animation token every frame
|
||||||
|
// !! - IMPORTANT - !!
|
||||||
|
animDude.UpdateState(dude.animstate, fElapsedTime); |
||||||
|
} |
||||||
|
|
||||||
|
// Render Dudes
|
||||||
|
for (const auto& dude : vDudes) |
||||||
|
{ |
||||||
|
// Get the frame, this contains both source image and source location rectangle
|
||||||
|
// !! - IMPORTANT - !!
|
||||||
|
const auto& frame = animDude.GetFrame(dude.animstate); |
||||||
|
|
||||||
|
// Thats cool, but there are 28x2 dudes on the sprite sheet, so using the ID, construct
|
||||||
|
// an offset to the correct dude
|
||||||
|
olc::vi2d vOffset = { (dude.id % 28) * 78, (dude.id / 28) * 288 }; |
||||||
|
|
||||||
|
// Use DrawPartialDecal to chop out the correct dude frm the image source
|
||||||
|
DrawPartialDecal(dude.pos, frame.GetSourceImage()->Decal(), frame.GetSourceRect().pos + vOffset, frame.GetSourceRect().size); |
||||||
|
} |
||||||
|
|
||||||
|
// That's it
|
||||||
|
return true; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
int main() |
||||||
|
{ |
||||||
|
TEST_Animate2D demo; |
||||||
|
if (demo.Construct(640, 480, 2, 2)) |
||||||
|
demo.Start(); |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,226 @@ |
|||||||
|
/*
|
||||||
|
Example file for olcPGEX_QuickGUI.h |
||||||
|
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2018 - 2022 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 |
||||||
|
~~~~~ |
||||||
|
YouTube: https://www.youtube.com/javidx9
|
||||||
|
Discord: https://discord.gg/WhwHUMV
|
||||||
|
Twitter: https://www.twitter.com/javidx9
|
||||||
|
Twitch: https://www.twitch.tv/javidx9
|
||||||
|
GitHub: https://www.github.com/onelonecoder
|
||||||
|
Homepage: https://www.onelonecoder.com
|
||||||
|
|
||||||
|
Author |
||||||
|
~~~~~~ |
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022 |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#define OLC_PGE_APPLICATION |
||||||
|
#include "olcPixelGameEngine.h" |
||||||
|
|
||||||
|
// PGEX Require the presence of olc::PixelGameEngine
|
||||||
|
#define OLC_PGEX_QUICKGUI |
||||||
|
#include "extensions/olcPGEX_QuickGUI.h" |
||||||
|
|
||||||
|
|
||||||
|
class olcDemo_QuickGUI : public olc::PixelGameEngine |
||||||
|
{ |
||||||
|
public: |
||||||
|
olcDemo_QuickGUI() |
||||||
|
{ |
||||||
|
sAppName = "olcDemo_QuickGUI"; |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
olc::QuickGUI::Manager guiManager; |
||||||
|
|
||||||
|
olc::QuickGUI::Slider* guiSlider1 = nullptr; |
||||||
|
olc::QuickGUI::Slider* guiSlider2 = nullptr; |
||||||
|
olc::QuickGUI::Slider* guiSlider3 = nullptr; |
||||||
|
|
||||||
|
olc::QuickGUI::Button* guiButton1 = nullptr; |
||||||
|
olc::QuickGUI::Button* guiButton2 = nullptr; |
||||||
|
olc::QuickGUI::Button* guiButton3 = nullptr; |
||||||
|
|
||||||
|
olc::QuickGUI::Slider* guiThemeColourR = nullptr; |
||||||
|
olc::QuickGUI::Slider* guiThemeColourG = nullptr; |
||||||
|
olc::QuickGUI::Slider* guiThemeColourB = nullptr; |
||||||
|
|
||||||
|
olc::QuickGUI::Label* guiLabelR = nullptr; |
||||||
|
olc::QuickGUI::Label* guiLabelG = nullptr; |
||||||
|
olc::QuickGUI::Label* guiLabelB = nullptr; |
||||||
|
|
||||||
|
olc::QuickGUI::CheckBox* guiCheckBox1 = nullptr; |
||||||
|
|
||||||
|
olc::QuickGUI::TextBox* guiTextBox1 = nullptr; |
||||||
|
olc::QuickGUI::TextBox* guiTextBox2 = nullptr; |
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::string> listExample; |
||||||
|
olc::QuickGUI::ListBox* guiListBox = nullptr; |
||||||
|
|
||||||
|
public: |
||||||
|
bool OnUserCreate() override |
||||||
|
{ |
||||||
|
// Horizontal Slider
|
||||||
|
guiSlider1 = new olc::QuickGUI::Slider(guiManager,
|
||||||
|
{ 30.0f, 10.0f }, { 246.0f, 10.0f }, 0, 100, 50); |
||||||
|
// Diagonal Slider!
|
||||||
|
guiSlider2 = new olc::QuickGUI::Slider(guiManager,
|
||||||
|
{ 20.0f, 20.0f }, { 120.0f, 120.0f }, 0, 100, 50); |
||||||
|
// Vertical Slider
|
||||||
|
guiSlider3 = new olc::QuickGUI::Slider(guiManager,
|
||||||
|
{ 10.0f, 30.0f }, { 10.0f, 230.0f }, 0, 100, 50); |
||||||
|
|
||||||
|
// Theme colour slider - Red
|
||||||
|
guiThemeColourR = new olc::QuickGUI::Slider(guiManager, |
||||||
|
{ 150.0f, 30.0f }, { 246.0f, 30.0f }, 0, 255, 0); |
||||||
|
// Theme colour slider - Green
|
||||||
|
guiThemeColourG = new olc::QuickGUI::Slider(guiManager, |
||||||
|
{ 150.0f, 50.0f }, { 246.0f, 50.0f }, 0, 255, 0); |
||||||
|
// Theme colour slider - Blue
|
||||||
|
guiThemeColourB = new olc::QuickGUI::Slider(guiManager, |
||||||
|
{ 150.0f, 70.0f }, { 246.0f, 70.0f }, 0, 255, 128); |
||||||
|
|
||||||
|
// Labels for theme colour sliders
|
||||||
|
guiLabelR = new olc::QuickGUI::Label(guiManager, |
||||||
|
"Red:", { 80.0f, 22.0f }, { 50.0f, 16.0f }); |
||||||
|
guiLabelG = new olc::QuickGUI::Label(guiManager, |
||||||
|
"Green:", { 80.0f, 42.0f }, { 50.0f, 16.0f }); |
||||||
|
guiLabelB = new olc::QuickGUI::Label(guiManager, |
||||||
|
"Blue:", { 80.0f, 62.0f }, { 50.0f, 16.0f }); |
||||||
|
|
||||||
|
// Customize how the labels look
|
||||||
|
guiLabelB->nAlign = olc::QuickGUI::Label::Alignment::Right;
|
||||||
|
guiLabelG->nAlign = olc::QuickGUI::Label::Alignment::Right; |
||||||
|
guiLabelG->bHasBorder = true;
|
||||||
|
guiLabelR->nAlign = olc::QuickGUI::Label::Alignment::Right; |
||||||
|
guiLabelR->bHasBorder = true; |
||||||
|
guiLabelR->bHasBackground = true; |
||||||
|
|
||||||
|
// Some Buttons, 1 is just a thing, 2 has its text updated and 3 resets the theme
|
||||||
|
guiButton1 = new olc::QuickGUI::Button(guiManager, |
||||||
|
"Button 1", { 30.0f, 150.0f }, { 100.0f, 16.0f }); |
||||||
|
guiButton2 = new olc::QuickGUI::Button(guiManager, |
||||||
|
"Button 2", { 30.0f, 170.0f }, { 100.0f, 16.0f }); |
||||||
|
guiButton3 = new olc::QuickGUI::Button(guiManager, |
||||||
|
"Reset Theme", { 30.0f, 190.0f }, { 100.0f, 16.0f }); |
||||||
|
|
||||||
|
// A CheckBox, switches between sprite or decal drawing
|
||||||
|
guiCheckBox1 = new olc::QuickGUI::CheckBox(guiManager, |
||||||
|
"Use Decals", false, { 30.0f, 210.0f }, { 100.0f, 16.0f }); |
||||||
|
|
||||||
|
// TextBox, allows basic text entry
|
||||||
|
guiTextBox1 = new olc::QuickGUI::TextBox(guiManager, |
||||||
|
"", { 150.0f, 140.0f }, { 100.0f, 16.0f }); |
||||||
|
guiTextBox2 = new olc::QuickGUI::TextBox(guiManager, |
||||||
|
"0.04", { 150.0f, 160.0f }, { 100.0f, 16.0f }); |
||||||
|
|
||||||
|
listExample.push_back("Item 1"); |
||||||
|
listExample.push_back("Item 2"); |
||||||
|
listExample.push_back("Item 3"); |
||||||
|
listExample.push_back("Item 4"); |
||||||
|
listExample.push_back("Item 5"); |
||||||
|
listExample.push_back("Item 6"); |
||||||
|
|
||||||
|
guiListBox = new olc::QuickGUI::ListBox(guiManager, |
||||||
|
listExample, { 150.0f, 180.0f }, { 100.0f, 54.0f }); |
||||||
|
|
||||||
|
// but but but.... waaaaaaaaahaaaaaaaa.... where do I delete these horrible
|
||||||
|
// pointers??? I just can't accept that addressable memory exists and it makes
|
||||||
|
// me feel really insecure!
|
||||||
|
//
|
||||||
|
// By default olc::QuickGUI::Manager will delete any Controls added to it, so you
|
||||||
|
// dont have to. If you must obfuscate your program with smart pointers, or require
|
||||||
|
// that you are in rage-control of your memory at all times, construct the Manager
|
||||||
|
// with false as the argument - then its all up to you buddy.
|
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool OnUserUpdate(float fElapsedTime) override |
||||||
|
{ |
||||||
|
// We must update the manager at some point each frame. Values of controls
|
||||||
|
// are only valid AFTER this call to update()
|
||||||
|
guiManager.Update(this); |
||||||
|
|
||||||
|
// Some silly examples...
|
||||||
|
|
||||||
|
// 001) The "theme colour" can be set from slider values
|
||||||
|
guiManager.colNormal = olc::Pixel( |
||||||
|
uint8_t(guiThemeColourR->fValue), |
||||||
|
uint8_t(guiThemeColourG->fValue), |
||||||
|
uint8_t(guiThemeColourB->fValue)); |
||||||
|
|
||||||
|
// 002) Display Slider 1 value on Button 2
|
||||||
|
guiButton2->sText = "Button " + std::to_string(int32_t(guiSlider1->fValue)); |
||||||
|
|
||||||
|
// 003) Check if "Reset Theme" button is pressed, if it is, well, err...
|
||||||
|
if (guiButton3->bPressed) |
||||||
|
{ |
||||||
|
// ...reset the theme! (which also updates the sliders)
|
||||||
|
guiThemeColourR->fValue = 0.0f; |
||||||
|
guiThemeColourG->fValue = 0.0f; |
||||||
|
guiThemeColourB->fValue = 128.0f; |
||||||
|
} |
||||||
|
|
||||||
|
// 004) Link Slider 2 and Slider 3 together
|
||||||
|
if(guiSlider2->bHeld) |
||||||
|
guiSlider3->fValue = 100.0f - guiSlider2->fValue; |
||||||
|
if (guiSlider3->bHeld) |
||||||
|
guiSlider2->fValue = 100.0f - guiSlider3->fValue; |
||||||
|
|
||||||
|
|
||||||
|
// Draw Stuff!
|
||||||
|
Clear(olc::BLACK); |
||||||
|
|
||||||
|
// 005) Use checkbox to determine rendering mode
|
||||||
|
if (guiCheckBox1->bChecked) |
||||||
|
guiManager.DrawDecal(this); |
||||||
|
else |
||||||
|
guiManager.Draw(this); |
||||||
|
return true; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
int main() |
||||||
|
{ |
||||||
|
olcDemo_QuickGUI demo; |
||||||
|
if (demo.Construct(256, 240, 4, 4)) |
||||||
|
demo.Start(); |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,212 @@ |
|||||||
|
/*
|
||||||
|
OneLoneCoder - Animate2D v1.00 |
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
Handles animated Sprites efficiently |
||||||
|
|
||||||
|
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2018 - 2022 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 |
||||||
|
~~~~~ |
||||||
|
YouTube: https://www.youtube.com/javidx9
|
||||||
|
Discord: https://discord.gg/WhwHUMV
|
||||||
|
Twitter: https://www.twitter.com/javidx9
|
||||||
|
Twitch: https://www.twitch.tv/javidx9
|
||||||
|
GitHub: https://www.github.com/onelonecoder
|
||||||
|
Homepage: https://www.onelonecoder.com
|
||||||
|
|
||||||
|
Author |
||||||
|
~~~~~~ |
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022 |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "olcPixelGameEngine.h" |
||||||
|
#include "utilities/olcUTIL_Geometry2D.h" |
||||||
|
|
||||||
|
namespace olc::utils::Animate2D |
||||||
|
{ |
||||||
|
// This class rerpresents a valid "frame" of an animation. It could be from any image source, and
|
||||||
|
// any location withing that image source. Once it's constructed, it's advised not to change it, as
|
||||||
|
// this likely indicates a usage bug.
|
||||||
|
//
|
||||||
|
// "Sourceless" frames are valid too - this is useful if you have a particular animation set, but
|
||||||
|
// want to apply it to a variety of sources, for example sprite maps with common layouts.
|
||||||
|
class Frame |
||||||
|
{ |
||||||
|
public: |
||||||
|
inline Frame(const olc::Renderable* gfxSource, const geom2d::rect<int32_t>& rectSource = { {0,0},{0,0} }) |
||||||
|
: gfxImageSource(gfxSource), rectFrameSource(rectSource) |
||||||
|
{ |
||||||
|
// If no source rectangle specified then use whole image source. Ignore in the event
|
||||||
|
// that a frame is set up as source-less
|
||||||
|
if(gfxSource && rectFrameSource.size.x == 0) |
||||||
|
rectFrameSource.size = gfxSource->Sprite()->Size(); |
||||||
|
} |
||||||
|
|
||||||
|
inline const olc::Renderable* GetSourceImage() const |
||||||
|
{ |
||||||
|
return gfxImageSource; |
||||||
|
} |
||||||
|
|
||||||
|
inline const geom2d::rect<int32_t>& GetSourceRect() const |
||||||
|
{ |
||||||
|
return rectFrameSource; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
const olc::Renderable* gfxImageSource; |
||||||
|
geom2d::rect<int32_t> rectFrameSource; |
||||||
|
}; |
||||||
|
|
||||||
|
// Animation styles decide how the frames should be traversed in time
|
||||||
|
enum class Style : uint8_t |
||||||
|
{ |
||||||
|
Repeat, // Cycle through, go back to beginning
|
||||||
|
OneShot, // Play once and suspend on final frame
|
||||||
|
PingPong, // Traverse through forwards, then backwards
|
||||||
|
Reverse, // Cycle through sequence backwards
|
||||||
|
}; |
||||||
|
|
||||||
|
class FrameSequence |
||||||
|
{ |
||||||
|
public: |
||||||
|
// Constructs a sequence of frames with a duration and a traversal style
|
||||||
|
inline FrameSequence(const float fFrameDuration = 0.1f, const Style nStyle = Style::Repeat) |
||||||
|
{ |
||||||
|
m_fFrameDuration = fFrameDuration; |
||||||
|
m_fFrameRate = 1.0f / m_fFrameDuration; |
||||||
|
m_nStyle = nStyle; |
||||||
|
} |
||||||
|
|
||||||
|
// Adds a frame to this sequence
|
||||||
|
inline void AddFrame(const Frame& frame) |
||||||
|
{ |
||||||
|
m_vFrames.emplace_back(frame); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns a Frame Object for a given time into an animation
|
||||||
|
inline const Frame& GetFrame(const float fTime) const |
||||||
|
{ |
||||||
|
return m_vFrames[ConvertTimeToFrame(fTime)]; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
Style m_nStyle; |
||||||
|
std::vector<Frame> m_vFrames; |
||||||
|
float m_fFrameDuration = 0.1f; |
||||||
|
float m_fFrameRate = 10.0f; |
||||||
|
|
||||||
|
inline const size_t ConvertTimeToFrame(const float fTime) const |
||||||
|
{ |
||||||
|
switch (m_nStyle) |
||||||
|
{ |
||||||
|
case Style::Repeat: |
||||||
|
return size_t(fTime * m_fFrameRate) % m_vFrames.size(); |
||||||
|
break; |
||||||
|
case Style::OneShot: |
||||||
|
return std::clamp(size_t(fTime * m_fFrameRate), size_t(0), m_vFrames.size() - 1); |
||||||
|
break; |
||||||
|
case Style::PingPong: |
||||||
|
// TODO
|
||||||
|
break; |
||||||
|
case Style::Reverse: |
||||||
|
return (m_vFrames.size() - 1) - (size_t(fTime * m_fFrameRate) % m_vFrames.size()); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// A Animate2D::State is a lightweight token that can be attached to things
|
||||||
|
// that are animated. Under normal circumstances, it is never updated manually
|
||||||
|
struct AnimationState |
||||||
|
{ |
||||||
|
private: |
||||||
|
size_t nIndex = 0; |
||||||
|
float fTime = 0.0f;
|
||||||
|
template<typename StatesEnum> |
||||||
|
friend class Animation; |
||||||
|
}; |
||||||
|
|
||||||
|
// Animation object holds a group of frame sequences and can mutate an AnimationState token
|
||||||
|
template<typename StatesEnum> |
||||||
|
class Animation |
||||||
|
{ |
||||||
|
public: |
||||||
|
Animation() = default; |
||||||
|
|
||||||
|
// Change an animation state token to a new state
|
||||||
|
inline bool ChangeState(AnimationState& state, const StatesEnum& sStateName) const |
||||||
|
{ |
||||||
|
size_t idx = m_mapStateIndices.at(sStateName); |
||||||
|
if (state.nIndex != idx) |
||||||
|
{ |
||||||
|
state.fTime = 0.0f; |
||||||
|
state.nIndex = idx; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// Update an animation state token
|
||||||
|
inline void UpdateState(AnimationState& state, const float fElapsedTime) const |
||||||
|
{ |
||||||
|
state.fTime += fElapsedTime; |
||||||
|
} |
||||||
|
|
||||||
|
public: |
||||||
|
// Retrieve the frame information for a given animation state
|
||||||
|
inline const Frame& GetFrame(const AnimationState& state) const |
||||||
|
{ |
||||||
|
return m_vSequences[state.nIndex].GetFrame(state.fTime); |
||||||
|
} |
||||||
|
|
||||||
|
public: |
||||||
|
// Add a named Frame sequence as a state
|
||||||
|
inline void AddState(const StatesEnum& sStateName, const FrameSequence& sequence) |
||||||
|
{ |
||||||
|
m_vSequences.emplace_back(sequence); |
||||||
|
m_mapStateIndices[sStateName] = m_vSequences.size() - 1; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
std::vector<FrameSequence> m_vSequences; |
||||||
|
std::unordered_map<StatesEnum, size_t> m_mapStateIndices; |
||||||
|
}; |
||||||
|
} |
Loading…
Reference in new issue