I'm working on a camera that moves around the player at a fixed distance. It's the standard right-stick-to-move-cam-around kind. The camera follows the player but stays at a fixed angle to (0,0,1) until moved by input, and the player is free to move in any direction without changing the camera rotation with respect to the world. Imagine the cam as being a point on an invisible sphere with the player at the centre.
However, there's a problem with it in that when I rotate the camera around the player too fast, the lerping I added to try and smooth the movement tries to "shortcut" across instead of moving around the sphere gracefully when the desired position gets too far away. The image below is a top-down view, the camera takes the red route, but I want it to take the green route while remaining smooth.
Currently I'm:
- Checking whether the look controls are inverted, and setting a modifier value.
- Updating the current angle to (0,0,1) using the normalized X component of a Vector2 set by keyboard input (cam left, cam right).
- Updating the current angle to the Y axis using the Y component, set by input (cam up, cam down).
- Finding the desired cam position from the two angles and position of the focus point (player).
- Lerping between the current cam position and the desired one and updating the lookAt vector.
Here is the update code (it's in Go which isn't especially common for game dev, but hopefully the syntax should be easy enough to understand what's going on):
// Update the gameplay camera with the focus at focusPos.
func StepGameplayCam(focusPos math32.Vector3, delta float64) {
invertLookMod := float32(1)
if settings.Control().InvertLook {
invertLookMod = -1
}
//Get up/down and left/right components and set angles.
currentAngleToZ = util.WrapAngleRad(currentAngleToZ + float32(movementVec.X*float32(delta)*settings.Control().CamSpeed*invertLookMod))
if !((movementVec.Y < 0 && currentAngleToY <= math32.DegToRad(15)) || (movementVec.Y > 0 && currentAngleToY >= math32.DegToRad(165))) {
currentAngleToY = util.WrapAngleRad(currentAngleToY + float32(movementVec.Y*float32(delta)*settings.Control().CamSpeed*invertLookMod))
}
distanceToFocus = maxDistanceToFocus //TODO cam collisions/not clipping through things
//Set desired cam position.
targetPos.X = focusPos.X + distanceToFocus*math32.Cos(currentAngleToZ)*math32.Sin(currentAngleToY)
targetPos.Z = focusPos.Z + distanceToFocus*math32.Sin(currentAngleToZ)*math32.Sin(currentAngleToY)
targetPos.Y = focusPos.Y + distanceToFocus*math32.Cos(currentAngleToY)
camPos.Lerp(targetPos, float32(delta)) //TODO problem is here, we're LERPing between vectors but we need to do it spherically
gameCam.SetPositionVec(camPos)
gameCam.LookAt(&focusPos)
if settings.Dev().DebugEnabled {
updateCamDebug(focusPos.X, focusPos.Y, focusPos.Z)
}
}
I have a strong feeling slerp is what I'm looking for here, but my understanding of 3D mathematics is starting to brickwall at this point, and I have no idea how to use it in this context.
How can I stop my camera from "shortcutting" through the imaginary sphere around the player it is bound to?