Jump to content
  • Advertisement
Sign in to follow this  
SiS-Shadowman

Smooth camera movement

This topic is 3051 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I want to implement a camera similar to the one from Homeworld 2.
For those of you who don't know it: It has some sort of focus point and you orbit around it using the mouse (and change the radius with the mouse wheel).
Implementing that alone is a trivial task, however I've been thinking about how I can smooth the camera movement.
Usually I would divide both positions into an actual position and target: The actual position will then need to change over time to reach the target. I could come up with a certain speed (based on the distance between the actual and target positions) that moves the camera. However this approach will not work if I change the target position in between:

vector3 targetFocus, actualFocus;
// Per update
vector3 delta = targetFocus - actualFocus;
float dist = delta.length();
float maxSpeed = ...;
vecto3 speed = delta / length * std::min(maxSpeed, length); // Maybe this can be simplified
actualFocus += speed;



Permanently storing the speed of the camera and using acceleration instead might work, however I must ensure that the camera doesn't miss the target (ie. doesn't deccelerate too slow / accelerates too fast).

But maybe there is a simple approach I don't think of: What else can I do to ensure a smooth movement of such a camera?

Share this post


Link to post
Share on other sites
Advertisement
Maybe you could increase the speed by acceleration which is based on the distance between the actual target and a projected target which would be reached using the current speed.

This way acceleration would become negative if the camera would miss the target.
And is the sign of speed indicates the rotation direction the sign of the acceleration would indicate the same, i.e. you always accelerate in one direction and if that direction is opposite to the current speed the effect is decceleration (or negative acceleration).

If I'm not completely wrong this approach could also help with situation where the new target is set to be on the opposite side of the current target, i.e. when the target is to the right, the camera rotates left and you now set the new target on the left of the camera which would mean the camera should stop smoothly and then begin to rotate right.

I hope this is understandable, if not, please don't hesitate to reveal my poor skill of describing things in a way that others can understand [smile].

Share this post


Link to post
Share on other sites
I think I know what you mean, but how can I ensure that the camera never accelerates too much? Ideally I want to prevent the camera from missing the target, otherwise the camera could bounce around the target for several times.

Share this post


Link to post
Share on other sites
This is off the top of my head so be careful: you'll probably want to fix a maximum acceleration (to prevent unacceptable changes in velocity?) and maximum velocity (to prevent unacceptable changes in position between frames).

Given that, when a target is identified and current velocity is 0, calculate target_distance = target_pos - camera_pos. Then accelerate each frame until you reach max velocity. Save the distance traveled from the start position (accelDist) as the distance needed to decelerate at max deceleration. Continue at max velocity until you reach a position accelDist from the target. Then decelerate to the target.

If you reach midpoint before reaching max velocity, just start decelerating.

Note: that's an underdamped system to prevent overshoot and is the slowest way to get there. You may want to set the target used for calcs to something beyond the actual target and just set the camera to the target when you're within some delta.

Note2: that doesn't address a change in the target when velocity > 0. Have to do a bit more thinking as max accelerations and max velocities have to be handled. It would have to do with calculating the extra distance to decelerate from current velocity to 0 at max deceleration ( 0 = v - 1/2a*t*t; solve for t. Calculate distance. )

EDIT: duh.. accelDist can be pre-calculated.

Share this post


Link to post
Share on other sites
This is actually really easy to do.

Just define your start and target coordinates as well as a desired travel time, and use a non-linear interpolator to move the camera between the two. Depending on the curve you select, you can get different acceleration/deceleration profiles and therefore different visual feels for the movement itself.

Handling target point changes is also easy; just keep a history of the last, say, 3 target points. Each time you reach a target point, erase it from the history. Then, during interpolation, do a weighted sum of the history instead of just using the final interpolated value. By decreasing the weight of a historical target sharply over time, and subsequently increasing/blending up the weight of the new target, you can get a pretty convincing "oops, slow down and turn around" effect going.

Share this post


Link to post
Share on other sites
Would that work for a moving target?

