1
0
forked from 0ad/0ad

# Basic animation support in new simulation system.

Movement speed.
Improved accuracy of walking.

This was SVN commit r7313.
This commit is contained in:
Ykkrosh 2010-02-07 20:06:16 +00:00
parent ea4c22fc0e
commit d609a9f81c
13 changed files with 155 additions and 22 deletions

View File

@ -36,6 +36,8 @@ UnitAI.prototype.Init = function()
this.attackRechargeTime = 0;
// Timer for AttackTimeout
this.attackTimer = undefined;
this.nextAnimation = undefined;
};
UnitAI.prototype.OnDestroy = function()
@ -47,15 +49,27 @@ UnitAI.prototype.OnDestroy = function()
}
};
UnitAI.prototype.OnTurnStart = function()
{
if (this.nextAnimation)
{
this.SelectAnimation(this.nextAnimation.name, this.nextAnimation.once, this.nextAnimation.speed);
this.nextAnimation = undefined;
}
};
//// Interface functions ////
UnitAI.prototype.Walk = function(x, z)
{
var motion = Engine.QueryInterface(this.entity, IID_UnitMotion);
if (!motion)
var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
if (!cmpMotion)
return;
motion.MoveToPoint(x, z, 0, 0);
this.SelectAnimation("walk", false, cmpMotion.GetSpeed());
cmpMotion.MoveToPoint(x, z, 0, 0);
this.state = STATE_WALKING;
};
@ -85,8 +99,31 @@ UnitAI.prototype.Attack = function(target)
this.state = STATE_ATTACKING;
};
//// Message handlers ////
UnitAI.prototype.OnMotionStopped = function()
{
this.SelectAnimationDelayed("idle");
};
//// Private functions ////
UnitAI.prototype.SelectAnimation = function(name, once, speed)
{
var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
if (!cmpVisual)
return;
cmpVisual.SelectAnimation(name, once, speed);
this.nextAnimation = undefined;
};
UnitAI.prototype.SelectAnimationDelayed = function(name, once, speed)
{
this.nextAnimation = { "name": name, "once": once, "speed": speed };
}
UnitAI.prototype.MoveToTarget = function(target)
{
var cmpPositionTarget = Engine.QueryInterface(target, IID_Position);
@ -95,8 +132,10 @@ UnitAI.prototype.MoveToTarget = function(target)
var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
this.SelectAnimation("walk", false, cmpMotion.GetSpeed());
var pos = cmpPositionTarget.GetPosition();
cmpMotion.MoveToPoint(pos.x, pos.z, 0, 8);
cmpMotion.MoveToPoint(pos.x, pos.z, 0, 1);
};
UnitAI.prototype.AttackTimeout = function(data)
@ -126,9 +165,13 @@ UnitAI.prototype.AttackTimeout = function(data)
// Otherwise it's impossible to reach the target, so give up
// and switch back to idle
this.state = STATE_IDLE;
this.SelectAnimation("idle");
return;
}
// Play the attack animation
this.SelectAnimationDelayed("melee", false, 1);
// Hit the target
cmpAttack.PerformAttack(data.target);

View File

@ -240,14 +240,18 @@ bool CGame::Update(double deltaTime, bool doInterpolate)
deltaTime *= m_SimRate;
bool ok = m_Simulation->Update(deltaTime);
bool ok = true;
if (g_UseSimulation2)
m_Simulation2->Update(deltaTime);
else
ok = m_Simulation->Update(deltaTime);
if (doInterpolate)
{
m_Simulation->Interpolate(deltaTime);
if (g_UseSimulation2)
m_Simulation2->Interpolate(deltaTime);
else
m_Simulation->Interpolate(deltaTime);
}
g_GUI->SendEventToAll("SimulationUpdate");

View File

@ -63,11 +63,12 @@ class CMessageInterpolate : public CMessage
public:
DEFAULT_MESSAGE_IMPL(Interpolate)
CMessageInterpolate(float offset) :
offset(offset)
CMessageInterpolate(float frameTime, float offset) :
frameTime(frameTime), offset(offset)
{
}
float frameTime;
float offset;
};
@ -139,5 +140,14 @@ public:
entity_angle_t a;
};
class CMessageMotionStopped : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(MotionStopped)
CMessageMotionStopped()
{
}
};
#endif // INCLUDED_MESSAGETYPES

View File

@ -156,12 +156,12 @@ void CSimulation2Impl::Update(float frameTime)
}
}
void CSimulation2Impl::Interpolate(float UNUSED(frameTime))
void CSimulation2Impl::Interpolate(float frameTime)
{
// TODO: Use CTurnManager
double turnLength = TURN_LENGTH / 1000.0;
float offset = clamp(m_DeltaTime / turnLength + 1.0, 0.0, 1.0);
m_ComponentManager.BroadcastMessage(CMessageInterpolate(offset));
m_ComponentManager.BroadcastMessage(CMessageInterpolate(frameTime, offset));
}
////////////////////////////////////////////////////////////////

