Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp
Show First 20 Lines • Show All 610 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, const fixed& turnRate, 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 239 Lines • ▼ Show 20 Lines | else if (!TargetHasValidPosition()) | ||||
MoveFailed(); | MoveFailed(); | ||||
} | } | ||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return; | return; | ||||
CFixedVector2D initialPos = cmpPosition->GetPosition2D(); | CFixedVector2D initialPos = cmpPosition->GetPosition2D(); | ||||
entity_angle_t initialAngle = cmpPosition->GetRotation().Y; | |||||
// Keep track of the current unit's position during the update | // Keep track of the current unit's position and rotation during the update. | ||||
CFixedVector2D pos = initialPos; | CFixedVector2D pos = initialPos; | ||||
entity_angle_t angle = initialAngle; | |||||
// 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, cmpPosition->GetTurnRate(), 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) | ||||
{ | |||||
if (angle != initialAngle) | |||||
cmpPosition->TurnTo(angle); | |||||
UpdateMovementState(fixed::Zero()); | UpdateMovementState(fixed::Zero()); | ||||
} | |||||
else | else | ||||
{ | { | ||||
// Update the Position component after our movement (if we actually moved anywhere) | // Update the Position component after our movement (if we actually moved anywhere) | ||||
// When moving always set the angle in the direction of the movement. | |||||
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); | ||||
// 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 32 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, const fixed& turnRate, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, entity_angle_t& angle) | ||||
{ | { | ||||
// 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; | ||||
// Wrap the angle to (-Pi, Pi]. | |||||
while (angle > entity_angle_t::Pi()) | |||||
angle -= entity_angle_t::Pi() * 2; | |||||
while (angle < -entity_angle_t::Pi()) | |||||
angle += entity_angle_t::Pi() * 2; | |||||
// 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. | ||||
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); | ||||
// Find the speed factor of the underlying terrain | // Find the speed factor of the underlying terrain. | ||||
// (We only care about the tile we start on - it doesn't matter if we're moving | // (We only care about the tile we start on - it doesn't matter if we're moving | ||||
// partially onto a much slower/faster tile) | // partially onto a much slower/faster tile). | ||||
// TODO: Terrain-dependent speeds are not currently supported | // TODO: Terrain-dependent speeds are not currently supported. | ||||
fixed terrainSpeed = fixed::FromInt(1); | fixed terrainSpeed = fixed::FromInt(1); | ||||
fixed maxSpeed = basicSpeed.Multiply(terrainSpeed); | fixed maxSpeed = basicSpeed.Multiply(terrainSpeed); | ||||
// We want to move (at most) maxSpeed*dt units from pos towards the next waypoint | |||||
fixed timeLeft = dt; | fixed timeLeft = dt; | ||||
fixed zero = fixed::Zero(); | fixed zero = fixed::Zero(); | ||||
while (timeLeft > zero) | while (timeLeft > zero) | ||||
{ | { | ||||
// If we ran out of path, we have to stop | // If we ran out of path, we have to stop. | ||||
if (shortPath.m_Waypoints.empty() && longPath.m_Waypoints.empty()) | if (shortPath.m_Waypoints.empty() && longPath.m_Waypoints.empty()) | ||||
break; | break; | ||||
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 (turnRate > zero) | |||||
{ | |||||
fixed maxRotation = turnRate.Multiply(timeLeft); | |||||
fixed angleDiff = angle - atan2_approx(offset.X, offset.Y); | |||||
if (angleDiff != zero) | |||||
{ | |||||
fixed absoluteAngleDiff = angleDiff.Absolute(); | |||||
if (absoluteAngleDiff > entity_angle_t::Pi()) | |||||
absoluteAngleDiff = entity_angle_t::Pi() * 2 - absoluteAngleDiff; | |||||
// Figure out whether rotating will increase or decrease the angle, and how far we need to rotate in that direction. | |||||
int direction = (entity_angle_t::Zero() < angleDiff && angleDiff <= entity_angle_t::Pi()) || angleDiff < -entity_angle_t::Pi() ? -1 : 1; | |||||
// Can't rotate far enough, just rotate in the correct direction. | |||||
if (absoluteAngleDiff > maxRotation) | |||||
{ | |||||
angle += maxRotation * direction; | |||||
if (angle * direction > entity_angle_t::Pi()) | |||||
angle -= entity_angle_t::Pi() * 2 * direction; | |||||
break; | |||||
} | |||||
// Rotate towards the next waypoint and continue moving. | |||||
angle = atan2_approx(offset.X, offset.Y); | |||||
timeLeft = (maxRotation - absoluteAngleDiff) / turnRate; | |||||
} | |||||
} | |||||
// 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) | ||||
{ | { | ||||
if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) | if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) | ||||
{ | { | ||||
pos = target; | pos = target; | ||||
// Spend the rest of the time heading towards the next waypoint | // Spend the rest of the time heading towards the next waypoint. | ||||
timeLeft = (maxdist - offsetLength) / maxSpeed; | timeLeft = (maxdist - offsetLength) / maxSpeed; | ||||
if (shortPath.m_Waypoints.empty()) | if (shortPath.m_Waypoints.empty()) | ||||
longPath.m_Waypoints.pop_back(); | longPath.m_Waypoints.pop_back(); | ||||
else | else | ||||
shortPath.m_Waypoints.pop_back(); | shortPath.m_Waypoints.pop_back(); | ||||
continue; | continue; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
// Error - path was obstructed | // Error - path was obstructed. | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
// Not close enough, so just move in the right direction | // Not close enough, so just move in the right direction. | ||||
offset.Normalize(maxdist); | offset.Normalize(maxdist); | ||||
target = pos + offset; | target = pos + offset; | ||||
if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) | if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass)) | ||||
pos = target; | pos = target; | ||||
else | else | ||||
return true; | return true; | ||||
▲ Show 20 Lines • Show All 561 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator