Changeset View
Changeset View
Standalone View
Standalone View
source/simulation2/components/CCmpUnitMotion.cpp
Show First 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | |||||
static const entity_pos_t SHORT_PATH_GOAL_RADIUS = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*2); | static const entity_pos_t SHORT_PATH_GOAL_RADIUS = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*2); | ||||
/** | /** | ||||
* If we are this close to our target entity/point, then think about heading | * If we are this close to our target entity/point, then think about heading | ||||
* for it in a straight line instead of pathfinding. | * for it in a straight line instead of pathfinding. | ||||
*/ | */ | ||||
static const entity_pos_t DIRECT_PATH_RANGE = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*4); | static const entity_pos_t DIRECT_PATH_RANGE = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*4); | ||||
/** | |||||
* If we're following a target entity, | |||||
* we will recompute our path if the target has moved | |||||
* more than this distance from where we last pathed to. | |||||
*/ | |||||
static const entity_pos_t CHECK_TARGET_MOVEMENT_MIN_DELTA = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*4); | |||||
/** | |||||
* If we're following as part of a formation, | |||||
* but can't move to our assigned target point in a straight line, | |||||
* we will recompute our path if the target has moved | |||||
* more than this distance from where we last pathed to. | |||||
*/ | |||||
static const entity_pos_t CHECK_TARGET_MOVEMENT_MIN_DELTA_FORMATION = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*1); | |||||
/** | |||||
* If we're following something but it's more than this distance away along | |||||
* our path, then don't bother trying to repath regardless of how much it has | |||||
* moved, until we get this close to the end of our old path. | |||||
*/ | |||||
static const entity_pos_t CHECK_TARGET_MOVEMENT_AT_MAX_DIST = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*16); | |||||
/** | |||||
* If we're following something and the angle between the (straight-line) directions to its previous target | |||||
* position and its present target position is greater than a given angle, recompute the path even far away | |||||
* (i.e. even if CHECK_TARGET_MOVEMENT_AT_MAX_DIST condition is not fulfilled). The actual check is done | |||||
* on the cosine of this angle, with a PI/6 angle. | |||||
*/ | |||||
static const fixed CHECK_TARGET_MOVEMENT_MIN_COS = fixed::FromInt(866)/1000; | |||||
static const CColor OVERLAY_COLOR_LONG_PATH(1, 1, 1, 1); | static const CColor OVERLAY_COLOR_LONG_PATH(1, 1, 1, 1); | ||||
static const CColor OVERLAY_COLOR_SHORT_PATH(1, 0, 0, 1); | static const CColor OVERLAY_COLOR_SHORT_PATH(1, 0, 0, 1); | ||||
class CCmpUnitMotion : public ICmpUnitMotion | class CCmpUnitMotion : public ICmpUnitMotion | ||||
{ | { | ||||
public: | public: | ||||
static void ClassInit(CComponentManager& componentManager) | static void ClassInit(CComponentManager& componentManager) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 504 Lines • ▼ Show 20 Lines | private: | ||||
/** | /** | ||||
* 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 | ||||
* entity, if it's close enough and the route is not obstructed. | * entity, if it's close enough and the route is not obstructed. | ||||
*/ | */ | ||||
bool TryGoingStraightToTargetEntity(const CFixedVector2D& from); | bool TryGoingStraightToTargetEntity(const CFixedVector2D& from); | ||||
/** | /** | ||||
* Returns whether the target entity has moved more than minDelta since our | * Returns whether our we need to recompute a path to reach our target. | ||||
* last path computations, and we're close enough to it to care. | |||||
*/ | */ | ||||
bool CheckTargetMovement(const CFixedVector2D& from, entity_pos_t minDelta); | bool PathingUpdateNeeded(const CFixedVector2D& from); | ||||
/** | /** | ||||
* Update goal position if moving target | * Update goal position if moving target | ||||
*/ | */ | ||||
void UpdateFinalGoal(); | void UpdateFinalGoal(); | ||||
/** | /** | ||||
* Returns whether we are close enough to the target to assume it's a good enough | * Returns whether we are close enough to the target to assume it's a good enough | ||||
▲ Show 20 Lines • Show All 189 Lines • ▼ Show 20 Lines | void CCmpUnitMotion::Move(fixed dt) | ||||
if (wasObstructed && HandleObstructedMove()) | if (wasObstructed && HandleObstructedMove()) | ||||
return; | return; | ||||
if (m_PathState == PATHSTATE_FOLLOWING) | if (m_PathState == PATHSTATE_FOLLOWING) | ||||
{ | { | ||||
// We may need to recompute our path sometimes (e.g. if our target moves). | // We may need to recompute our path sometimes (e.g. if our target moves). | ||||
// Since we request paths asynchronously anyways, this does not need to be done before moving. | // Since we request paths asynchronously anyways, this does not need to be done before moving. | ||||
if (IsFormationMember()) | PathingUpdateNeeded(pos); | ||||
CheckTargetMovement(pos, CHECK_TARGET_MOVEMENT_MIN_DELTA_FORMATION); | |||||
else | |||||
CheckTargetMovement(pos, CHECK_TARGET_MOVEMENT_MIN_DELTA); | |||||
} | } | ||||
} | } | ||||
bool CCmpUnitMotion::PossiblyAtDestination() const | bool CCmpUnitMotion::PossiblyAtDestination() const | ||||
{ | { | ||||
if (m_MoveRequest.m_Type == MoveRequest::NONE) | if (m_MoveRequest.m_Type == MoveRequest::NONE) | ||||
return false; | return false; | ||||
▲ Show 20 Lines • Show All 231 Lines • ▼ Show 20 Lines | bool CCmpUnitMotion::TryGoingStraightToTargetEntity(const CFixedVector2D& from) | ||||
m_FinalGoal = goal; | m_FinalGoal = goal; | ||||
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::CheckTargetMovement(const CFixedVector2D& from, entity_pos_t minDelta) | bool CCmpUnitMotion::PathingUpdateNeeded(const CFixedVector2D& from) | ||||
{ | { | ||||
CFixedVector2D targetPos; | if (m_MoveRequest.m_Type == MoveRequest::NONE) | ||||
if (!ComputeTargetPosition(targetPos)) | |||||
return false; | return false; | ||||
// Fail unless the target has moved enough | CFixedVector2D targetPos; | ||||
CFixedVector2D oldTargetPos(m_FinalGoal.x, m_FinalGoal.z); | if (!ComputeTargetPosition(targetPos)) | ||||
if ((targetPos - oldTargetPos).CompareLength(minDelta) < 0) | |||||
return false; | return false; | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | if (PossiblyAtDestination()) | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | |||||
return false; | return false; | ||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | |||||
CFixedVector2D oldDir = (oldTargetPos - pos); | |||||
CFixedVector2D newDir = (targetPos - pos); | |||||
oldDir.Normalize(); | |||||
newDir.Normalize(); | |||||
// Fail unless we're close enough to the target to care about its movement | // We are not at destination and have no waypoints: we need a path. | ||||
// and the angle between the (straight-line) directions of the previous and new target positions is small | if (m_LongPath.m_Waypoints.empty() && m_ShortPath.m_Waypoints.empty()) | ||||
if (oldDir.Dot(newDir) > CHECK_TARGET_MOVEMENT_MIN_COS && !PathIsShort(m_LongPath, from, CHECK_TARGET_MOVEMENT_AT_MAX_DIST)) | return true; | ||||
wraitii: Move comment below | |||||
return false; | |||||
// Fail if the target is no longer visible to this entity's owner | // Get the obstruction shape and translate it where we estimate the target to be. | ||||
// (in which case we'll continue moving to its last known location, | ICmpObstructionManager::ObstructionSquare estimatedTargetShape; | ||||
// unless it comes back into view before we reach that location) | if (m_MoveRequest.m_Type == MoveRequest::POINT || m_MoveRequest.m_Type == MoveRequest::OFFSET) | ||||
CmpPtr<ICmpOwnership> cmpOwnership(GetEntityHandle()); | |||||
if (cmpOwnership) | |||||
{ | { | ||||
CmpPtr<ICmpRangeManager> cmpRangeManager(GetSystemEntity()); | estimatedTargetShape.x = targetPos.X; | ||||
if (cmpRangeManager && cmpRangeManager->GetLosVisibility(m_MoveRequest.m_Entity, cmpOwnership->GetOwner()) == ICmpRangeManager::VIS_HIDDEN) | estimatedTargetShape.z = targetPos.Y; | ||||
return false; | } | ||||
else if (m_MoveRequest.m_Type == MoveRequest::ENTITY) | |||||
{ | |||||
CmpPtr<ICmpObstruction> cmpTargetObstruction(GetSimContext(), m_MoveRequest.m_Entity); | |||||
Done Inline ActionsThere's a more straightforward way to write this wraitii: There's a more straightforward way to write this | |||||
if (cmpTargetObstruction) | |||||
cmpTargetObstruction->GetObstructionSquare(estimatedTargetShape); | |||||
estimatedTargetShape.x = targetPos.X; | |||||
estimatedTargetShape.z = targetPos.Y; | |||||
// TODO: scale it up by a certain factor based on distance to account for uncertainty? | |||||
} | } | ||||
// The target moved and we need to update our current path; | // Translate our own obstruction shape to our last waypoint | ||||
// change the goal here and expect our caller to start the path request | CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle()); | ||||
m_FinalGoal.x = targetPos.X; | ICmpObstructionManager::ObstructionSquare shape; | ||||
m_FinalGoal.z = targetPos.Y; | if (cmpObstruction) | ||||
cmpObstruction->GetObstructionSquare(shape); | |||||
Waypoint lastWaypoint = m_ShortPath.m_Waypoints.empty() ? m_LongPath.m_Waypoints.front() : m_ShortPath.m_Waypoints.front(); | |||||
shape.x = lastWaypoint.x; | |||||
shape.z = lastWaypoint.z; | |||||
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); | |||||
ENSURE(cmpObstructionManager); | |||||
Done Inline ActionsPerhaps introduce/expose an "AreShapesInRange" function in the obstruction manager? wraitii: Perhaps introduce/expose an "AreShapesInRange" function in the obstruction manager? | |||||
entity_pos_t distance = cmpObstructionManager->DistanceBetweenShapes(shape, estimatedTargetShape); | |||||
if (distance >= m_MoveRequest.m_MinRange && (distance <= m_MoveRequest.m_MaxRange || m_MoveRequest.m_MaxRange < fixed::Zero())) | |||||
return false; | |||||
// We won't be in-range when we reach our final waypoint : recompute our path. | |||||
m_FinalGoal.x = estimatedTargetShape.x; | |||||
m_FinalGoal.z = estimatedTargetShape.z; | |||||
RequestLongPath(from, m_FinalGoal); | RequestLongPath(from, m_FinalGoal); | ||||
m_PathState = PATHSTATE_FOLLOWING_REQUESTING_LONG; | m_PathState = PATHSTATE_FOLLOWING_REQUESTING_LONG; | ||||
return true; | |||||
} | } | ||||
void CCmpUnitMotion::UpdateFinalGoal() | void CCmpUnitMotion::UpdateFinalGoal() | ||||
{ | { | ||||
if (m_MoveRequest.m_Type != MoveRequest::ENTITY || m_MoveRequest.m_Type != MoveRequest::OFFSET) | if (m_MoveRequest.m_Type != MoveRequest::ENTITY || m_MoveRequest.m_Type != MoveRequest::OFFSET) | ||||
return; | return; | ||||
CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), m_MoveRequest.m_Entity); | CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), m_MoveRequest.m_Entity); | ||||
if (!cmpUnitMotion) | if (!cmpUnitMotion) | ||||
▲ Show 20 Lines • Show All 479 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator
Move comment below