1
0
forked from 0ad/0ad

# Enhanced unit movement smoothness and multiplayer speed.

- Decreased MP turn length to 150 ms.
- Let units move along multiple steps of a path per turn. This means
they no longer "hesitate" between tiles. Especially noticeable in MP
games or at low framerates.
- Joined segments of paths generated by the pathfinder into linear
pieces for better repathing.

This was SVN commit r5224.
This commit is contained in:
Matei 2007-07-05 07:33:43 +00:00
parent 1b25d94183
commit bb517e3daf
4 changed files with 67 additions and 35 deletions

View File

@ -67,8 +67,8 @@ CNetServer::CNetServer(CGame *pGame, CGameAttributes *pGameAttribs):
m_pGame->GetSimulation()->SetTurnManager(this);
// Set an incredibly long turn length for debugging - less command batch spam that way
for (int i=0;i<3;i++)
CTurnManager::SetTurnLength(i, 1000);
for (int i=0; i<3; i++)
CTurnManager::SetTurnLength(i, 150);
g_ScriptingHost.SetGlobal("g_NetServer", OBJECT_TO_JSVAL(GetScript()));
}

View File

@ -224,21 +224,21 @@ public:
private:
CEntity( CEntityTemplate* base, CVector3D position, float orientation, const std::set<CStr8>& actorSelections, const CStrW* building = 0 );
uint ProcessGotoHelper( CEntityOrder* current, size_t timestep_milli, HEntity& collide );
uint ProcessGotoHelper( CEntityOrder* current, size_t timestep_millis, HEntity& collide, float& timeLeft );
bool ProcessContactAction( CEntityOrder* current, size_t timestep_millis, CEntityOrder::EOrderType transition, SEntityAction* action );
bool ProcessContactActionNoPathing( CEntityOrder* current, size_t timestep_millis, const CStr& animation, CScriptEvent* contactEvent, SEntityAction* action );
bool ProcessGeneric( CEntityOrder* current, size_t timestep_milli );
bool ProcessGenericNoPathing( CEntityOrder* current, size_t timestep_milli );
bool ProcessGeneric( CEntityOrder* current, size_t timestep_millis );
bool ProcessGenericNoPathing( CEntityOrder* current, size_t timestep_millis );
bool ProcessProduce( CEntityOrder* order );
bool ProcessGotoNoPathing( CEntityOrder* current, size_t timestep_milli );
bool ProcessGoto( CEntityOrder* current, size_t timestep_milli );
bool ProcessGotoWaypoint( CEntityOrder* current, size_t timestep_milli, bool contact );
bool ProcessGotoNoPathing( CEntityOrder* current, size_t timestep_millis );
bool ProcessGoto( CEntityOrder* current, size_t timestep_millis );
bool ProcessGotoWaypoint( CEntityOrder* current, size_t timestep_millis, bool contact );
bool ProcessPatrol( CEntityOrder* current, size_t timestep_milli );
bool ProcessPatrol( CEntityOrder* current, size_t timestep_millis );
float ChooseMovementSpeed( float distance );

View File