I was recently trying some things for smooth transitions of my camera, rather than it's position just changing instantly, where each frame it moves a portion of the distance it needs to go. The trouble with that is it never actually gets there. It's a fine effect for some games, like the camera is on elastic behind the target, but in mine, a 2D solar system game, the targets for the camera are different planets, which are always moving, so the camera moves smoothly towards the target when its changed but never actually gets there. It settles a distance away such that its first step towards the target is just enough to keep up but never catch it.

Share this post


Link to post
Share on other sites
I did this recently, if you look up B-Spline curves you can get very smooth and natural feeling movement. For a B-Spline curve of degree 3 you need 4 control points, the way I did it was by storing an internal list of 5 control points, and I'd change the fifth to whatever target position I wanted (then duplicate it whenever I passed a segment).

I stored a scaling factor (called timeFactor) that adjusted the speed it traveled along a curve segment, with a value of 1 it takes exactly one second to travel along a segment, which means you will not notice a deviation from the course for 1 second after you tell it to move to a new position.

void ElasticCamera::Update(float dt)
{ //ElasticCamera::t is the distance along the current curve, range [0,1)
this->t += dt * this->timeFactor;
if(this->t >= 1.0f)
{
while(this->t >= 1.0f)
{
this->t -= 1.0f;
}

this->points.erase(this->points.begin());
while(this->points.size() < 4)
{
this->points.push_back(this->points.back());
}
}

InterpolatePosition();
}

void ElasticCamera::MoveTo(const Vector3 &point)
{ // Only ever keep five points in the list.
while(this->points.size() >= 5)
{
this->points.pop_back();
}

this->points.push_back(point);
}

void ElasticCamera::SetPosition(const Vector3 &position)
{
this->points.assign(4, position);
}

void ElasticCamera::SetTimeFactor(float timeFactor)
{
if(timeFactor >= 0.0f)
{
this->timeFactor = timeFactor;
}
}

void ElasticCamera::AdjustSpeed(float scalar)
{
if(scalar >= 0.0f)
{
this->timeFactor *= scalar;
}
}

float ElasticCamera::Basis(int i, float t)
{ // The performance of this function can be improved.
switch (i)
{
case -2:
return (((-t + 3.0f) * t - 3.0f) * t + 1.0f) / 6.0f;
case -1:
return (((3.0f * t - 6.0f) * t) * t + 4.0f) / 6.0f;
case 0:
return (((-3.0f * t + 3.0f) * t + 3.0f) * t + 1.0f) / 6.0f;
case 1:
return (t * t * t) / 6.0f;
default:
return 0.0f;
}
}

void ElasticCamera::InterpolatePosition()
{
Vector3 position;
std::list<Vector3>::const_iterator point = this->points.begin();
for (int i = -2; i <= 1; ++i)
{
position += *point * Basis(i, this->t);

if(++point == this->points.end())
{ //If we hit this on anything other than the last loop we've got problems.
break;
}
}

this->position = position;
}




Edit: You will never settle on the target point unless all four control points are equal, so this may not work like you want with moving targets. You will find the position is between the second and third control point, so that may be acceptable for you.

Share this post


Link to post
Share on other sites
I just solved mine actually. One of those glorious moments of realization.

I had been moving the camera after the target with this

camera.x += (target.x - camera.x) * 5 * GameAccess.elapsed;
camera.y += (target.y - camera.y) * 5 * GameAccess.elapsed;


But it never caught it obviously enough. So I changed things a bit so the camera's position was relative to its target, so relative to the target it kept approaching what it considered a static point. Then converted the relative position back to absolute to render things properly. When changing target I set the camera's relative position to be its current absolutePos - target.absolutePos

and use this to approach it. Quickly heads towards the target and gradually comes to a stop.

_relativePosition.x += (0 - _relativePosition.x) * 5 * GameAccess.elapsed;
_relativePosition.y += (0 - _relativePosition.y) * 5 * GameAccess.elapsed;


Simple once I realised to do it relative. I may change the interpolation to more of a curve but that's look and feel stuff for later. I'll remember B-splines for when the time comes.

Thanks

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!