# C# - Spacecraft docking problem: Camera look at with relative roll (Solved)

Hi,

Not sure how to bite following problem, neither know, how to search for the specific answer, while trying to get my head round.

Prior to explaining the case, I just would like to state, I am ok generally with quaternions, identity, vectors, etc. math, when required. But yet struggling this time, to picture the solution.

Lets start simple case scenario (OK).

Imagine docking of the spacecraft, to the spinning orbital space station, around own (forward z) axis.
Normally and easiest way to dock, is to align spacecraft, parallel to axis of the rotating space station.
Then it is simple, to apply spacecraft rotation, to match rotation of space station. And docking can be complete, with no issue.

But what I am trying to achieve, is the camera with look at space station, which spins (rolls) the same, as the docking space station. Result would be simple as that, if I look from behind of spacecraft at space station, spacecraft and stars will appear rotating while docking station will be appearing none rotating (static).

Ok. That is simple to do. By grabbing rotation of the space station and applying roll to the camera. Alternatively getting relative rotation of the docking space station to the spacecraft, along given axis, to get relevant rotation angle.

Now troubling case scenario (Problem).

Imagine same spacecraft and spinning docking space station.
However, this time my spacecraft is approaching the space station from the distance and is not aligned in parallel, to the spinning axis of the docking station. It means, there is some angular offset from the spinning axis, between spacecraft and the station.

For simplicity lets assume, docking station rotates along world forward axis (z).
Then my spacecraft is approaching space station, on the world xz plane, while world position y (up) is 0 (for simplicity), in respect to space station.

Lets see 3 cases:

1. If my spacecraft would approach space station from z axis (from the front of space station), while x axis is 0, which is 0 degree offset (along world y axis) to forward z axis, looking camera at the station (from the front of station), should rotate (roll), as per described simple case scenario.

2. If my spacecraft would approach space station from x axis (left / right), while z axis is 0, which is ±90 degree offset (along world y axis) to forward z axis, looking camera at the station (from the side of station), should not rotate (roll).

3. However, if my spacecraft would approach space station from z axis 1, while x axis is also 1, which is 45 degrees offset (along world y axis) to forward z axis, my look at space station camera rotation should be what? This is a point I struggle atm.

1. That I should have a vector between spacecraft and space station (straight forward).
2. Getting perpendicular to that vector local up (y’) and right (x’) vectors.
3. Get a plane of that vectors y’x’, with the world xyz origin of the space station position. In other words, plane is perpendicular to the vector, described in point 1.
4. Get space station local right (x) [or up (y)] axis, which are perpendicular to spinning forward z axis.
5. Project that x [or y] axis on y’x’ plane ( derived earlier from the vector ).
6. Get position of projected vector on the y’x’ plane
7. Calculate the angle using atan2 method.
8. Apply angle to the roll of to the camera, with look at the space station.

I can see potential problem, when angle offset is approaching absolute ±90 degrees. But I think, I can simply trigger bool flag, to on / off, if angle is too big.

I will get hands on, to try this approach, as soon, as I get chance.

Providing you understood what I had on mind ( without visualization atm.), do you think this approach will work? Or do you know any different (better/simpler?) method (s)?

Many thanks in advance for a contribution.

And I have done it.

Made a quick prototype, and very simplistic. More-less as described above, with additional tweaking.
Script allows mi to be hooked by other scripts, and switch, along which axis object rotates.
And weather I want to roll my camera along own forward axis, or not. Or override roll angle manually.

As predicted, bigger deviation of the spinning angle, makes spinning of the camera (if enabled) getting weird. Therefore I made relevant switches via enumerator.

In attached screenshot:

• Can move Targets/Follow and Targets/SpaceDock.
• Where Targets/Follow (box with camera) always look at SpaceDock (darker green).
• SpaceDock can be set to spin around own axis.
• VectorNormalPlane (light semitransparent blue-ish) indicates perpendicular plane, to the vector (blue line) between camera follower and SpaceDock target.
• Magneta line marks 0 angle on the plane
• Yellow line indicates current angle (103 deg as example)
• Green and red lines indicate plane’s local vector up and right.

Code example is attached below and screenshot of project, to make a bit more sense of the prototype.

