non-oscillation movement?

Started by
10 comments, last by hereticprophecy 17 years, 1 month ago
Hi Im trying to get my chasecam to follow my ship in a nice fashion. camNow=where the cam is camPos=the position it tries to get to (slightly behind and above the ship) My problem is that i dont know how to move it closer to this camPos with a ship of non-constant velocity without getting the oscillation (back and forth movement of a pendilum). With my current code i get this rocking motion, and i dont want it. I cannot use simple constants: if the ship is moving faster/turning etc the distance between where the camera is and where it must go differs and so must the speed at which i move it... the camX,camY,camZ are movespeed for the camera thanks for any help

		//close on the desired position
		vect3d camNow=camera->getPosition();

		float disX=(abs(camPos.X-camNow.X));
		float disY=(abs(camPos.Y-camNow.Y));
		float disZ=(abs(camPos.Z-camNow.Z));

		float camX=camSpeed*disX*disX;
		float camY=camSpeed*disY*disY;
		float camZ=camSpeed*disZ*disZ;


		if(camPos.X>camNow.X)
			camNow.X+=camX;
		if(camPos.X<camNow.X)
			camNow.X-=camX;
		if(camPos.Y>camNow.Y)
			camNow.Y+=camY;
		if(camPos.Y<camNow.Y)
			camNow.Y-=camY;
		if(camPos.Z>camNow.Z)
			camNow.Z+=camZ;
		if(camPos.Z<camNow.Z)
			camNow.Z-=camZ;

		camera->setPosition(camNow);

Advertisement
Harmonic oscillation like this arises from directing the acceleration towards the target. Whether you intended it or not, this is what you are doing, indirectly, by acting on the first derivative of the camera's velocity.

To get the smooth transitions you desire, you should act on the first derivative of motion, not the second. By this, I mean the velocity should always be directed towards the target, rather than finding its way there via an acceleration.
One way to achieve this would be to do exactly what it sounds like, but there are easier ways:

The camera doesn't need a velocity of its own, if it is to be bound to the craft. All it needs is its own position, knowledge of the position of the craft and the elapsed time since the last frame. Here's how I'd do it:

1. Determine the equilibrium position of the camera, peq. This can be done by extrapolating the craft's position (pcurrent) backwards along its heading some fixed distance. This point is where all the camera's calculations relate to.
2. Find the vector separating the camera's position relative to its equilibrium: d = peq - pcurrent.
3. In the fixed-time-step model, you'd simply need to take a weighted average of the two positions:

vnew = λpcurrent + (1 - λ)peq

where λ = max(1, elapsed_time / time_to_reach_equilibrium)

If you expect your frame-rate to change much, then this will need a little tweaking, as there is a slight error (e.g. of λ2(pcurrent - peq) between a single step and two steps in half the time, if you follow).

This way, the camera will always take the direct route to equilibrium, moving at whatever speed is necessary to bring it to rest in almost-constant time. Speak up if you'd like to see the full Euler-integration method that introduces no error.

Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
thanks for your reply but im not really with you.

the equilibrium is already found (i call it camPos). I do not understand what your are suggesting really, but would that make the camera be slightly left begind when the craft accelerates and after some second catch up with its desired position(equilibrium)? Would it give that good ol chasecam feeling?

The method i use is perfect except for that "rocking motion" back and forth that i only get when turning sharply, not when acc/deaccelerate. is there no way to simply correct what i do;

1. See how far the cam is from where it should be
2. Move towards that point, the more off the cam is, the longer this tick's jump should be

