Skip to content

Commit 3280f71

Browse files
authored
Merge pull request #300 from OneLoneCoder/develop
PGE 2.20 + Utilities
2 parents c080589 + f467e7d commit 3280f71

10 files changed

Lines changed: 2522 additions & 20 deletions

examples/TEST_Animate2D.cpp

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
/*
2+
Example file for olcUTIL_Animate2D.h
3+
4+
License (OLC-3)
5+
~~~~~~~~~~~~~~~
6+
7+
Copyright 2018 - 2022 OneLoneCoder.com
8+
9+
Redistribution and use in source and binary forms, with or without
10+
modification, are permitted provided that the following conditions
11+
are met:
12+
13+
1. Redistributions or derivations of source code must retain the above
14+
copyright notice, this list of conditions and the following disclaimer.
15+
16+
2. Redistributions or derivative works in binary form must reproduce
17+
the above copyright notice. This list of conditions and the following
18+
disclaimer must be reproduced in the documentation and/or other
19+
materials provided with the distribution.
20+
21+
3. Neither the name of the copyright holder nor the names of its
22+
contributors may be used to endorse or promote products derived
23+
from this software without specific prior written permission.
24+
25+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36+
37+
Links
38+
~~~~~
39+
YouTube: https://www.youtube.com/javidx9
40+
Discord: https://discord.gg/WhwHUMV
41+
Twitter: https://www.twitter.com/javidx9
42+
Twitch: https://www.twitch.tv/javidx9
43+
GitHub: https://www.github.com/onelonecoder
44+
Homepage: https://www.onelonecoder.com
45+
46+
Author
47+
~~~~~~
48+
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022
49+
50+
*/
51+
52+
53+
#define OLC_PGE_APPLICATION
54+
#include "olcPixelGameEngine.h"
55+
56+
#include "utilities/olcUTIL_Animate2D.h"
57+
58+
class TEST_Animate2D : public olc::PixelGameEngine
59+
{
60+
public:
61+
TEST_Animate2D()
62+
{
63+
sAppName = "Animate2D Utility Test";
64+
}
65+
66+
// These are the states the dude can exist in, we'll need these for physics
67+
// and animation. The physics side of things moves the dude around according to state,
68+
// while the animator chooses the frames based upon state and time
69+
enum class DudeState: uint8_t
70+
{
71+
WALK_N, WALK_S, WALK_E, WALK_W, IDLE_STAND, LAUGH, CHEER, YES, NO
72+
};
73+
74+
// !! - IMPORTANT - !!
75+
// The animation set
76+
olc::utils::Animate2D::Animation<DudeState> animDude;
77+
78+
// One big sprite containing all the graphics
79+
olc::Renderable gfxAll;
80+
81+
// Small object to reprsent a dude walking around doing things
82+
struct sWalkingDude
83+
{
84+
// Which dude overall out of graophic?
85+
int32_t id = 0;
86+
// Where are they?
87+
olc::vf2d pos;
88+
// What are they doing?
89+
DudeState UserState = DudeState::IDLE_STAND;
90+
// For how long should they do it?
91+
float fTick = 0.0f;
92+
93+
// !! - IMPORTANT - !!
94+
// Animation Token - links this object's state to animator. Note
95+
// there is no 'ownership' or memory issues here, this is a token
96+
// that the animator can use to quickly get to where it needs
97+
// in order to return frames upon request for this object.
98+
olc::utils::Animate2D::AnimationState animstate;
99+
};
100+
101+
// Introducing.... The dudes!
102+
size_t nDudes = 500;
103+
std::vector<sWalkingDude> vDudes;
104+
105+
public:
106+
bool OnUserCreate() override
107+
{
108+
// For this appliaction I have a single image that contains
109+
// 28x2 unique characters, each character contains 8 animations of 3
110+
// frames each. Each frame is 26x36 pixels
111+
gfxAll.Load("./assets/MegaSprite1.png");
112+
113+
// Thats A LOT of individual graphics, but they all follow a similar pattern
114+
// because the asset was created usefully (take note certain popular asset creators)
115+
// which means we can reuse the animation without needing to define it
116+
// individually for all the "dudes" - the "cookie cutter" approach
117+
118+
// Manually construct sequences - gfxAll could in fact be nullptr for this
119+
// application, but ive kept it here for convenience
120+
olc::utils::Animate2D::FrameSequence anim_fs_walk_s;
121+
anim_fs_walk_s.AddFrame({ &gfxAll, {{0,0}, {26,36}} });
122+
anim_fs_walk_s.AddFrame({ &gfxAll, {{26,0}, {26,36}} });
123+
anim_fs_walk_s.AddFrame({ &gfxAll, {{52,0}, {26,36}} });
124+
125+
olc::utils::Animate2D::FrameSequence anim_fs_walk_w;
126+
anim_fs_walk_w.AddFrame({ &gfxAll, {{ 0,36}, {26,36}} });
127+
anim_fs_walk_w.AddFrame({ &gfxAll, {{26,36}, {26,36}} });
128+
anim_fs_walk_w.AddFrame({ &gfxAll, {{52,36}, {26,36}} });
129+
130+
olc::utils::Animate2D::FrameSequence anim_fs_walk_e;
131+
anim_fs_walk_e.AddFrame({ &gfxAll, {{ 0,72}, {26,36}} });
132+
anim_fs_walk_e.AddFrame({ &gfxAll, {{26,72}, {26,36}} });
133+
anim_fs_walk_e.AddFrame({ &gfxAll, {{52,72}, {26,36}} });
134+
135+
olc::utils::Animate2D::FrameSequence anim_fs_walk_n;
136+
anim_fs_walk_n.AddFrame({ &gfxAll, {{ 0,108}, {26,36}} });
137+
anim_fs_walk_n.AddFrame({ &gfxAll, {{26,108}, {26,36}} });
138+
anim_fs_walk_n.AddFrame({ &gfxAll, {{52,108}, {26,36}} });
139+
140+
olc::utils::Animate2D::FrameSequence anim_fs_yes;
141+
anim_fs_yes.AddFrame({ &gfxAll, {{ 0,144}, {26,36}} });
142+
anim_fs_yes.AddFrame({ &gfxAll, {{26,144}, {26,36}} });
143+
anim_fs_yes.AddFrame({ &gfxAll, {{52,144}, {26,36}} });
144+
145+
olc::utils::Animate2D::FrameSequence anim_fs_no;
146+
anim_fs_no.AddFrame({ &gfxAll, {{ 0,180}, {26,36}} });
147+
anim_fs_no.AddFrame({ &gfxAll, {{26,180}, {26,36}} });
148+
anim_fs_no.AddFrame({ &gfxAll, {{52,180}, {26,36}} });
149+
150+
olc::utils::Animate2D::FrameSequence anim_fs_laugh;
151+
anim_fs_laugh.AddFrame({ &gfxAll, {{ 0,216}, {26,36}} });
152+
anim_fs_laugh.AddFrame({ &gfxAll, {{26,216}, {26,36}} });
153+
anim_fs_laugh.AddFrame({ &gfxAll, {{52,216}, {26,36}} });
154+
155+
olc::utils::Animate2D::FrameSequence anim_fs_cheer;
156+
anim_fs_cheer.AddFrame({ &gfxAll, {{ 0,252}, {26,36}} });
157+
anim_fs_cheer.AddFrame({ &gfxAll, {{26,252}, {26,36}} });
158+
anim_fs_cheer.AddFrame({ &gfxAll, {{52,252}, {26,36}} });
159+
160+
// A special "idle" animation just consists of a single frame
161+
olc::utils::Animate2D::FrameSequence anim_fs_idle;
162+
anim_fs_idle.AddFrame({ &gfxAll, {{26,0}, {26,36}} });
163+
164+
// We have constructed teh individual sequences, now its time
165+
// to add them to an animation, along with a state name/enum.
166+
//
167+
// I have chosen to use the enum shown earlier. You could use
168+
// std::string too, which is conveninent if you need to display
169+
// the states, though potentially far less performant
170+
171+
animDude.AddState(DudeState::WALK_S, anim_fs_walk_s);
172+
animDude.AddState(DudeState::WALK_W, anim_fs_walk_w);
173+
animDude.AddState(DudeState::WALK_E, anim_fs_walk_e);
174+
animDude.AddState(DudeState::WALK_N, anim_fs_walk_n);
175+
animDude.AddState(DudeState::IDLE_STAND, anim_fs_idle);
176+
animDude.AddState(DudeState::YES, anim_fs_yes);
177+
animDude.AddState(DudeState::NO, anim_fs_no);
178+
animDude.AddState(DudeState::LAUGH, anim_fs_laugh);
179+
animDude.AddState(DudeState::CHEER, anim_fs_cheer);
180+
181+
// Initialise the dudes
182+
for (size_t n = 0; n < nDudes; n++)
183+
{
184+
sWalkingDude dude;
185+
186+
// Random dude
187+
dude.id = rand() % (28 * 2);
188+
189+
// Begin in idle state, at random location
190+
dude.UserState = DudeState::IDLE_STAND;
191+
dude.pos = { float(rand() % ScreenWidth()), float(rand() % ScreenHeight()) };
192+
193+
// The animation token needs to be updated too
194+
animDude.ChangeState(dude.animstate, dude.UserState);
195+
196+
vDudes.push_back(dude);
197+
}
198+
return true;
199+
}
200+
201+
bool OnUserUpdate(float fElapsedTime) override
202+
{
203+
// Update Dudes
204+
float fSpeed = 32.0f;
205+
206+
for (auto& dude : vDudes)
207+
{
208+
// If a dude's tick reaches 0, it will select a new state
209+
dude.fTick -= fElapsedTime;
210+
if (dude.fTick < 0.0f)
211+
{
212+
// Choose one out of the 9 randomly
213+
int nAction = rand() % 9;
214+
215+
// Choose for how long it should do it in seconds
216+
dude.fTick = (float(rand()) / float(RAND_MAX)) * 5.0f;
217+
218+
// Assign the state depending on the dice roll - (since enum
219+
// we could cast here too, but, well, meh...)
220+
if (nAction == 0) dude.UserState = DudeState::IDLE_STAND;
221+
if (nAction == 1) dude.UserState = DudeState::WALK_S;
222+
if (nAction == 2) dude.UserState = DudeState::WALK_N;
223+
if (nAction == 3) dude.UserState = DudeState::WALK_E;
224+
if (nAction == 4) dude.UserState = DudeState::WALK_W;
225+
if (nAction == 5) dude.UserState = DudeState::YES;
226+
if (nAction == 6) dude.UserState = DudeState::NO;
227+
if (nAction == 7) dude.UserState = DudeState::LAUGH;
228+
if (nAction == 8) dude.UserState = DudeState::CHEER;
229+
230+
// State has changed, so update animation token
231+
// !! - IMPORTANT - !!
232+
animDude.ChangeState(dude.animstate, dude.UserState);
233+
}
234+
235+
// Update "physics", if walking move in that direction at speed
236+
if (dude.UserState == DudeState::WALK_S) dude.pos += olc::vf2d(0, +1) * fSpeed * fElapsedTime;
237+
if (dude.UserState == DudeState::WALK_N) dude.pos += olc::vf2d(0, -1) * fSpeed * fElapsedTime;
238+
if (dude.UserState == DudeState::WALK_E) dude.pos += olc::vf2d(+1, 0) * fSpeed * fElapsedTime;
239+
if (dude.UserState == DudeState::WALK_W) dude.pos += olc::vf2d(-1, 0) * fSpeed * fElapsedTime;
240+
241+
// If walk off screen, wrap around to other side
242+
if (dude.pos.x > ScreenWidth()) dude.pos.x -= ScreenWidth();
243+
if (dude.pos.y > ScreenHeight()) dude.pos.x -= ScreenHeight();
244+
if (dude.pos.x < 0) dude.pos.x += ScreenWidth();
245+
if (dude.pos.y < 0) dude.pos.y += ScreenHeight();
246+
247+
// Update animation token every frame
248+
// !! - IMPORTANT - !!
249+
animDude.UpdateState(dude.animstate, fElapsedTime);
250+
}
251+
252+
// Render Dudes
253+
for (const auto& dude : vDudes)
254+
{
255+
// Get the frame, this contains both source image and source location rectangle
256+
// !! - IMPORTANT - !!
257+
const auto& frame = animDude.GetFrame(dude.animstate);
258+
259+
// Thats cool, but there are 28x2 dudes on the sprite sheet, so using the ID, construct
260+
// an offset to the correct dude
261+
olc::vi2d vOffset = { (dude.id % 28) * 78, (dude.id / 28) * 288 };
262+
263+
// Use DrawPartialDecal to chop out the correct dude frm the image source
264+
DrawPartialDecal(dude.pos, frame.GetSourceImage()->Decal(), frame.GetSourceRect().pos + vOffset, frame.GetSourceRect().size);
265+
}
266+
267+
// That's it
268+
return true;
269+
}
270+
};
271+
272+
int main()
273+
{
274+
TEST_Animate2D demo;
275+
if (demo.Construct(640, 480, 2, 2))
276+
demo.Start();
277+
return 0;
278+
}

0 commit comments

Comments
 (0)