10 bool loop,
float speed) {
11 int idx =
static_cast<int>(m_states.size());
12 m_states.push_back({name, clip, loop, speed});
17 bool waitForClipEnd) {
18 int idx =
static_cast<int>(m_transitions.size());
24 m_transitions.push_back(std::move(t));
29 const std::string& paramName,
32 if (transIndex >= 0 && transIndex <
static_cast<int>(m_transitions.size())) {
33 m_transitions[transIndex].conditions.push_back({paramName, op, threshold});
38 m_currentState = stateIndex;
44 m_params[name] = value;
48 m_params[name] = value;
52 m_params[name] = value;
56 static const std::string empty;
57 if (m_currentState >= 0 && m_currentState <
static_cast<int>(m_states.size()))
58 return m_states[m_currentState].name;
65 if (m_currentState < 0 || m_states.empty())
70 if (!currentClip)
return req;
76 if (m_currentTime > currentClip->
duration) {
77 if (currentState.
loop) {
78 m_currentTime = std::fmod(m_currentTime, currentClip->
duration);
80 m_currentTime = currentClip->
duration;
90 if (m_prevTime > prevClip->
duration) {
92 m_prevTime = std::fmod(m_prevTime, prevClip->
duration);
98 m_blendElapsed += deltaTime;
99 float t = std::min(m_blendElapsed / m_blendDuration, 1.0f);
104 req.
clipA = currentClip;
105 req.
timeA = m_currentTime;
111 req.
clipA = prevClip;
112 req.
timeA = m_prevTime;
113 req.
clipB = currentClip;
114 req.
timeB = m_currentTime;
121 bool clipEnded = !currentState.
loop &&
122 m_currentTime >= currentClip->
duration - 0.001f;
124 for (
int i = 0; i < static_cast<int>(m_transitions.size()); ++i) {
133 if (EvaluateConditions(trans)) {
138 req.
clipA = currentClip;
139 req.
timeA = m_prevTime;
141 req.
timeB = m_currentTime;
148 req.
clipA = currentClip;
149 req.
timeA = m_currentTime;
160 auto it = m_params.find(cond.paramName);
161 if (it == m_params.end())
164 if (!CompareParam(it->second, cond.op, cond.threshold))
173 auto toFloat = [](
const ParamValue& v) ->
float {
174 if (std::holds_alternative<bool>(v))
175 return std::get<bool>(v) ? 1.0f : 0.0f;
176 if (std::holds_alternative<float>(v))
177 return std::get<float>(v);
178 if (std::holds_alternative<int>(v))
179 return static_cast<float>(std::get<int>(v));
183 float a = toFloat(param);
184 float b = toFloat(threshold);
197void AnimationStateMachine::StartTransition(
int transIndex) {
198 const AnimationTransition& trans = m_transitions[transIndex];
200 m_prevState = m_currentState;
201 m_prevTime = m_currentTime;
203 m_currentState = trans.
toState;
204 m_currentTime = 0.0f;
207 m_blendElapsed = 0.0f;
208 m_blendDuration = trans.blendDuration;
210 SLEAK_INFO(
"AnimSM: {} -> {} (blend {:.2f}s)",
211 m_states[m_prevState].name,
212 m_states[m_currentState].name,
void SetDefaultState(int stateIndex)
Sets the state the machine starts in, with no blend.
int AddTransition(int from, int to, float blendDuration=0.3f, bool waitForClipEnd=false)
Adds a transition edge from -> to, returning its index.
SampleRequest Update(float deltaTime)
Advances playback/blend time, evaluates transitions, and returns what to sample this frame.
void SetFloat(const std::string &name, float value)
void SetBool(const std::string &name, bool value)
void AddTransitionCondition(int transIndex, const std::string ¶mName, CompareOp op, ParamValue threshold)
Appends a guard condition to the transition at transIndex.
int AddState(const std::string &name, AnimationClip *clip, bool loop=true, float speed=1.0f)
Adds a state bound to clip, returning its index for use in AddTransition.
void SetInt(const std::string &name, int value)
const std::string & GetCurrentStateName() const
Root namespace for everything the engine exposes.
std::variant< bool, float, int > ParamValue
std::vector< TransitionCondition > conditions