Game Development Community

Camera follow target issue

by Daniel Allessi · in Torque Game Engine · 11/04/2006 (9:35 am) · 1 replies

I have written a camera system where each camera is placed in a stationary position in the level, and can optionally pan or tilt (or both) to follow the player. This lets me have Resident Evil style cameras or allow them to pan around and cover both sides of a hallway or even tilt to watch the player going up and down stairs. The current camera is decided when the player passes through a trigger to set the active camera.

The problem is that tilt is coming out skewed. I have spent a lot of time already looking over everything and I cannot figure out where the problem might be. Hopefully someone can shed some light on the issue.

Here is the function in question (which is updated every $levelCameras::updateDelay from a master camera update function):
function Camera::updateTracking(%this)
{
    if(%this.getDatablock().className !$= "levelCamera")
        return;

    // calculate direction vector
    %dirVec = vectorSub(%this.trackObject.getPosition(), %this.getPosition());


    // pan ------------------
    %oldAngZ = getWord(%this.getTransform(), 5) * getWord(%this.getTransform(), 6);
    
    if(%this.canPan)
    {
        %rotAngZ = mAtan(getWord(%dirVec, 0), getWord(%dirVec, 1));
        
        %deltaZ = (%rotAngZ - %oldAngZ);
    
        // take the shortest pan
        if(%deltaZ > mDegToRad(180))
            %deltaZ -= mDegToRad(360);
        else if(%deltaZ < mDegToRad(-180))
            %deltaZ += mDegToRad(360);

        // if change required is too small, skip it
        if(mAbs(%deltaZ) >= $levelCameras::minFollowChange)
        {
            // smooth out the rotation - we don't want to 'snap' instantly to the new orientation
            %rotAngZ = %oldAngZ + (%deltaZ / $levelCameras::followSmoothing);
        }
        else
            %deltaZ = 0;
    }
    else
    {
        %rotAngZ = %oldAngZ;
    }


    // tilt -----------------
    %oldAngXY = getWord(%this.getTransform(), 3) * getWord(%this.getTransform(), 6);

    if(%this.canTilt)
    {
        %targetAngXY = mAtan(vectorLen(%dirVec), getWord(%dirVec, 2)) - ($PI / 2);
    
        %deltaXY = (%targetAngXY - %oldAngXY);

        // if change required is too small, skip it
        if(mAbs(%deltaXY) >= $levelCameras::minFollowChange)
        {
            %rotAngXY = %oldAngXY + (%deltaXY / $levelCameras::followSmoothing);
        }
        else
            %deltaXY = 0;
    }
    else
    {
        %rotAngXY = %oldAngXY;
    }


    echo("Orig Trans:" SPC %this.getTransform());


    // combine rotations ----
    %matZ  = matrixCreateFromEuler("0 0" SPC -%rotAngZ);
    %matXY = matrixCreateFromEuler(-%rotAngXY SPC "0 0"); 
    %finalRot = getWords(matrixMultiply(%matZ, %matXY), 3, 6);
    %this.setTransform(%this.getPosition() SPC %finalRot);

    echo("oldAngZ:"     SPC %oldAngZ);
    echo("oldAngXY:"    SPC %oldAngXY);
    echo("targetAngZ:"  SPC %targetAngZ);
    echo("targetAngXY:" SPC %targetAngXY);
    echo("RotAngZ:"     SPC %rotAngZ);
    echo("RotAngXY:"    SPC %rotAngXY);
    echo("DeltaZ:"      SPC %deltaZ);
    echo("DeltaXY:"     SPC %deltaXY);
    echo("FinalRot:"    SPC %finalRot);
    echo("New Trans:"   SPC %this.getTransform());
}

#1
11/04/2006 (9:35 am)
Given settings of:
$levelCameras::updateDelay     = 35;    // ms
$levelCameras::minFollowChange = 0.025; // any movement requiring less than this in radians will be ignored
$levelCameras::followSmoothing = 10;    // camera movements are smoothed by this - higher is slower but smoother

on a camera that pans and tilts the output after the camera has 'settled' into what it considers a locked position are:

Orig Trans: 7.23139 -3.49921 6.51936 0.176248 0.132009 -0.975454 1.30965
oldAngZ: -1.2775
oldAngXY: 0.230823
targetAngZ: -1.35971
targetAngXY: 0.612866
RotAngZ: -1.28572
RotAngXY: 0.269028
DeltaZ: -0.0822066
DeltaXY: 0.382043
FinalRot: 0.176248 0.132009 -0.975454 1.30965
New Trans: 7.23139 -3.49921 6.51936 0.176248 0.132009 -0.975454 1.30965

but it's aiming higher than the player and DeltaZ should be 0 if it's 'locked' (DeltaZ is perfect 0 when tilting is disabled) Given settings of:

$levelCameras::updateDelay     = 35;    // ms
$levelCameras::minFollowChange = 0.005; // any movement requiring less than this in radians will be ignored
$levelCameras::followSmoothing = 1;    // camera movements are smoothed by this - higher is slower but smoother

the output is:

Orig Trans: 7.23139 -3.49921 6.51936 0.349602 0.282624 -0.893254 1.47117
oldAngZ: -1.31413
oldAngXY: 0.514324
targetAngZ: -1.35971
targetAngXY: 0.612866
RotAngZ: -1.35971
RotAngXY: 0.612866
DeltaZ: -0.0455815
DeltaXY: 0.0985424
FinalRot: 0.349601 0.282625 -0.893254 1.47117
New Trans: 7.23139 -3.49921 6.51936 0.349602 0.282624 -0.893254 1.47117

and the camera seems to lock on ok but the output tells me it's still not working properly.

Finally, for comparison, here is the output of the same camera with tilt disabled using the same settings:

Orig Trans: 7.23139 -3.49921 6.51936 0 0 -1 1.35797
oldAngZ: -1.35797
oldAngXY: 0
targetAngZ: -1.35971
targetAngXY: 
RotAngZ: -1.35797
RotAngXY: 0
DeltaZ: 0
DeltaXY: 
FinalRot: 0 0 -1 1.35797
New Trans: 7.23139 -3.49921 6.51936 0 0 -1 1.35797

The camera is aiming far too high, but as you can see, pan is now perfect as it should be. Thanks to anyone who read this far, and hopefully someone better at math can shed some light on why this is occuring.