1
0
forked from 0ad/0ad
0ad/source/graphics/UnitAnimation.cpp
Ykkrosh bdbb2bcb16 # Updated unit animation code.
Added UnitAnimation class, to act as the interface between the entity
and actor. (Currently doesn't work very well, but it does make
animations loop smoothly and sometimes kind of makes them stay
synchronised.)
Fixed corpse animation - it now plays the final frame of the death
animation before turning static.
Fixed update/interpolate timings.
Added JS function saveProfileData.
Updated ffmpeg library.

This was SVN commit r4880.
2007-02-10 03:09:52 +00:00

96 lines
2.8 KiB
C++

#include "precompiled.h"
#include "UnitAnimation.h"
#include "graphics/Model.h"
#include "graphics/SkeletonAnim.h"
#include "graphics/SkeletonAnimDef.h"
#include "graphics/Unit.h"
#include "ps/CStr.h"
namespace
{
// Randomly modify the speed, so that units won't stay perfectly
// synchronised if they're playing animations of the same length
float DesyncSpeed(float speed)
{
// const float var = 0.05f; // max fractional variation from default
// return speed * (1.f - var + 2.f*var*(rand(0, 256)/255.f));
// TODO: enable this desyncing for cases where we don't care about
// accurate looping, and just don't do it for e.g. projectile-launchers
// where we do care
return speed;
}
}
CUnitAnimation::CUnitAnimation(CUnit& unit)
: m_Unit(unit), m_State("idle"), m_Looping(true), m_Speed(0.f), m_OriginalSpeed(0.f), m_TimeToNextSync(0.f)
{
}
void CUnitAnimation::SetAnimationState(const CStr& name, bool once, float speed, bool keepSelection)
{
if (name == m_State)
return;
m_State = name;
m_Looping = !once;
m_Speed = m_OriginalSpeed = speed;
m_TimeToNextSync = 0.f;
if (! keepSelection)
m_Unit.SetEntitySelection(name);
m_Unit.SetRandomAnimation(m_State, !m_Looping, DesyncSpeed(m_Speed));
}
void CUnitAnimation::SetAnimationSync(float timeUntilActionPos)
{
// We need to finish looping our animation at the specified time from now.
// Assume it's playing at nearly the right speed, and we just need to perhaps
// shift it a little bit to stay in sync.
m_TimeToNextSync = timeUntilActionPos;
CModel* model = m_Unit.GetModel();
// Calculate the required playback speed so ActionPos coincides with timeUntilActionPos
float currentPos = model->m_AnimTime / model->m_Anim->m_AnimDef->GetDuration();
float length = (model->m_Anim->m_ActionPos - currentPos);
if (length < 0.f)
length += 1.f;
float requiredSpeed = length / m_TimeToNextSync;
// Shift in the right direction
if (requiredSpeed > m_OriginalSpeed)
m_Speed = std::min(requiredSpeed, m_OriginalSpeed*1.1f);
else if (requiredSpeed < m_OriginalSpeed)
m_Speed = std::max(requiredSpeed, m_OriginalSpeed*0.9f);
model->m_AnimSpeed = m_Speed * model->m_Anim->m_AnimDef->GetDuration() * model->m_Anim->m_Speed;
// TODO: this should use the ActionPos2, instead of totally ignoring it
m_Unit.ShowAmmunition();
}
void CUnitAnimation::Update(float time)
{
CModel* model = m_Unit.GetModel();
// Choose a new random animation if we're going to loop
if (m_Looping && model->NeedsNewAnim(time))
{
m_Unit.SetRandomAnimation(m_State, !m_Looping, DesyncSpeed(m_Speed));
}
if (m_TimeToNextSync >= 0.0 && m_TimeToNextSync-time < 0.0)
m_Unit.HideAmmunition();
m_TimeToNextSync -= time;
// TODO: props should get a new random animation once they loop, independent
// of the object they're propped onto
model->Update(time);
}