Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

 
Advanced search

1044530 Posts in 42328 Topics- by 34017 Members - Latest Member: Rxanadu

September 21, 2014, 12:09:19 PM
TIGSource ForumsDeveloperTechnical (Moderators: Glaiel-Gamer, ThemsAllTook)Smooth camera following
Pages: [1]
Print
Author Topic: Smooth camera following  (Read 709 times)
kamac
Level 10
*****


Notorious posts editor


View Profile Email
« on: March 24, 2013, 12:12:05 AM »

Hey there.

After lots of tries, ups and downs, I have to ask here.
I need to create a camera which is smoothly following a point. You could imagine this as point-to-point follow, where one point has to get to the other one, hopefully with easing-in-out (speeds up as he starts, slows down as it is about to arrive).

Now, I want to do it with vectors probably, so here's what I've got (pseudocode):

Code:
// Get vector between two positions
translateVector = targetPos - cameraPos
// Normalize it, and decrease it's speed
translateVector = translateVector / translateVector.length() / 4
if((totalSpeed + translateVector).length() < maxSpeed)
    totalSpeed = totalSpeed + translateVector

cameraPos += totalSpeed

Though, this code doesn't include slowing down when approaching the destination.
Also, it's not fully working as camera speeds through my destination point, then tries to go back to it, speeds through it again, and so forth. Here's a gif:

Logged

Nothing to do here
nospoon
Level 1
*


View Profile Email
« Reply #1 on: March 24, 2013, 12:58:52 AM »

You could use constraints.

It's really simple to implement, and I think it should work fine.
http://www.gamasutra.com/resource_guide/20030121/jacobson_pfv.htm

Basically this :
Code:
// Pseudo-code to satisfy (C2)
delta = x2-x1;
deltalength = sqrt(delta*delta);
diff = (deltalength-restlength)/deltalength;
x1 -= delta*0.5*diff;
x2 += delta*0.5*diff;
Just change the 0.5 to smaller value, and it should work fine... probably.
Logged
Syl
Level 0
*



View Profile Email
« Reply #2 on: March 24, 2013, 01:41:05 AM »

Here is an example of my code for making sure the camera gets to the right spot. The code looks clunky, but the result is smooth.

Code:
if(CameraPosition.y != 0.1f)
{
  if(CameraPosition.y > 0.1f + 0.005f || CameraPosition.y < 0.1f - 0.005f)
  {
    if(CameraPosition.y > 0.1f + 0.005f)
      CameraPosition.y -= DeltaTime * (CameraPosition.y - 0.1f) * 2;
    if(CameraPosition.y < 0.1f - 0.005f)
      CameraPosition.y += DeltaTime * -(CameraPosition.y - 0.1f) * 2;
  }
  else
    CameraPosition.y = 0.1f;
}
Logged
powly
Level 3
***



View Profile WWW
« Reply #3 on: March 24, 2013, 02:39:03 AM »

A small edit:

Code:
// Get vector between two positions
translateVector = targetPos - cameraPos
// Normalize it, and decrease it's speed
translateVector = translateVector / translateVector.length() / 4
if((totalSpeed + translateVector).length() > maxSpeed)
    totalSpeed = translateVector/translateVector.length()*maxSpeed
else
    totalSpeed = translateVector
cameraPos += totalSpeed

That should work way better. The formula you had basically took the distance and used it as acceleration, increasing up to the point where the distance was 0 - at that point, totalSpeed was at its maximum value so it kept going and decreased due to the formula, until it became 0 after it had gone twice your planned distance. What you created is essentially a spring - something that accelerates proportionate to a negative constant times its distance to a certain point. What you wanted is damping, so the same something has a velocity proportionate to a negative constant times its distance to that point.
Logged
kamac
Level 10
*****


Notorious posts editor


View Profile Email
« Reply #4 on: March 24, 2013, 03:17:53 AM »

For now, I've modified my code as powly suggested.
Now that's the output:



Camera sometimes arrives at desired location, with smoothed movements and everything, and sometimes (as seen on gif), when it approaches destination, it starts doing these weirdo-movements. Could it be that it's my fault, somewhere in the code? Or...?
Logged

Nothing to do here
surt
Level 5
*****


Meat by-product.


View Profile Email
« Reply #5 on: March 24, 2013, 03:18:15 AM »

What's wrong with a simple weighted lerp?
Code:
function lerp(start, end, pos) {
  return start + (end - start) * pos;
}

Camera.track = function(pos, trackWeight) {
  this.pos.x = lerp(this.pos.x, pos.x, trackWeight);
  this.pos.y = lerp(this.pos.y, pos.y, trackWeight);
}

// each cycle
camera.track(player.pos, 1 / 32);
Will naturally ease out. If the point you're tracking accelerates to speed then it will also ease in.
Logged

Real life would be so much better with permadeath.
PJ Gallery - OGA Gallery - CC0 Scraps
kamac
Level 10
*****


Notorious posts editor


View Profile Email
« Reply #6 on: March 24, 2013, 03:21:20 AM »

What's wrong with a simple weighted lerp?
Code:
function lerp(start, end, pos) {
  return start + (end - start) * pos;
}

Camera.track = function(pos, trackWeight) {
  this.pos.x = lerp(this.pos.x, pos.x, trackWeight);
  this.pos.y = lerp(this.pos.y, pos.y, trackWeight);
}

// each cycle
camera.track(player.pos, 1 / 32);
Will naturally ease out. If the point you're tracking accelerates to speed then it will also ease in.

My destination point can change as the camera moves towards it. I think that matters (?)
Logged

