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