Converting FPS Template to uniform movement speed

#1
By default the player in this template moves faster when a combination of left/right + forward/backward are pressed as opposed to just one direction at a time.
I feel like the movement speed should be constant, so I made some changes. This seems to work fine, but I want to explore other, more elegant options. What do you think?

Code: Select all

void CPlayerComponent::UpdateMovementRequest(const float frametime) const
{
Vec3 velocity = ZERO;
const float speedMod = (g_CVars.m_playerMoveSpeed > 0.f) ? g_CVars.m_playerMoveSpeed : 20.f;
const float moveSpeed = speedMod * frametime;
const float reducedMoveSpeed = (speedMod * cosf(DEG2RAD(45.f))) * frametime;

if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveLeft))
{
if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveForward) || m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveBack))
{
velocity.x -= reducedMoveSpeed;
}
else
{
velocity.x -= moveSpeed;
}
}
if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveRight))
{
if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveForward) || m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveBack))
{
velocity.x += reducedMoveSpeed;
}
else
{
velocity.x += moveSpeed;
}
}
if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveForward))
{
if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveLeft) || m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveRight))
{
velocity.y += reducedMoveSpeed;
}
else
{
velocity.y += moveSpeed;
}
}
if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveBack))
{
if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveLeft) || m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveRight))
{
velocity.y -= reducedMoveSpeed;
}
else
{
velocity.y -= moveSpeed;
}
}
m_pCharacterController->AddVelocity(GetEntity()->GetWorldRotation() * velocity);
}

Re: Converting FPS Template to have uniform movement speed

#2
An easier way to do this is to normalize the velocity. This will look like this:

Code: Select all

void CPlayerComponent::UpdateMovementRequest(const float frametime) const
{
Vec3 velocity = ZERO;
const float moveSpeed = (g_CVars.m_playerMoveSpeed > 0.f) ? g_CVars.m_playerMoveSpeed : 20.f;

if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveLeft))
{
velocity.x -= 1.0f;
}
if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveRight))
{
velocity.x += 1.0f;
}
if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveForward))
{
velocity.y += 1.0f;
}
if (m_inputFlags & static_cast<TInputFlags>(EInputFlag::MoveBack))
{
velocity.y -= 1.0f;
}

velocity = velocity.normalize()
velocity *= moveSpeed * frametime;

m_pCharacterController->AddVelocity(GetEntity()->GetWorldRotation() * velocity);
}
By normalizing it, you make sure that the length of vector will be exactly 1. After that you can multiply it with the movement speed and the frametime without having to worry about the speed increasing when you hold more buttons.

Re: Converting FPS Template to uniform movement speed

#4
Another way to do it is to use a bitset for the directions the player has indicated they want to move, and a switch statement with just the subset of directions that are valid i.e. left + right = no movement.

The following example shows how a switch statement can be used to select a quaternion for the movement direction. You can then use this to get a vector that is guaranteed to be the same length for each direction. Ignore the baseRotation I am passing in (it's from my working code). I am using that to allow the character to move in a direction relative to the camera, which is passed in as the baseRotation. Because it's using a switch the compiler should be able to optimise the result to a very quick table lookup for which code to run. The default case will catch any set of values that isn't valid.

Finally, this code is meant to return a unit vector, I handle multiplying it out somewhere else for setting the movement speed. It's easy to change that to happen in this code instead.

Code: Select all

Vec3 CPlayerInputComponent::GetMovement(const Quat& baseRotation) { bool allowMovement = true; Quat quatRelativeDirection; Vec3 vecMovement = Vec3(ZERO); // Take the mask and turn it into a vector to indicate the direction we need to pan independent of the present // camera direction. switch (m_inputFlags) { case (TInputFlags)EInputFlag::Forward: quatRelativeDirection = Quat::CreateIdentity(); break; case ((TInputFlags)EInputFlag::Forward | (TInputFlags)EInputFlag::Right): quatRelativeDirection = Quat::CreateRotationZ(DEG2RAD(45.0f)); break; case (TInputFlags)EInputFlag::Right: quatRelativeDirection = Quat::CreateRotationZ(DEG2RAD(90.0f)); break; case ((TInputFlags)EInputFlag::Backward | (TInputFlags)EInputFlag::Right): quatRelativeDirection = Quat::CreateRotationZ(DEG2RAD(135.0f)); break; case (TInputFlags)EInputFlag::Backward: quatRelativeDirection = Quat::CreateRotationZ(DEG2RAD(180.0f)); break; case ((TInputFlags)EInputFlag::Backward | (TInputFlags)EInputFlag::Left): quatRelativeDirection = Quat::CreateRotationZ(DEG2RAD(225.0f)); break; case (TInputFlags)EInputFlag::Left: quatRelativeDirection = Quat::CreateRotationZ(DEG2RAD(270.0f)); break; case ((TInputFlags)EInputFlag::Forward | (TInputFlags)EInputFlag::Left): quatRelativeDirection = Quat::CreateRotationZ(DEG2RAD(315.0f)); break; default: quatRelativeDirection = Quat::CreateIdentity(); allowMovement = false; break; } // Create a vector based on key direction. This is computed in local space for the base rotation. if (allowMovement) vecMovement = Vec3(baseRotation.GetFwdX(), baseRotation.GetFwdY(), 0.0f).GetNormalized() * quatRelativeDirection; return vecMovement; }

Who is online

Users browsing this forum: No registered users and 1 guest

cron