View File

@ -37,6 +37,7 @@ MESSAGE(RenderSubmit) // non-deterministic (use with caution)
MESSAGE(Destroy)
MESSAGE(OwnershipChanged)
MESSAGE(PositionChanged)
MESSAGE(MotionStopped)
// TemplateManager must come before all other (non-test) components,
// so that it is the first to be (de)serialized

View File

@ -702,6 +702,14 @@ void CCmpPathfinder::ComputePath(entity_pos_t x0, entity_pos_t z0, const Goal& g
return;
}
// If we're already at the goal tile, then move directly to the exact goal coordinates
if (AtGoal(i0, j0, state.iGoal, state.jGoal, state.rGoal, state.aimingInwards))
{
Waypoint w = { goal.x, goal.z, 0 };
path.m_Waypoints.push_back(w);
return;
}
state.steps = 0;
state.tiles = new Grid<PathfindTile>(m_MapSize, m_MapSize);
@ -761,8 +769,17 @@ void CCmpPathfinder::ComputePath(entity_pos_t x0, entity_pos_t z0, const Goal& g
while (ip != i0 || jp != j0)
{
PathfindTile& n = state.tiles->get(ip, jp);
// Pick the exact point if it's the goal tile, else the tile's centre
entity_pos_t x, z;
TileCenter(ip, jp, x, z);
if (ip == state.iGoal && jp == state.jGoal)
{
x = goal.x;
z = goal.z;
}
else
{
TileCenter(ip, jp, x, z);
}
Waypoint w = { x, z, n.cost };
path.m_Waypoints.push_back(w);

View File

@ -44,11 +44,12 @@ public:
ICmpPathfinder::Path m_Path;
entity_pos_t m_TargetX, m_TargetZ; // these values contain undefined junk if !HasTarget
virtual void Init(const CSimContext& context, const CParamNode& UNUSED(paramNode))
virtual void Init(const CSimContext& context, const CParamNode& paramNode)
{
m_Context = &context;
m_Speed = CFixed_23_8::FromInt(4);
m_HasTarget = false;
m_Speed = paramNode.GetChild("WalkSpeed").ToFixed();
}
virtual void Deinit(const CSimContext& UNUSED(context))
@ -122,11 +123,20 @@ public:
goal.maxRadius = maxRadius;
cmpPathfinder->SetDebugPath(pos.X, pos.Z, goal);
cmpPathfinder->ComputePath(pos.X, pos.Z, goal, m_Path);
if (!m_Path.m_Waypoints.empty())
// If there's no waypoints then we've stopped already, otherwise move to the first one
if (m_Path.m_Waypoints.empty())
m_Context->GetComponentManager().BroadcastMessage(CMessageMotionStopped());
else
PickNextWaypoint(pos);
}
}
virtual CFixed_23_8 GetSpeed()
{
return m_Speed;
}
void Move(const CSimContext& context, CFixed_23_8 dt);
void PickNextWaypoint(const CFixedVector3D& pos);
@ -168,6 +178,7 @@ void CCmpUnitMotion::Move(const CSimContext& context, CFixed_23_8 dt)
{
cmpPosition->MoveTo(target.X, target.Z);
m_HasTarget = false;
context.GetComponentManager().BroadcastMessage(CMessageMotionStopped());
return;
}

View File