Thanks
Quote:Original post by suliman
would that make the camera be slightly left begind when the craft accelerates and after some second catch up with its desired position(equilibrium)? Would it give that good ol chasecam feeling?
Absolutely. The camera would follow behind the target, moving at an instantaneous speed that's directly proportional to its distance from the target. This is the chase-cam feeling. The only difference between my approach and yours is that you accelerate the camera towards equilibrium, whereas I, err, velocitate it there. I suspect I'm about to lose you, but if you've ever solved an ordinary differential equation it's the difference between y''' = -y and y' = -y: the first will oscillate about its equilibrium point (like a pendulum) and the second will decay towards it exponentially.

Quote:is there no way to simply correct what i do?

1. See how far the cam is from where it should be
2. Move towards that point, the more off the cam is, the longer this tick's jump should be
That's precisely what my method does [wink]. Maybe some code would clarify things:

// for example:const float MILLISECONDS_TO_EQUILIBRIUM = 1000.0f;// close on the desired positionvect3d camNow = camera->getPosition();static long last_update = GetTickCount(); // I've assumed Windows as the platformlong ms_elapsed = GetTickCount() - last_update;last_update = GetTickCount();float lambda = static_cast<float> (ms_elapsed) / MILLISECONDS_TO_EQUILIBRIUM;lambda = std::min(lambda, 1.0f);// In component form:float camNow.X = (lambda * camPos.X) + ((1.0f - lambda) * camNow.X);float camNow.Y = (lambda * camPos.Y) + ((1.0f - lambda) * camNow.Y);float camNow.Z = (lambda * camPos.Z) + ((1.0f - lambda) * camNow.Z);// Or, if your vect3d class has the appropriate operators;camNow = (lambda * camPos) + ((1.0f - lambda) * camNow);camera->setPosition(camNow);


Be warned that I haven't tested the code, but the theory is sound.

Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
Doesnt work

I had to change that min (and _cpp_min doesnt work either for me) but this is the same right?

if(lambda>1.0f)
lambda=1.0;

the rocking motion is gone but the problem is that the cam gets left behind if i go faster. The distance between cam and ship should not be relative to the velocity of the ship. Any way to solve this?

static long last_update = GetTickCount();long ms_elapsed = GetTickCount() - last_update;last_update = GetTickCount();// close on the desired positionvect3d camNow = camera->getPosition();const float MILLISECONDS_TO_EQUILIBRIUM = 1000.0f;float lambda = (ms_elapsed) / MILLISECONDS_TO_EQUILIBRIUM;if(lambda>1.0f)	lambda=1.0;camNow.X = (lambda * camPos.X) + ((1.0f - lambda) * camNow.X);camNow.Y = (lambda * camPos.Y) + ((1.0f - lambda) * camNow.Y);camNow.Z = (lambda * camPos.Z) + ((1.0f - lambda) * camNow.Z);
Your implementation of the min function is fine. I believe the std::min template is defined in algorithm.h, which contains shedloads of wonderful stuff, so check it out (along with the rest of the C/C++ standard library) if you haven't already.

As the code stands, the camera shouldn't lag too far behind the ship, and should only be affected momentarily after accelerating. If it seems a little unresponsive, you need only decrease the value of MILLISECONDS_TO_EQUILIBRIUM.

Nevertheless, if you want the camera to be a fixed distance behind the target at all times with no exception, you can normalise the separation vector:

static long last_update = GetTickCount();long ms_elapsed = GetTickCount() - last_update;last_update = GetTickCount();// close on the desired positionvect3d camNow = camera->getPosition();const float MILLISECONDS_TO_EQUILIBRIUM = 1000.0f;float lambda = ((float) ms_elapsed) / MILLISECONDS_TO_EQUILIBRIUM;if (lambda > 1.0f) lambda = 1.0;camNow.X = (lambda * camPos.X) + ((1.0f - lambda) * camNow.X);camNow.Y = (lambda * camPos.Y) + ((1.0f - lambda) * camNow.Y);camNow.Z = (lambda * camPos.Z) + ((1.0f - lambda) * camNow.Z);// Normalisevect3d diff;// So I take it vect3d doesn't have any arithmetic operators definedvect3d.X = camNow.X - camPos.X;vect3d.Y = camNow.Y - camPos.Y;vect3d.Z = camNow.Z - camPos.Z;const float CAMERA_CHASE_DISTANCE = 1.0f; // In world length unitsfloat norm_factor = CAMERA_CHASE_DISTANCE / sqrt(diff.X * diff.X + diff.Y * diff.Y + diff.Z * diff.Z);camNow.X = camPos.X + norm_factor * diff.X;camNow.Y = camPos.Y + norm_factor * diff.Y;camNow.Z = camPos.Z + norm_factor * diff.Z;camera->setPosition(camNow);


Are you working in C, perchance? If not, may I ask why you removed my static_cast? I may be wrong, but leaving ms_elapsed as an integer before the divide will cost you some accuracy, particularly at high frame-rates. If you are using C, then a C-style cast (as in the sample) would be better than nought; for clarity, if nothing else.

Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
thanks for helping out

the static_cast is back :)

No, its not a matter of unresponsiveness, if i lower the MILLISECONDS_TO_EQUILIBRIUM the distance where the cam stops increase from the ship. And if i raise velocity of the ship the distance increases (not temporarily, the pos where the cam "lands/rests" is further behind the ship).

I need it to be a chasecam. The last suggestion you make is a static behindcam that dont "chase" when i turn. So the first version is good, exept the distance is relative to the speed at which the ship is moving ( if speed is negative for example, the cam ends up in front of the ship), and its also jittery.

http://epagames.cabspace.com/islands.rar

if you have the time, plz look at it
steer with mouse (Buttons will acc/deacc)

Erik

[Edited by - suliman on February 13, 2007 6:32:35 AM]
anyone?
Quote:Original post by suliman
The last suggestion you make is a static behindcam that dont "chase" when i turn.

Are you sure about that? The second sample will chase at an angle just as in the first sample, except that it stays a fixed distance from the target. If we were to extrapolate backwards along the ship's heading vector (rather than the camera displacement) then it would be what I'd consider a 'behind cam'.

Perhaps I just don't understand what you mean by chase-cam.

Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
if i am correct by changing

vect3d.X = camNow.X - camPos.X;
vect3d.Y = camNow.Y - camPos.Y;
vect3d.Z = camNow.Z - camPos.Z;

to

diff.X = camNow.X - camPos.X;
diff.Y = camNow.Y - camPos.Y;
diff.Z = camNow.Z - camPos.Z;

then it doesnt work as a chasecam. It is just fixed behind the ship. With chasecam i mean the cam should if the ship turn, be sort of "the side" at first and then gradually catch up and end up behind the ship again. Like it does in the first code (altough that code has the problem with distance relative to the velocity of the ship and also the jitterness).

Have you seen the demo? It explains the problem with the first code. The second is simply not a chasecam at all.

Thanks a lot for helping out

This topic is closed to new replies.

Advertisement