Nothing to do here
JakobProgsch
Level 1
*



View Profile Email
« Reply #7 on: March 24, 2013, 04:40:42 AM »

Smoothly going from A to B can be done by using some interpolation function that has 0 derivative at the "ends"

Code:
Vector iterpolate_sine(A, B, t)
{
   return lerp(A, B, 0.5+0.5*sin((t-0.5)*PI));
}
Vector iterpolate_poly3(A, B, t)
{
   return lerp(A, B, (3 - 2*t)*t*t);
}
Vector iterpolate_poly5(A, B, t)
{
   return lerp(A, B, (6*t*t - 15*t +10)*t*t*t);
}
//etc.

Also surts solution is nice if the destination point changes each frame. It will result in exponential behavior...

What makes me wonder though is the "My destination point can change as the camera moves towards it." part. It sounds weird... If the desired camera position depends on the current one there is a chance for some form of "feedback" which for example in surts solution could lead to oscillation. Adding a critical damping term could help with that if one knew how that dependence looks.
Logged

kamac
Level 10
*****


Notorious posts editor


View Profile Email
« Reply #8 on: March 24, 2013, 05:05:06 AM »

Quote
Smoothly going from A to B can be done by using some interpolation function that has 0 derivative at the "ends"

What I ment by "My destination point can change as the camera moves towards it.", is that camera can be halfway between A and B, and B will change it's position. In such situation, I don't want my camera to either:
A) Magically change it's position (because halfway between A and B is different than A and B2)
B) Start moving on a new track (which would be A and B2). In such scenario, if B would be constantly changing it's position, camera would try to accelerate all the time, barely moving at all.

The way to go is vector movement here, I think.
Logged

Nothing to do here
JakobProgsch
Level 1
*



View Profile Email
« Reply #9 on: March 24, 2013, 05:52:11 AM »

Well if the camera is somewhere along the way from A to B and then the new position to B2 appears you would obviously not move along A - B2 but along A2 - B2 where A2 is the position of the camera at the moment of the "direction change". But i would go with the "exponential" solution as well. It has the slight disadvantage that the camera accelerates instantly and that it technically never arrives at the target/stops moving. One could probably tweak it to avoid that though.
Logged

kamac
Level 10
*****


Notorious posts editor


View Profile Email
« Reply #10 on: March 24, 2013, 06:31:05 AM »

Quote
Well if the camera is somewhere along the way from A to B and then the new position to B2 appears you would obviously not move along A - B2 but along A2 - B2 where A2 is the position of the camera at the moment of the "direction change"

Right, that's what I wanted to write  Durr...?

Althrough, when I imagine B point constantly moving, A would stay in the same point, or move VERY slowly towards it ("thanks" to acceleration). I think that's not the way to go here, since I want it to accelerate when camera speeds up from 0 velocity, and keep constant speed later on, up until the very end. That'd require some customization with lerp, I guess..
Logged

Nothing to do here
st33d
Level 1
*



View Profile WWW
« Reply #11 on: March 24, 2013, 08:04:49 AM »

Err. I just do this:

Code:
public function main():void {

// update the canvas position
vx = (targetPos.x - canvasX) * interpolation;
vy = (targetPos.y - canvasY) * interpolation;

canvasX += vx;
canvasY += vy;

canvas.x = int(canvasX);
canvas.y = int(canvasY);

}

And that's that. "interpolation" is a constant between 0.0 and 1.0.

You round off the position because your camera is rendering pixels.
Logged
BleakProspects
Level 4
****



View Profile WWW Email
« Reply #12 on: March 24, 2013, 09:26:23 AM »

Hey, for this I use a PID controller with a "dead zone". The reason your code oscillates is that you only have a proportional (P) term, without a derivative (D) term. Just imagine your camera as having a position, a velocity, and a controlling force.

Each frame, it's position is given by velocity * dt (where dt is the number of seconds passed since the last frame).

Code:
CameraPosition += CameraVelocity * dt;

Also, each frame, its velocity is governed by the output of a PID controller, it is also clamped to a maximum velocity:

Code:
CameraVelocity += PID.Output(Target, CameraPosition, CameraVelocity, dt);
Clamp(CameraVelocity, MaxVelocity);

PID is an object with the following properties:

Code:
// Proportional gain
float kP;

// Derivative gain
float kD;

// Integral gain
float kI;

// Applies no output inside this range
float deadZone;

// Inside the deadzone, attempts to reduce velocity
float brakeSpeed;

//These are used for book-keeping
Vector2 lastError;
Vector2 sumError;

The output function is given by this:
Code:

Vector2 Output(Vector2 target, Vector2 position, Vector2 velocity, float dt)
{
   Vector2 error = (target - position);
  
   if(error.Length() < deadZone)
   {
       return -velocity * brakeSpeed * dt;
   }

   Vector2 dError = (error - lastError) / dt;
   sumError += error * dt;
   lastError = error;

   return kP * error + kD * dError + kI * sumError;

}


Basically what's happening here is that kP is a spring term which pulls the camera toward the target. kD is a damping term which decreases oscillations, and kI is a term which fights systematic errors. For your application I would set kP to about 0.5, kD to about 0.8, and kI to 0.0. Just experiment with the values until you get something which is fast enough and which doesn't oscillate.
Logged

kamac
Level 10
*****


Notorious posts editor


View Profile Email
« Reply #13 on: March 25, 2013, 10:25:09 PM »

Alright, in the end, I went with sine interpolation..
Logged

Nothing to do here
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic