Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/components/CCmpUnitMotion.h
Show First 20 Lines • Show All 710 Lines • ▼ Show 20 Lines | private: | ||||
{ | { | ||||
return ComputeTargetPosition(out, m_MoveRequest); | return ComputeTargetPosition(out, m_MoveRequest); | ||||
} | } | ||||
/** | /** | ||||
* Attempts to replace the current path with a straight line to the target, | * Attempts to replace the current path with a straight line to the target, | ||||
* if it's close enough and the route is not obstructed. | * if it's close enough and the route is not obstructed. | ||||
*/ | */ | ||||
bool TryGoingStraightToTarget(const CFixedVector2D& from); | bool TryGoingStraightToTarget(const CFixedVector2D& from, bool updatePaths); | ||||
/** | /** | ||||
* Returns whether our we need to recompute a path to reach our target. | * Returns whether our we need to recompute a path to reach our target. | ||||
*/ | */ | ||||
bool PathingUpdateNeeded(const CFixedVector2D& from) const; | bool PathingUpdateNeeded(const CFixedVector2D& from) const; | ||||
/** | /** | ||||
* Rotate to face towards the target point, given the current pos | * Rotate to face towards the target point, given the current pos | ||||
▲ Show 20 Lines • Show All 222 Lines • ▼ Show 20 Lines | |||||
void CCmpUnitMotion::Move(CCmpUnitMotionManager::MotionState& state, fixed dt) | void CCmpUnitMotion::Move(CCmpUnitMotionManager::MotionState& state, fixed dt) | ||||
{ | { | ||||
PROFILE("Move"); | PROFILE("Move"); | ||||
// 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. | ||||
state.wentStraight = TryGoingStraightToTarget(state.initialPos); | state.wentStraight = TryGoingStraightToTarget(state.initialPos, true); | ||||
state.wasObstructed = PerformMove(dt, state.cmpPosition->GetTurnRate(), m_ShortPath, m_LongPath, state.pos, state.angle); | state.wasObstructed = PerformMove(dt, state.cmpPosition->GetTurnRate(), m_ShortPath, m_LongPath, state.pos, state.angle); | ||||
} | } | ||||
void CCmpUnitMotion::PostMove(CCmpUnitMotionManager::MotionState& state, fixed dt) | void CCmpUnitMotion::PostMove(CCmpUnitMotionManager::MotionState& state, fixed dt) | ||||
{ | { | ||||
// 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 (state.pos == state.initialPos) | if (state.pos == state.initialPos) | ||||
▲ Show 20 Lines • Show All 302 Lines • ▼ Show 20 Lines | if (moveRequest.m_Type == MoveRequest::OFFSET) | ||||
// There is an offset, so compute it relative to orientation | // There is an offset, so compute it relative to orientation | ||||
entity_angle_t angle = cmpTargetPosition->GetRotation().Y; | entity_angle_t angle = cmpTargetPosition->GetRotation().Y; | ||||
CFixedVector2D offset = moveRequest.GetOffset().Rotate(angle); | CFixedVector2D offset = moveRequest.GetOffset().Rotate(angle); | ||||
out = cmpTargetPosition->GetPosition2D() + offset; | out = cmpTargetPosition->GetPosition2D() + offset; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
out = cmpTargetPosition->GetPosition2D(); | out = cmpTargetPosition->GetPosition2D(); | ||||
// Because units move one-at-a-time and pathing is asynchronous, we need to account for target movement, | // Position is only updated after all units have moved & pushed. | ||||
// if we are computing this during the MT_Motion* part of the turn. | // Therefore, we may need to interpolate the target position, depending on when this call takes place during the turn: | ||||
// If our entity ID is lower, we move first, and so we need to add a predicted movement to compute a path for next turn. | // - On "Turn Start", we'll check positions directly without interpolation. | ||||
// If our entity ID is higher, the target has already moved, so we can just use the position directly. | // - During movement, we'll call this for direct-pathing & we need to interpolate | ||||
// (this way, we move where the unit will end up at the end of _this_ turn, making it match on next turn start). | |||||
// - After movement, we'll call this to request paths & we need to interpolate | |||||
// (this way, we'll move where the unit ends up in the end of _next_ turn, making it a match in 2 turns). | |||||
// TODO: This does not really aim many turns in advance, with orthogonal trajectories it probably should. | // TODO: This does not really aim many turns in advance, with orthogonal trajectories it probably should. | ||||
CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), moveRequest.m_Entity); | CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), moveRequest.m_Entity); | ||||
CmpPtr<ICmpUnitMotionManager> cmpUnitMotionManager(GetSystemEntity()); | CmpPtr<ICmpUnitMotionManager> cmpUnitMotionManager(GetSystemEntity()); | ||||
bool needInterpolation = cmpUnitMotion && cmpUnitMotion->IsMoveRequested() && cmpUnitMotionManager->ComputingMotion(); | bool needInterpolation = cmpUnitMotion && cmpUnitMotion->IsMoveRequested() && cmpUnitMotionManager->ComputingMotion(); | ||||
if (needInterpolation && GetEntityId() < moveRequest.m_Entity) | if (needInterpolation) | ||||
{ | { | ||||
// Add predicted movement. | // Add predicted movement. | ||||
CFixedVector2D tempPos = out + (out - cmpTargetPosition->GetPreviousPosition2D()); | CFixedVector2D tempPos = out + (out - cmpTargetPosition->GetPreviousPosition2D()); | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | |||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | |||||
return true; // Still return true since we don't need a position for the target to have one. | |||||
// Fleeing fix: if we anticipate the target to go through us, we'll suddenly turn around, which is bad. | |||||
// Pretend that the target is still behind us in those cases. | |||||
if (m_MoveRequest.m_MinRange > fixed::Zero()) | |||||
{ | |||||
if ((out - cmpPosition->GetPosition2D()).RelativeOrientation(tempPos - cmpPosition->GetPosition2D()) >= 0) | |||||
out = tempPos; | |||||
} | |||||
else | |||||
out = tempPos; | |||||
} | |||||
else if (needInterpolation && GetEntityId() > moveRequest.m_Entity) | |||||
{ | |||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | |||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | |||||
return true; // Still return true since we don't need a position for the target to have one. | |||||
// Fleeing fix: opposite to above, check if our target has travelled through us already this turn. | |||||
CFixedVector2D tempPos = out - (out - cmpTargetPosition->GetPreviousPosition2D()); | |||||
if (m_MoveRequest.m_MinRange > fixed::Zero() && | |||||
(out - cmpPosition->GetPosition2D()).RelativeOrientation(tempPos - cmpPosition->GetPosition2D()) < 0) | |||||
out = tempPos; | out = tempPos; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CCmpUnitMotion::TryGoingStraightToTarget(const CFixedVector2D& from) | bool CCmpUnitMotion::TryGoingStraightToTarget(const CFixedVector2D& from, bool updatePaths) | ||||
{ | { | ||||
// Assume if we have short paths we want to follow them. | // Assume if we have short paths we want to follow them. | ||||
// Exception: offset movement (formations) generally have very short deltas | // Exception: offset movement (formations) generally have very short deltas | ||||
// and to look good we need them to walk-straight most of the time. | // and to look good we need them to walk-straight most of the time. | ||||
if (!IsFormationMember() && !m_ShortPath.m_Waypoints.empty()) | if (!IsFormationMember() && !m_ShortPath.m_Waypoints.empty()) | ||||
return false; | return false; | ||||
CFixedVector2D targetPos; | CFixedVector2D targetPos; | ||||
Show All 34 Lines | bool CCmpUnitMotion::TryGoingStraightToTarget(const CFixedVector2D& from, bool updatePaths) | ||||
if (specificIgnore.valid()) | if (specificIgnore.valid()) | ||||
{ | { | ||||
if (!cmpPathfinder->CheckMovement(GetObstructionFilter(specificIgnore), from.X, from.Y, goalPos.X, goalPos.Y, m_Clearance, m_PassClass)) | if (!cmpPathfinder->CheckMovement(GetObstructionFilter(specificIgnore), from.X, from.Y, goalPos.X, goalPos.Y, m_Clearance, m_PassClass)) | ||||
return false; | return false; | ||||
} | } | ||||
else if (!cmpPathfinder->CheckMovement(GetObstructionFilter(), from.X, from.Y, goalPos.X, goalPos.Y, m_Clearance, m_PassClass)) | else if (!cmpPathfinder->CheckMovement(GetObstructionFilter(), from.X, from.Y, goalPos.X, goalPos.Y, m_Clearance, m_PassClass)) | ||||
return false; | return false; | ||||
if (!updatePaths) | |||||
return true; | |||||
// That route is okay, so update our path | // That route is okay, so update our path | ||||
m_LongPath.m_Waypoints.clear(); | m_LongPath.m_Waypoints.clear(); | ||||
m_ShortPath.m_Waypoints.clear(); | m_ShortPath.m_Waypoints.clear(); | ||||
m_ShortPath.m_Waypoints.emplace_back(Waypoint{ goalPos.X, goalPos.Y }); | m_ShortPath.m_Waypoints.emplace_back(Waypoint{ goalPos.X, goalPos.Y }); | ||||
return true; | return true; | ||||
} | } | ||||
bool CCmpUnitMotion::PathingUpdateNeeded(const CFixedVector2D& from) const | bool CCmpUnitMotion::PathingUpdateNeeded(const CFixedVector2D& from) const | ||||
▲ Show 20 Lines • Show All 216 Lines • ▼ Show 20 Lines | #if DISABLE_PATHFINDER | ||||
CFixedVector2D goalPos = m_FinalGoal.NearestPointOnGoal(from); | CFixedVector2D goalPos = m_FinalGoal.NearestPointOnGoal(from); | ||||
m_LongPath.m_Waypoints.clear(); | m_LongPath.m_Waypoints.clear(); | ||||
m_ShortPath.m_Waypoints.clear(); | m_ShortPath.m_Waypoints.clear(); | ||||
m_ShortPath.m_Waypoints.emplace_back(Waypoint{ goalPos.X, goalPos.Y }); | m_ShortPath.m_Waypoints.emplace_back(Waypoint{ goalPos.X, goalPos.Y }); | ||||
return; | return; | ||||
} | } | ||||
#endif | #endif | ||||
// If the target is close and we can reach it in a straight line, | // If the target is close enough, hope that we'll be able to go straight next turn. | ||||
// then we'll just go along the straight line instead of computing a path. | if (!ShouldAlternatePathfinder() && TryGoingStraightToTarget(from, false)) | ||||
if (!ShouldAlternatePathfinder() && TryGoingStraightToTarget(from)) | { | ||||
// NB: since we may fail to move straight next turn, we should edge our bets. | |||||
// Since the 'go straight' logic currently fires only if there's no short path, | |||||
// we'll compute a long path regardless to make sure _that_ stays up to date. | |||||
// (it's also extremely likely to be very fast to compute, so no big deal). | |||||
m_ShortPath.m_Waypoints.clear(); | |||||
RequestLongPath(from, goal); | |||||
return; | return; | ||||
} | |||||
// Otherwise we need to compute a path. | // Otherwise we need to compute a path. | ||||
// If it's close then just do a short path, not a long path | // If it's close then just do a short path, not a long path | ||||
// TODO: If it's close on the opposite side of a river then we really | // TODO: If it's close on the opposite side of a river then we really | ||||
// need a long path, so we shouldn't simply check linear distance | // need a long path, so we shouldn't simply check linear distance | ||||
// the check is arbitrary but should be a reasonably small distance. | // the check is arbitrary but should be a reasonably small distance. | ||||
// We want to occasionally compute a long path if we're computing short-paths, because the short path domain | // We want to occasionally compute a long path if we're computing short-paths, because the short path domain | ||||
▲ Show 20 Lines • Show All 143 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator