Skip to content

Instantly share code, notes, and snippets.

@Ryoga-exe
Last active August 16, 2023 16:58
Show Gist options
  • Save Ryoga-exe/0484cd78dd382a035f901936ab5b3a16 to your computer and use it in GitHub Desktop.
Save Ryoga-exe/0484cd78dd382a035f901936ab5b3a16 to your computer and use it in GitHub Desktop.
# pragma once
# include <Siv3D.hpp>
namespace Tween
{
struct transition_t
{
double to;
Duration begin, end;
std::function<double(double)> easingFunction = EaseInLinear;
};
// template <typename T>
class Timeline
{
public:
explicit Timeline(const double initialValue = 0.0, bool startImmediately = false)
:m_initialValue(initialValue), m_value(initialValue), m_isRunning(false)
{
if (startImmediately)
{
m_isRunning = true;
m_stopwatch.start();
}
}
~Timeline()
{
}
Timeline& then(const double to, const Duration dur = 0.0s, double easingFunction(double) = EaseInLinear)
{
addTransition(to, dur, easingFunction);
return *this;
}
Timeline& wait(const Duration dur)
{
const double prevTarget = (m_transitions.isEmpty() ? 0 : m_transitions.back().to);
addTransition(prevTarget, dur, EaseInLinear);
return *this;
}
bool update()
{
if (m_transitions.isEmpty()) return false;
if (m_transitions.size() <= m_currentIndex)
{
m_value = m_transitions.back().to;
m_isRunning = false;
return false;
}
if (m_transitions[m_currentIndex].end < m_stopwatch.elapsed()) {
m_currentIndex++;
if (m_transitions.size() <= m_currentIndex)
{
m_value = m_transitions.back().to;
m_isRunning = false;
return false;
}
}
const double t = (m_transitions[m_currentIndex].end - m_transitions[m_currentIndex].begin).count();
const double c = m_transitions[m_currentIndex].to - (m_currentIndex > 0 ? m_transitions[m_currentIndex - 1].to : m_initialValue);
double e = 1;
if (t > 0) e = m_transitions[m_currentIndex].easingFunction(Min((m_stopwatch.sF() - m_transitions[m_currentIndex].begin.count()), t) / t);
m_value = c*e + (m_currentIndex > 0 ? m_transitions[m_currentIndex - 1].to : m_initialValue);
return true;
}
void start() { m_isRunning = true; m_stopwatch.start(); }
void clear()
{
m_currentIndex = 0;
m_initialValue = 0.0;
m_transitions.clear();
m_isRunning = false;
m_value = 0.0;
m_stopwatch.reset();
}
void restart()
{
m_currentIndex = 0;
m_value = m_initialValue;
m_stopwatch.restart();
}
bool isEmpty() { return m_transitions.isEmpty(); }
bool isRunning() { return m_isRunning; }
size_t size() { return m_transitions.size(); }
double getValue() { return m_value; }
operator double() { return getValue(); }
Duration elapsed() { return m_stopwatch.elapsed(); }
private:
void addTransition(const double to, const Duration dur, double easingFunction(double))
{
const Duration prevDur = (m_transitions.isEmpty() ? 0.0s : m_transitions.back().end);
m_transitions.emplace_back(transition_t{ to, prevDur, prevDur + dur, easingFunction });
}
Stopwatch m_stopwatch;
Array<transition_t> m_transitions;
int32 m_currentIndex = 0;
double m_initialValue = 0.0;
double m_value = 0.0;
bool m_isRunning;
};
}
# include <Siv3D.hpp>
# include "Tween.hpp"
void Main()
{
Scene::SetBackground(Palette::White);
Tween::Timeline timelineEase(600), timelineLinear(600);
timelineEase
.then(200, 5.0s, EaseInOutSine)
.then(400, 5.0s, EaseInOutElastic)
.wait(1.0s)
.then(600, 5.0s, EaseOutBounce);
timelineLinear
.then(200, 5.0s)
.then(400, 5.0s)
.wait(1.0s)
.then(600, 5.0s);
constexpr Size canvasSize(800, 600);
constexpr int32 thickness = 4;
Image image(canvasSize, Palette::White);
DynamicTexture texture(image);
timelineEase.start();
timelineLinear.start();
Point posEase((int32)(timelineEase.elapsed().count() * 50), (int32)timelineEase);
Point posLinear = posEase;
while (System::Update())
{
ClearPrint();
timelineEase.update();
timelineLinear.update();
const Point fromL = posLinear;
posLinear = { (int32)(timelineLinear.elapsed().count() * 50), (int32)timelineLinear };
const Point toL = posLinear;
Line(fromL, toL).overwrite(image, thickness, Palette::Cyan);
const Point fromE = posEase;
posEase = { (int32)(timelineEase.elapsed().count() * 50), (int32)timelineEase };
const Point toE = posEase;
Line(fromE, toE).overwrite(image, thickness, Palette::Orange);
texture.fill(image);
if (KeyC.down())
{
timelineEase.restart();
timelineLinear.restart();
posLinear = { (int32)(timelineLinear.elapsed().count() * 50), (int32)timelineLinear };
posEase = { (int32)(timelineEase.elapsed().count() * 50), (int32)timelineEase };
image.fill(Palette::White);
texture.fill(image);
}
texture.draw();
Print << timelineEase.elapsed();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment