Changeset View
Standalone View
source/simulation2/components/CCmpUnitMotion.cpp
Show First 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | public: | ||||
// Dynamic state: | // Dynamic state: | ||||
entity_pos_t m_Clearance; | entity_pos_t m_Clearance; | ||||
// cached for efficiency | // cached for efficiency | ||||
fixed m_WalkSpeed, m_RunMultiplier; | fixed m_WalkSpeed, m_RunMultiplier; | ||||
bool m_FacePointAfterMove; | bool m_FacePointAfterMove; | ||||
wraitii: Rename this to `m_TakeTimeToTurn` or alternatively `m_InstantTurning` or something, the name… | |||||
bool m_RotateTime; | |||||
// Number of path computations that failed (in a row). | // Number of path computations that failed (in a row). | ||||
// When this gets above MAX_FAILED_PATH_COMPUTATIONS, inform other components | // When this gets above MAX_FAILED_PATH_COMPUTATIONS, inform other components | ||||
// that the move will likely fail. | // that the move will likely fail. | ||||
u8 m_FailedPathComputations = 0; | u8 m_FailedPathComputations = 0; | ||||
// If true, PathingUpdateNeeded returns false always. | // If true, PathingUpdateNeeded returns false always. | ||||
// This exists because the goal may be unreachable to the short/long pathfinder. | // This exists because the goal may be unreachable to the short/long pathfinder. | ||||
// In such cases, we would compute inacceptable paths and PathingUpdateNeeded would trigger every turn. | // In such cases, we would compute inacceptable paths and PathingUpdateNeeded would trigger every turn. | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | return | ||||
"</element>"; | "</element>"; | ||||
} | } | ||||
virtual void Init(const CParamNode& paramNode) | virtual void Init(const CParamNode& paramNode) | ||||
{ | { | ||||
m_FormationController = paramNode.GetChild("FormationController").ToBool(); | m_FormationController = paramNode.GetChild("FormationController").ToBool(); | ||||
m_FacePointAfterMove = true; | m_FacePointAfterMove = true; | ||||
m_RotateTime = false; | |||||
Not Done Inline ActionsNotice CCmpPosition already has a turn rate. Freagarach: Notice `CCmpPosition` already has a turn rate. | |||||
m_WalkSpeed = m_TemplateWalkSpeed = m_Speed = paramNode.GetChild("WalkSpeed").ToFixed(); | m_WalkSpeed = m_TemplateWalkSpeed = m_Speed = paramNode.GetChild("WalkSpeed").ToFixed(); | ||||
m_SpeedMultiplier = fixed::FromInt(1); | m_SpeedMultiplier = fixed::FromInt(1); | ||||
m_CurSpeed = fixed::Zero(); | m_CurSpeed = fixed::Zero(); | ||||
m_RunMultiplier = m_TemplateRunMultiplier = fixed::FromInt(1); | m_RunMultiplier = m_TemplateRunMultiplier = fixed::FromInt(1); | ||||
if (paramNode.GetChild("RunMultiplier").IsOk()) | if (paramNode.GetChild("RunMultiplier").IsOk()) | ||||
m_RunMultiplier = m_TemplateRunMultiplier = paramNode.GetChild("RunMultiplier").ToFixed(); | m_RunMultiplier = m_TemplateRunMultiplier = paramNode.GetChild("RunMultiplier").ToFixed(); | ||||
Show All 34 Lines | void SerializeCommon(S& serialize) | ||||
serialize.NumberFixed_Unbounded("target min range", m_MoveRequest.m_MinRange); | serialize.NumberFixed_Unbounded("target min range", m_MoveRequest.m_MinRange); | ||||
serialize.NumberFixed_Unbounded("target max range", m_MoveRequest.m_MaxRange); | serialize.NumberFixed_Unbounded("target max range", m_MoveRequest.m_MaxRange); | ||||
serialize.NumberFixed_Unbounded("speed multiplier", m_SpeedMultiplier); | serialize.NumberFixed_Unbounded("speed multiplier", m_SpeedMultiplier); | ||||
serialize.NumberFixed_Unbounded("current speed", m_CurSpeed); | serialize.NumberFixed_Unbounded("current speed", m_CurSpeed); | ||||
serialize.Bool("facePointAfterMove", m_FacePointAfterMove); | serialize.Bool("facePointAfterMove", m_FacePointAfterMove); | ||||
serialize.Bool("rotateTime", m_RotateTime); | |||||
SerializeVector<SerializeWaypoint>()(serialize, "long path", m_LongPath.m_Waypoints); | SerializeVector<SerializeWaypoint>()(serialize, "long path", m_LongPath.m_Waypoints); | ||||
SerializeVector<SerializeWaypoint>()(serialize, "short path", m_ShortPath.m_Waypoints); | SerializeVector<SerializeWaypoint>()(serialize, "short path", m_ShortPath.m_Waypoints); | ||||
} | } | ||||
virtual void Serialize(ISerializer& serialize) | virtual void Serialize(ISerializer& serialize) | ||||
{ | { | ||||
SerializeCommon(serialize); | SerializeCommon(serialize); | ||||
▲ Show 20 Lines • Show All 132 Lines • ▼ Show 20 Lines | virtual fixed GetCurrentSpeed() const | ||||
return m_CurSpeed; | return m_CurSpeed; | ||||
} | } | ||||
virtual void SetFacePointAfterMove(bool facePointAfterMove) | virtual void SetFacePointAfterMove(bool facePointAfterMove) | ||||
{ | { | ||||
m_FacePointAfterMove = facePointAfterMove; | m_FacePointAfterMove = facePointAfterMove; | ||||
} | } | ||||
virtual void SetRotateTime(bool rotateTime) | |||||
{ | |||||
m_RotateTime = rotateTime; | |||||
} | |||||
virtual void SetDebugOverlay(bool enabled) | virtual void SetDebugOverlay(bool enabled) | ||||
{ | { | ||||
m_DebugOverlayEnabled = enabled; | m_DebugOverlayEnabled = enabled; | ||||
UpdateMessageSubscriptions(); | UpdateMessageSubscriptions(); | ||||
} | } | ||||
virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) | virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | private: | ||||
*/ | */ | ||||
bool PossiblyAtDestination() const; | bool PossiblyAtDestination() const; | ||||
/** | /** | ||||
* Process the move the unit will do this turn. | * Process the move the unit will do this turn. | ||||
* This does not send actually change the position. | * This does not send actually change the position. | ||||
* @returns true if the move was obstructed. | * @returns true if the move was obstructed. | ||||
*/ | */ | ||||
bool PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos) const; | bool PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, entity_angle_t& angle); | ||||
/** | /** | ||||
* Update other components on our speed. | * Update other components on our speed. | ||||
* (For performance, this should try to avoid sending messages). | * (For performance, this should try to avoid sending messages). | ||||
*/ | */ | ||||
void UpdateMovementState(entity_pos_t speed); | void UpdateMovementState(entity_pos_t speed); | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 238 Lines • ▼ Show 20 Lines | void CCmpUnitMotion::Move(fixed dt) | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return; | return; | ||||
CFixedVector2D initialPos = cmpPosition->GetPosition2D(); | CFixedVector2D initialPos = cmpPosition->GetPosition2D(); | ||||
// Keep track of the current unit's position during the update | // Keep track of the current unit's position during the update | ||||
CFixedVector2D pos = initialPos; | CFixedVector2D pos = initialPos; | ||||
entity_angle_t angle = cmpPosition->GetRotation().Y; | |||||
Done Inline ActionsI think it'd be clearer if you moved the initial calls together, and the others together, so the comment is relevant. wraitii: I think it'd be clearer if you moved the initial calls together, and the others together, so… | |||||
// If we're chasing a potentially-moving unit and are currently close | // If we're chasing a potentially-moving unit and are currently close | ||||
// enough to its current position, and we can head in a straight line | // enough to its current position, and we can head in a straight line | ||||
// to it, then throw away our current path and go straight to it | // to it, then throw away our current path and go straight to it | ||||
bool wentStraight = TryGoingStraightToTarget(initialPos); | bool wentStraight = TryGoingStraightToTarget(initialPos); | ||||
bool wasObstructed = PerformMove(dt, m_ShortPath, m_LongPath, pos); | bool wasObstructed = PerformMove(dt, m_ShortPath, m_LongPath, pos, angle); | ||||
// Update our speed over this turn so that the visual actor shows the correct animation. | // Update our speed over this turn so that the visual actor shows the correct animation. | ||||
if (pos == initialPos) | if (pos == initialPos) | ||||
wraitiiUnsubmitted Not Done Inline ActionsI'd rather you checked for angle here too. wraitii: I'd rather you checked for angle here too. | |||||
{ | |||||
cmpPosition->TurnTo(angle); | |||||
Done Inline Actionsthis comment should stay above wraitii: this comment should stay above | |||||
UpdateMovementState(fixed::Zero()); | UpdateMovementState(fixed::Zero()); | ||||
} | |||||
else | else | ||||
Done Inline ActionsThink this would be better before UpdateMovementState, like it's done below, or we might run into weird inconsistencies down the line. wraitii: Think this would be better before UpdateMovementState, like it's done below, or we might run… | |||||
{ | { | ||||
// Update the Position component after our movement (if we actually moved anywhere) | // Update the Position component after our movement (if we actually moved anywhere) | ||||
CFixedVector2D offset = pos - initialPos; | CFixedVector2D offset = pos - initialPos; | ||||
angle = atan2_approx(offset.X, offset.Y); | |||||
// Face towards the target | |||||
entity_angle_t angle = atan2_approx(offset.X, offset.Y); | |||||
cmpPosition->MoveAndTurnTo(pos.X,pos.Y, angle); | cmpPosition->MoveAndTurnTo(pos.X, pos.Y, angle); | ||||
Done Inline Actions@Freagarach if one want turning on every move, one should remove this line and remove the m_RotateTime check bb: @Freagarach if one want turning on every move, one should remove this line and remove the… | |||||
Done Inline ActionsYeah, I had already tried it, and it was really not bad IMHO ^^ Freagarach: Yeah, I had already tried it, and it was really not bad IMHO ^^
I can try again with a larger… | |||||
// Calculate the mean speed over this past turn. | // Calculate the mean speed over this past turn. | ||||
UpdateMovementState(offset.Length() / dt); | UpdateMovementState(offset.Length() / dt); | ||||
} | } | ||||
if (wasObstructed && HandleObstructedMove()) | if (wasObstructed && HandleObstructedMove()) | ||||
return; | return; | ||||
else if (!wasObstructed) | else if (!wasObstructed) | ||||
Show All 30 Lines | if (m_MoveRequest.m_Type == MoveRequest::OFFSET) | ||||
CFixedVector2D targetPos; | CFixedVector2D targetPos; | ||||
ComputeTargetPosition(targetPos); | ComputeTargetPosition(targetPos); | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
return cmpObstructionManager->IsInPointRange(GetEntityId(), targetPos.X, targetPos.Y, m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false); | return cmpObstructionManager->IsInPointRange(GetEntityId(), targetPos.X, targetPos.Y, m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false); | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool CCmpUnitMotion::PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos) const | bool CCmpUnitMotion::PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, entity_angle_t& angle) | ||||
{ | { | ||||
while (angle > entity_angle_t::Pi()) | |||||
angle -= entity_angle_t::Pi() * 2; | |||||
while (angle < -entity_angle_t::Pi()) | |||||
angle += entity_angle_t::Pi() * 2; | |||||
pos = pos; | |||||
wraitiiUnsubmitted Not Done Inline Actions? wraitii: ? | |||||
// If there are no waypoint, behave as though we were obstructed and let HandleObstructedMove handle it. | // If there are no waypoint, behave as though we were obstructed and let HandleObstructedMove handle it. | ||||
if (shortPath.m_Waypoints.empty() && longPath.m_Waypoints.empty()) | if (shortPath.m_Waypoints.empty() && longPath.m_Waypoints.empty()) | ||||
return true; | return true; | ||||
// TODO: there's some asymmetry here when units look at other | // TODO: there's some asymmetry here when units look at other | ||||
// units' positions - the result will depend on the order of execution. | // units' positions - the result will depend on the order of execution. | ||||
// Maybe we should split the updates into multiple phases to minimise | // Maybe we should split the updates into multiple phases to minimise | ||||
// that problem. | // that problem. | ||||
Not Done Inline ActionsFeels like this should be done in CmpPosition->GetRotation maybe? wraitii: Feels like this should be done in CmpPosition->GetRotation maybe? | |||||
Done Inline ActionsCan, maybe even more wanted would be that it is in the entity_angle_t definition bb: Can, maybe even more wanted would be that it is in the `entity_angle_t` definition | |||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | ||||
ENSURE(cmpPathfinder); | ENSURE(cmpPathfinder); | ||||
fixed basicSpeed = m_Speed; | fixed basicSpeed = m_Speed; | ||||
// If in formation, run to keep up; otherwise just walk | // If in formation, run to keep up; otherwise just walk | ||||
if (IsFormationMember()) | if (IsFormationMember()) | ||||
basicSpeed = m_Speed.Multiply(m_RunMultiplier); | basicSpeed = m_Speed.Multiply(m_RunMultiplier); | ||||
Show All 18 Lines | while (timeLeft > zero) | ||||
CFixedVector2D target; | CFixedVector2D target; | ||||
if (shortPath.m_Waypoints.empty()) | if (shortPath.m_Waypoints.empty()) | ||||
target = CFixedVector2D(longPath.m_Waypoints.back().x, longPath.m_Waypoints.back().z); | target = CFixedVector2D(longPath.m_Waypoints.back().x, longPath.m_Waypoints.back().z); | ||||
else | else | ||||
target = CFixedVector2D(shortPath.m_Waypoints.back().x, shortPath.m_Waypoints.back().z); | target = CFixedVector2D(shortPath.m_Waypoints.back().x, shortPath.m_Waypoints.back().z); | ||||
CFixedVector2D offset = target - pos; | CFixedVector2D offset = target - pos; | ||||
if (m_RotateTime) | |||||
{ | |||||
fixed rotSpeed = fixed::FromInt(3); | |||||
fixed maxrot = rotSpeed.Multiply(timeLeft); | |||||
Done Inline ActionsAlarm bells should go off when using floats to affect the simstate => the position turnrate needs to be fixed bb: Alarm bells should go off when using floats to affect the simstate => the position turnrate… | |||||
fixed angleDiff = angle - atan2_approx(offset.X, offset.Y); | |||||
if (angleDiff != zero) | |||||
{ | |||||
Not Done Inline ActionsMight want to epsilon this, because it's somewhat unlikely that angle-atan2_approx will actually be 0 very often. wraitii: Might want to epsilon this, because it's somewhat unlikely that `angle-atan2_approx` will… | |||||
Done Inline ActionsIt actually will be 0 rather likely when inspecting L1034 below bb: It actually will be 0 rather likely when inspecting L1034 below | |||||
if (entity_angle_t::Zero() > angleDiff && angleDiff > -entity_angle_t::Pi()) | |||||
{ | |||||
if (-angleDiff > maxrot) | |||||
{ | |||||
angle += maxrot; | |||||
while (angle > entity_angle_t::Pi()) | |||||
angle -= entity_angle_t::Pi() * 2; | |||||
break; | |||||
} | |||||
else | |||||
{ | |||||
m_RotateTime = false; | |||||
angle = atan2_approx(offset.X, offset.Y); | |||||
timeLeft = (maxrot + angleDiff) / rotSpeed; | |||||
continue; | |||||
Done Inline Actionstowards wraitii: towards | |||||
} | |||||
} | |||||
else if (entity_angle_t::Zero() < angleDiff && angleDiff < entity_angle_t::Pi()) | |||||
{ | |||||
if (angleDiff > maxrot) | |||||
{ | |||||
angle -= maxrot; | |||||
while (angle < -entity_angle_t::Pi()) | |||||
Done Inline ActionsI don't think you need to continue here, which would avoid rechecking for angle. wraitii: I don't think you need to `continue` here, which would avoid rechecking for angle. | |||||
Done Inline ActionsAlso the else seems ditch candidate bb: Also the `else` seems ditch candidate | |||||
angle += entity_angle_t::Pi() * 2; | |||||
break; | |||||
} | |||||
else | |||||
{ | |||||
m_RotateTime = false; | |||||
angle = atan2_approx(offset.X, offset.Y); | |||||
timeLeft = (maxrot - angleDiff) / rotSpeed; | |||||
continue; | |||||
} | |||||
} | |||||
else if (angleDiff < -entity_angle_t::Pi()) | |||||
{ | |||||
if (entity_angle_t::Pi() * 2 + angleDiff > maxrot) | |||||
{ | |||||
angle -= maxrot; | |||||
while (angle < -entity_angle_t::Pi()) | |||||
angle += entity_angle_t::Pi() * 2; | |||||
break; | |||||
} | |||||
else | |||||
{ | |||||
m_RotateTime = false; | |||||
angle = atan2_approx(offset.X, offset.Y); | |||||
timeLeft = (maxrot - angleDiff - entity_angle_t::Pi() * 2) / rotSpeed; | |||||
continue; | |||||
} | |||||
} | |||||
else if (angleDiff > entity_angle_t::Pi()) | |||||
{ | |||||
if (entity_angle_t::Pi() * 2 - angleDiff > maxrot) | |||||
{ | |||||
angle += maxrot; | |||||
while (angle > entity_angle_t::Pi()) | |||||
angle -= entity_angle_t::Pi() * 2; | |||||
break; | |||||
} | |||||
else | |||||
{ | |||||
m_RotateTime = false; | |||||
angle = atan2_approx(offset.X, offset.Y); | |||||
timeLeft = (maxrot + angleDiff - entity_angle_t::Pi() * 2) / rotSpeed; | |||||
continue; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// Work out how far we can travel in timeLeft | // Work out how far we can travel in timeLeft | ||||
fixed maxdist = maxSpeed.Multiply(timeLeft); | fixed maxdist = maxSpeed.Multiply(timeLeft); | ||||
// If the target is close, we can move there directly | // If the target is close, we can move there directly | ||||
fixed offsetLength = offset.Length(); | fixed offsetLength = offset.Length(); | ||||
if (offsetLength <= maxdist) | if (offsetLength <= maxdist) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 568 Lines • Show Last 20 Lines |
Rename this to m_TakeTimeToTurn or alternatively m_InstantTurning or something, the name looks odd / is misleading if you don't have the big picture in mind.