From d609a9f81c7dec2534bafd1627b0c0ca34086a0e Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Sun, 7 Feb 2010 20:06:16 +0000 Subject: [PATCH] # Basic animation support in new simulation system. Movement speed. Improved accuracy of walking. This was SVN commit r7313. --- .../public/simulation/components/UnitAI.js | 51 +++++++++++++++++-- source/ps/Game.cpp | 8 ++- source/simulation2/MessageTypes.h | 14 ++++- source/simulation2/Simulation2.cpp | 4 +- source/simulation2/TypeList.h | 1 + .../simulation2/components/CCmpPathfinder.cpp | 19 ++++++- .../simulation2/components/CCmpUnitMotion.cpp | 17 +++++-- .../components/CCmpVisualActor.cpp | 39 +++++++++++--- .../simulation2/components/ICmpUnitMotion.cpp | 1 + .../simulation2/components/ICmpUnitMotion.h | 2 + source/simulation2/components/ICmpVisual.cpp | 1 + source/simulation2/components/ICmpVisual.h | 4 ++ .../scripting/MessageTypeConversions.cpp | 16 +++++- 13 files changed, 155 insertions(+), 22 deletions(-) diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js index 7f78e1ce3a..2aaa48c2c7 100644 --- a/binaries/data/mods/public/simulation/components/UnitAI.js +++ b/binaries/data/mods/public/simulation/components/UnitAI.js @@ -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); diff --git a/source/ps/Game.cpp b/source/ps/Game.cpp index 5650628456..66ef0fe1e3 100644 --- a/source/ps/Game.cpp +++ b/source/ps/Game.cpp @@ -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"); diff --git a/source/simulation2/MessageTypes.h b/source/simulation2/MessageTypes.h index 2ce421f114..5c42e8e892 100644 --- a/source/simulation2/MessageTypes.h +++ b/source/simulation2/MessageTypes.h @@ -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 diff --git a/source/simulation2/Simulation2.cpp b/source/simulation2/Simulation2.cpp index c279984051..19ad7b276e 100644 --- a/source/simulation2/Simulation2.cpp +++ b/source/simulation2/Simulation2.cpp @@ -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)); } //////////////////////////////////////////////////////////////// diff --git a/source/simulation2/TypeList.h b/source/simulation2/TypeList.h index 08a3d64e29..7815dad18f 100644 --- a/source/simulation2/TypeList.h +++ b/source/simulation2/TypeList.h @@ -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 diff --git a/source/simulation2/components/CCmpPathfinder.cpp b/source/simulation2/components/CCmpPathfinder.cpp index 70a806f56f..5035aa7b8a 100644 --- a/source/simulation2/components/CCmpPathfinder.cpp +++ b/source/simulation2/components/CCmpPathfinder.cpp @@ -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(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); diff --git a/source/simulation2/components/CCmpUnitMotion.cpp b/source/simulation2/components/CCmpUnitMotion.cpp index a15971d3d5..28ff282cf4 100644 --- a/source/simulation2/components/CCmpUnitMotion.cpp +++ b/source/simulation2/components/CCmpUnitMotion.cpp @@ -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; } diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp index 98f35eb495..c6046b887b 100644 --- a/source/simulation2/components/CCmpVisualActor.cpp +++ b/source/simulation2/components/CCmpVisualActor.cpp @@ -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 (msg).offset; - Interpolate(context, offset); + const CMessageInterpolate& msgData = static_cast (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) diff --git a/source/simulation2/components/ICmpUnitMotion.cpp b/source/simulation2/components/ICmpUnitMotion.cpp index 0d6dd4d01c..fb7c488d69 100644 --- a/source/simulation2/components/ICmpUnitMotion.cpp +++ b/source/simulation2/components/ICmpUnitMotion.cpp @@ -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) diff --git a/source/simulation2/components/ICmpUnitMotion.h b/source/simulation2/components/ICmpUnitMotion.h index 7198033cef..ec1a89d23c 100644 --- a/source/simulation2/components/ICmpUnitMotion.h +++ b/source/simulation2/components/ICmpUnitMotion.h @@ -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) }; diff --git a/source/simulation2/components/ICmpVisual.cpp b/source/simulation2/components/ICmpVisual.cpp index 4f10f63fd3..6c0aba5c04 100644 --- a/source/simulation2/components/ICmpVisual.cpp +++ b/source/simulation2/components/ICmpVisual.cpp @@ -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) diff --git a/source/simulation2/components/ICmpVisual.h b/source/simulation2/components/ICmpVisual.h index b3a1521f6b..65598a2aba 100644 --- a/source/simulation2/components/ICmpVisual.h +++ b/source/simulation2/components/ICmpVisual.h @@ -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 diff --git a/source/simulation2/scripting/MessageTypeConversions.cpp b/source/simulation2/scripting/MessageTypeConversions.cpp index 25b375de61..9a71258ceb 100644 --- a/source/simulation2/scripting/MessageTypeConversions.cpp +++ b/source/simulation2/scripting/MessageTypeConversions.cpp @@ -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)