``````using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestCamRoll : MonoBehaviour {

public Transform tr_camera ;

public Transform tr_lookAt ;

public Transform tr_target ;

public Transform tr_vectorPlane ; // perpendicular plane

public Vector3 V3_targetAngularVelocity ;

public TargetSpinAxis enum_targetSpinAxis = TargetSpinAxis.UpY ;

public enum TargetSpinAxis
{
Disabled, // none

/// <summary>
/// Same as Disabled
/// </summary>
Reset,

/// <summary>
/// Custom spin can be assigned, with given angle in degrees
/// </summary>
CustomCameraRollAngle,

UpY,
RightX,
ForwardZ,
}

private Vector3 V3_targetSpinAlongAxis ;

/// <summary>
/// Can be either -180 to +180 or 0 to 360 degrees
/// </summary>
public  float f_cameraAngleDeg = 0 ;

// Update is called once per frame
void Update ()
{
// test, normally dont call GetCompnent every frame
tr_target.GetComponent <Rigidbody> ().angularVelocity = tr_target.rotation * V3_targetAngularVelocity ;

Vector3 V3_diff = tr_follow.position - tr_lookAt.position ;

Vector3 V3_vectorNorm = V3_diff.normalized ;

Quaternion Q_lookAt = Quaternion.LookRotation ( V3_vectorNorm ) ;

tr_vectorPlane.position = tr_target.position ;
tr_vectorPlane.rotation = Q_lookAt ;

Vector3 V3_up = Q_lookAt * Vector3.up ;
Vector3 V3_right = Q_lookAt * Vector3.right ;

Debug.DrawLine ( tr_follow.position, tr_lookAt.position, Color.blue ) ;

// mark plane
Debug.DrawRay ( tr_lookAt.position, V3_up, Color.green ) ;
Debug.DrawRay ( tr_lookAt.position, V3_right, Color.red ) ;

switch ( enum_targetSpinAxis )
{

case TargetSpinAxis.UpY :
V3_targetSpinAlongAxis = tr_target.rotation * Vector3.right ;
// V3_targetSpinAlongAxis = tr_target.rotation * Vector3.right ;
break ;

case TargetSpinAxis.RightX:
V3_targetSpinAlongAxis = tr_target.rotation * Vector3.forward ;
// V3_targetSpinAlongAxis = tr_target.rotation * Vector3.up ;
break ;

case TargetSpinAxis.ForwardZ:
V3_targetSpinAlongAxis = tr_target.rotation * Vector3.up ;
// V3_targetSpinAlongAxis = tr_target.rotation * Vector3.forward ;
break ;

case TargetSpinAxis.CustomCameraRollAngle:
// Ignores target spin axis, but custom angle can be set via method
break ;

default : // Disabled or Reset
V3_targetSpinAlongAxis = Vector3.forward ;
f_cameraAngleDeg = 0 ;
break ;

}

if ( enum_targetSpinAxis == TargetSpinAxis.CustomCameraRollAngle
|| enum_targetSpinAxis == TargetSpinAxis.Disabled
|| enum_targetSpinAxis == TargetSpinAxis.Reset )
{
// f_cameraAngleDeg = 0 ;
// allows overriding custom angle through method
}
else
{
// according to target spin axis

Vector3 V3_projectOnPlane = Vector3.ProjectOnPlane ( V3_targetSpinAlongAxis, ( Q_lookAt * Vector3.forward ) ) ;
Vector3 V3_planeRight = Q_lookAt * Vector3.right ;

// direction of 0 angle
Debug.DrawLine ( tr_target.position, tr_target.position + ( V3_planeRight ), Color.magenta ) ;

// prjected vector on plane
Debug.DrawLine ( tr_target.position, tr_target.position + V3_projectOnPlane, Color.yellow ) ;

f_cameraAngleDeg = Vector3.SignedAngle ( V3_planeRight, V3_projectOnPlane, V3_vectorNorm ) ;

}

tr_camera.rotation = Q_lookAt * Quaternion.Euler ( Vector3.forward * f_cameraAngleDeg ) ;

}

// use by external scripts

public void _SetTargetSpinAxisReference ( TargetSpinAxis enum_targetSpinAxis )
{
this.enum_targetSpinAxis = enum_targetSpinAxis ;
}

/// <summary>
/// Accepts eiter -180 to +180 and 0 to 360 degrees.
/// Sets TargetSpinAxis mode to TargetSpinAxis.CustomCameraRollAngle
/// Optionally, _SetTargetSpinAxisReference method can be called, with reset flag
/// </summary>
/// <param name="targetSpinAxis"></param>
public void _SetCameraRollAngle ( float f_cameraAngleDeg )
{
_SetTargetSpinAxisReference ( TargetSpinAxis.CustomCameraRollAngle ) ;

this.f_cameraAngleDeg = f_cameraAngleDeg ;
}

}
``````