# Basic animation support in new simulation system.
Movement speed. Improved accuracy of walking. This was SVN commit r7313.
This commit is contained in:
parent
ea4c22fc0e
commit
d609a9f81c
@ -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);
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user