@ -47,6 +47,13 @@ public:
DEFAULT_COMPONENT_ALLOCATOR(VisualActor)
CUnit* m_Unit;
// Current animation state
std::string m_AnimName;
bool m_AnimOnce;
float m_AnimSpeed;
CCmpVisualActor() :
m_Unit(NULL)
{
@ -63,6 +70,8 @@ public:
return;
}
SelectAnimation("idle", false, 0.f);
m_Unit->SetID(GetEntityId()); // TODO: is it safe to be using entity IDs for unit IDs?
}
@ -93,6 +102,8 @@ public:
// variations with a 16-bit RNG seed (selected randomly when creating new units, or
// when someone hits the "randomise" button in the map editor), only overridden with
// a list of strings if it really needs to be a specific variation.
// TODO: store animation state
}
virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
@ -106,8 +117,8 @@ public:
{
case MT_Interpolate:
{
float offset = static_cast<const CMessageInterpolate&> (msg).offset;
Interpolate(context, offset);
const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
Interpolate(context, msgData.frameTime, msgData.offset);
break;
}
case MT_RenderSubmit:
@ -133,16 +144,29 @@ public:
return m_Unit->GetModel()->GetBounds();
}
private:
void Interpolate(const CSimContext& context, float frameOffset);
void RenderSubmit(const CSimContext& context, SceneCollector& collector, const CFrustum& frustum, bool culling);
virtual void SelectAnimation(std::string name, bool once, float speed)
{
if (!m_Unit)
return;
CUnit* m_Unit;
if (!isfinite(speed) || speed < 0) // JS 'undefined' converts to NaN, which causes Bad Things
speed = 0;
m_AnimName = name;
m_AnimOnce = once;
m_AnimSpeed = speed;
m_Unit->SetAnimationState(name, once, speed);
}
private:
void Interpolate(const CSimContext& context, float frameTime, float frameOffset);
void RenderSubmit(const CSimContext& context, SceneCollector& collector, const CFrustum& frustum, bool culling);
};
REGISTER_COMPONENT_TYPE(VisualActor)
void CCmpVisualActor::Interpolate(const CSimContext& context, float frameOffset)
void CCmpVisualActor::Interpolate(const CSimContext& context, float frameTime, float frameOffset)
{
if (m_Unit == NULL)
return;
@ -160,6 +184,7 @@ void CCmpVisualActor::Interpolate(const CSimContext& context, float frameOffset)
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset));
m_Unit->GetModel()->SetTransform(transform);
m_Unit->UpdateModel(frameTime);
}
void CCmpVisualActor::RenderSubmit(const CSimContext& UNUSED(context), SceneCollector& collector, const CFrustum& frustum, bool culling)

View File

@ -23,4 +23,5 @@
BEGIN_INTERFACE_WRAPPER(UnitMotion)
DEFINE_INTERFACE_METHOD_4("MoveToPoint", void, ICmpUnitMotion, MoveToPoint, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_0("GetSpeed", CFixed_23_8, ICmpUnitMotion, GetSpeed)
END_INTERFACE_WRAPPER(UnitMotion)

View File

@ -36,6 +36,8 @@ class ICmpUnitMotion : public IComponent
public:
virtual void MoveToPoint(entity_pos_t x, entity_pos_t z, entity_pos_t minRadius, entity_pos_t maxRadius) = 0;
virtual CFixed_23_8 GetSpeed() = 0;
DECLARE_INTERFACE_TYPE(UnitMotion)
};

View File

@ -22,4 +22,5 @@
#include "simulation2/system/InterfaceScripted.h"
BEGIN_INTERFACE_WRAPPER(Visual)
DEFINE_INTERFACE_METHOD_3("SelectAnimation", void, ICmpVisual, SelectAnimation, std::string, bool, float)
END_INTERFACE_WRAPPER(Visual)

View File

@ -34,7 +34,11 @@ public:
*/
virtual CBound GetBounds() = 0;
virtual void SelectAnimation(std::string name, bool once, float speed) = 0;
DECLARE_INTERFACE_TYPE(Visual)
};
// TODO: rename this to VisualActor, because the interface is actor-specific, maybe?
#endif // INCLUDED_ICMPVISUAL

View File

@ -83,6 +83,7 @@ CMessage* CMessageUpdate::FromJSVal(ScriptInterface& scriptInterface, jsval val)
jsval CMessageInterpolate::ToJSVal(ScriptInterface& scriptInterface) const
{
TOJSVAL_SETUP();
SET_MSG_PROPERTY(frameTime);
SET_MSG_PROPERTY(offset);
return OBJECT_TO_JSVAL(obj);
}
@ -90,8 +91,9 @@ jsval CMessageInterpolate::ToJSVal(ScriptInterface& scriptInterface) const
CMessage* CMessageInterpolate::FromJSVal(ScriptInterface& scriptInterface, jsval val)
{
FROMJSVAL_SETUP();
GET_MSG_PROPERTY(float, frameTime);
GET_MSG_PROPERTY(float, offset);
return new CMessageInterpolate(offset);
return new CMessageInterpolate(frameTime, offset);
}
////////////////////////////////
@ -150,6 +152,18 @@ CMessage* CMessagePositionChanged::FromJSVal(ScriptInterface& UNUSED(scriptInter
return NULL;
}
////////////////////////////////
jsval CMessageMotionStopped::ToJSVal(ScriptInterface& UNUSED(scriptInterface)) const
{
return JSVAL_VOID;
}
CMessage* CMessageMotionStopped::FromJSVal(ScriptInterface& UNUSED(scriptInterface), jsval UNUSED(val))
{
return NULL;
}
////////////////////////////////////////////////////////////////
CMessage* CMessageFromJSVal(int mtid, ScriptInterface& scriptingInterface, jsval val)