@ -85,7 +85,7 @@ float CEntity::ChooseMovementSpeed( float distance )
// Does all the shared processing for line-of-sight gotos
uint CEntity::ProcessGotoHelper( CEntityOrder* current, size_t timestep_millis, HEntity& collide )
uint CEntity::ProcessGotoHelper( CEntityOrder* current, size_t timestep_millis, HEntity& collide, float& timeLeft )
{
float timestep=timestep_millis/1000.0f;
@ -101,7 +101,8 @@ uint CEntity::ProcessGotoHelper( CEntityOrder* current, size_t timestep_millis,
// Curve smoothing.
// Here there be trig.
float scale = ChooseMovementSpeed( len ) * timestep;
float speed = ChooseMovementSpeed( len );
float scale = speed * timestep;
// Note: Easy optimization: flag somewhere that this unit
// is already pointing the way, and don't do this
@ -116,7 +117,7 @@ uint CEntity::ProcessGotoHelper( CEntityOrder* current, size_t timestep_millis,
{
if ( m_turningRadius != 0 )
{
float maxTurningSpeed = ( m_speed / m_turningRadius ) * timestep;
float maxTurningSpeed = ( speed / m_turningRadius ) * timestep;
deltatheta = clamp( deltatheta, -maxTurningSpeed, maxTurningSpeed );
}
m_orientation.Y = m_orientation.Y + deltatheta;
@ -152,13 +153,15 @@ uint CEntity::ProcessGotoHelper( CEntityOrder* current, size_t timestep_millis,
UpdateXZOrientation();
if( m_bounds && m_bounds->m_type == CBoundingObject::BOUND_OABB )
((CBoundingBox*)m_bounds)->SetOrientation( m_ahead );
((CBoundingBox*) m_bounds)->SetOrientation( m_ahead );
EGotoSituation rc = NORMAL;
if( scale > len )
{
// Reached destination. Calculate how much time we have left for the next order.
scale = len;
timeLeft = timestep - (len / speed);
rc = REACHED_DESTINATION;
}
@ -218,7 +221,6 @@ uint CEntity::ProcessGotoHelper( CEntityOrder* current, size_t timestep_millis,
// No?
return( COLLISION_OTHER );
}
}
@ -258,30 +260,46 @@ uint CEntity::ProcessGotoHelper( CEntityOrder* current, size_t timestep_millis,
bool CEntity::ProcessGotoNoPathing( CEntityOrder* current, size_t timestep_millis )
{
HEntity collide;
switch( ProcessGotoHelper( current, timestep_millis, collide ) )
float timeLeft;
switch( ProcessGotoHelper( current, timestep_millis, collide, timeLeft ) )
{
case ALREADY_AT_DESTINATION:
{
case ALREADY_AT_DESTINATION:
// If on a collision path; decide where to go next. Otherwise, proceed to the next waypoint.
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
{
Repath();
}
else
{
m_orderQueue.pop_front();
//entf_clear(ENTF_IS_RUNNING);
//entf_clear(ENTF_SHOULD_RUN);
return( false );
}
case REACHED_DESTINATION:
{
// Start along the next segment of the path, if one exists
m_orderQueue.pop_front();
if( !m_orderQueue.empty() )
{
CEntityOrder* newOrder = &m_orderQueue.front();
switch( newOrder->m_type )
{
case CEntityOrder::ORDER_GOTO_NOPATHING:
case CEntityOrder::ORDER_GOTO_COLLISION:
case CEntityOrder::ORDER_GOTO_SMOOTHED:
size_t newTimestep = cpu_i32FromFloat(timeLeft * 1000.0f);
return( ProcessGotoNoPathing( current, newTimestep ) );
}
}
return( false );
}
case COLLISION_OVERLAPPING_OBJECTS:
{
return( false );
}
case COLLISION_WITH_DESTINATION:
{
// We're as close as we can get...
m_orderQueue.pop_front();
//entf_clear(ENTF_IS_RUNNING);
//entf_clear(ENTF_SHOULD_RUN);
return( false );
}
case COLLISION_NEAR_DESTINATION:
{
// Here's a weird idea: (I hope it works)
@ -340,16 +358,14 @@ bool CEntity::ProcessGotoNoPathing( CEntityOrder* current, size_t timestep_milli
return( false );
}
case WOULD_LEAVE_MAP:
{
// Just stop here, Repath if necessary.
m_orderQueue.pop_front();
//entf_clear(ENTF_IS_RUNNING);
//entf_clear(ENTF_SHOULD_RUN);
return( false );
}
case STANCE_DISALLOWS:
return( false ); // The stance will have cleared our order queue already
default:
default:
return( false );
}
}
@ -511,11 +527,12 @@ bool CEntity::ProcessContactActionNoPathing( CEntityOrder* current, size_t times
current->m_target_location = (CVector2D)target->m_position - delta;
HEntity collide;
switch( ProcessGotoHelper( current, timestep_millis, collide ) )
HEntity collide;
float timeLeft;
switch( ProcessGotoHelper( current, timestep_millis, collide, timeLeft ) )
{
case ALREADY_AT_DESTINATION:
case REACHED_DESTINATION:
case ALREADY_AT_DESTINATION:
case COLLISION_WITH_DESTINATION:
case WOULD_LEAVE_MAP:
case STANCE_DISALLOWS:
@ -530,8 +547,7 @@ bool CEntity::ProcessContactActionNoPathing( CEntityOrder* current, size_t times
// Otherwise, continue chasing
return( false );
default:
// Path around it.
// We have a collision. Path around it.
CEntityOrder avoidance;
avoidance.m_type = CEntityOrder::ORDER_GOTO_COLLISION;
CVector2D right;

View File

@ -38,7 +38,23 @@ void CPathfindEngine::RequestLowLevelPath( HEntity entity, const CVector2D& dest
if ( mLowPathfinder.FindPath(source, destination, entity, radius) )
{
std::vector<CVector2D> path = mLowPathfinder.GetLastPath();
std::vector<CVector2D> stepwisePath = mLowPathfinder.GetLastPath();
// Make the path take as few steps as possible by collapsing steps in the same direction together.
std::vector<CVector2D> path;
CVector2D lastDir(0, 0);
for(size_t i=0; i < stepwisePath.size(); i++)
{
if(i >= 2 && stepwisePath[i]-stepwisePath[i-1] == lastDir)
// We're in a colinear range; just update last point
path[path.size()-1] = stepwisePath[i];
else
path.push_back(stepwisePath[i]);
if(i >= 1)
lastDir = stepwisePath[i] - stepwisePath[i-1];
}
if( path.size() > 0 )
{
// Push the path onto the front of our order queue in reverse order,