Child GameObject rotation works nicely until I change pitch and heading of the parent

Hello everyone,

Currently I am facing an issue when I pitch my ship down and then turn it to some side the turret rotation will get bugged.

I am not sure how to fix this. I have been reading about polar coordinate spaces from 3D Math Primer for Graphics and Game development and came to the conclusion that in order to rotate the turrets stand I have to change heading and pitch the gun up and down.

It all works correctly until I pitch my ship and then change its heading…

The turret kinda gets angled to the side.

You can see it here in the gif:

Unity_bRnhxuDKDO-ezgif.com-video-to-gif-converter

My code for turret rotation:

public void AimAt(Vector3 target)
{
    //Move stand(heading) 
    Vector3 lookDir = target - transform.position;  
    float standDeg = Mathf.Rad2Deg * Mathf.Atan2(lookDir.x, lookDir.z);
    transform.rotation = Quaternion.Euler(transform.rotation.eulerAngles.x, standDeg, transform.rotation.eulerAngles.z);

    //Move the gun(pitch) 
    float deg = Mathf.Rad2Deg * Mathf.Asin(-lookDir.y / lookDir.magnitude);
    if (deg > 0) return;
    Gun.transform.rotation = Quaternion.Euler(deg, Gun.transform.rotation.eulerAngles.y, Gun.transform.rotation.eulerAngles.z);
}

Code where I get the target from:

Vector3 mousePos = Mouse.current.position.ReadValue();
mousePos.z = Camera.main.farClipPlane;
Vector3 point = Camera.main.ScreenToWorldPoint(mousePos);

foreach (Turret t in Entity.Turrets)
    _lookAt.Exec(t, point);

Exec:

public void Exec(Turret entity, Vector3 data)
{
    if(entity == null) return;

    entity.AimAt(data);
}

transform.localRotation

1 Like

This should not occur if the turret is parented to the ship and the rotation is handled using only local rotation for the turret itself.

Also you will want to use only quaternions, not eulers, as the latter can’t represent all rotations in 3d space. You seem to be using math even (atan2, asin, ewww, ugh) when that aiming could easily be achieved with a lookat target.

Even if you need to make some adjustments to have imprecise aim you’d first look at target, then randomly offset the rotation using Random’s unitSphere and multiplying that with the rotation quaternion (multiply quaternions equals adding the rotations).

1 Like

I was using trig math because I am going through that book and was interested in polar coordinates.

I have fixed the issue by having the help from reddit user:

//Move stand(heading)

Vector3 lookDir = transform.parent.InverseTransformPoint(target - transform.position);

float standDeg = Mathf.Rad2Deg * Mathf.Atan2(lookDir.x, lookDir.z);

transform.localRotation = Quaternion.Euler(0, standDeg, 0);

//Move the gun(pitch)

float deg = Mathf.Rad2Deg * Mathf.Asin(-lookDir.y / lookDir.magnitude);

if (deg > 0) return;

Gun.transform.localRotation = Quaternion.Euler(deg, 0, 0);

But now I am wondering why did that work with transform.parent.InverseTransformPoint.

@CodeSmile
I want to be able to move the stand of my turret only around its UP axis and the gun itself only around its RIGHT axis. So the gun can only pitch up and down. I am not sure how to do that with LookRotation.

Still having issues with this… I simply don’t get unity rotations. I changed from polar coordinate calculation now to this code:

 public void AimAt(Vector3 target)
 {
     TurretBaseRotation(target);
     TurretGunRotation(target);
 }

 private void TurretBaseRotation(Vector3 target)
 {
     // Get aim position in parent gameobject local space in relation to aim position world space 
     Vector3 targetPositionInLocalSpace = transform.parent.InverseTransformPoint(target); 
     // Set "aimPoint" Y position to zero, since this is horizontal rotation n because we dont need it
     targetPositionInLocalSpace.y = 0.0f;
     // Store limit value of the rotation
     Vector3 limitedRotation = targetPositionInLocalSpace;

     // limit turret horizontal rotation according to its rotation limit, so how much to the left and how much to the right can it rotate
     // Rotate vector around a Cross(Vector3.forard, targetPosInLocalSpace)
     Vector3 localSpace = _turretBase.InverseTransformPoint(target);
     if (localSpace.x >= 0.0f)
         limitedRotation = Vector3.RotateTowards(Vector3.forward, targetPositionInLocalSpace, Mathf.Deg2Rad * maxMinHorizontalRot.x, float.MaxValue);
     else
         limitedRotation = Vector3.RotateTowards(Vector3.forward, targetPositionInLocalSpace, Mathf.Deg2Rad * maxMinHorizontalRot.y, float.MaxValue);

     //Get direction
     Vector3 right = limitedRotation;
     if (transform.localScale.x < 0.0f)           
         right.x *= -1.0f;
     
     Quaternion whereToRotate = Quaternion.LookRotation(right);
     // Rotate the turret
     _turretBase.transform.localRotation = Quaternion.RotateTowards(_turretBase.transform.localRotation, whereToRotate, RotateSpeed * Time.deltaTime);

 }

 private void TurretGunRotation(Vector3 target)
 {
     // Get aim position in barrel gameobject local space in relation to aim position world space 
     Vector3 targetPositionInLocalSpace = _turretBase.transform.InverseTransformPoint(target);
     if(isBaseSwitched)
         targetPositionInLocalSpace = _gun.transform.InverseTransformPoint(target);

     // Set "TargetPositionInLocalSpace" X position to zero, since this is vertical rotation n because we dont need it
     targetPositionInLocalSpace.x = 0.0f;
     // Store limit value of the rotation
     Vector3 limitedRotation = targetPositionInLocalSpace;
     // limit turret vertical rotation according to its rotation limit, so how much up and down can it rotate
     if (targetPositionInLocalSpace.y >= 0.0f)
         limitedRotation = Vector3.RotateTowards(Vector3.forward, targetPositionInLocalSpace, Mathf.Deg2Rad * maxMinVerticalRot.x, float.MaxValue);
     else
         limitedRotation = Vector3.RotateTowards(Vector3.forward, targetPositionInLocalSpace, Mathf.Deg2Rad * maxMinVerticalRot.y, float.MaxValue);
     
     Vector3 up = limitedRotation;
     if (transform.localScale.y < 0.0f)
         up.y *= -1.0f;

     //Get direction
     Quaternion whereToRotate = Quaternion.LookRotation(up);
     // Rotate the barrel
     _gun.transform.localRotation = Quaternion.RotateTowards(_gun.localRotation, whereToRotate, RotateSpeed * Time.deltaTime);


     Vector3 gunTransformPos = _gun.transform.position;
     Vector3 gunForward = _gun.transform.forward;
     
     if(isBaseSwitched)
     {
         gunTransformPos = _turretBase.transform.position;
         gunForward = _turretBase.transform.forward;
     }

     Vector3 fromTo = (target - gunTransformPos).normalized;
     if (Vector3.Dot(fromTo, gunForward) < 0.9f)
     {
         _cantAim = true;
     }
     else
         _cantAim = false;

 }

The gun seem to be aiming but to a massive offset. It is not aiming at the center of the ship.

    void Update()
    {
        Ray ray = cam.ScreenPointToRay(Input.mousePosition);
        if (!Physics.Raycast(ray, out RaycastHit hit, maxRayDistance))
            hit.point = ray.origin + ray.direction * maxRayDistance; // if nothing below the cursor then we just look in the cursor's general direction
        RotateTurret(hit.point);
        RotateCannon(hit.point);
    }

    void RotateTurret(Vector3 targetPosition)
    {
        turret.forward = Vector3.Slerp(turret.forward, targetPosition - turret.position, turretSpeed * Time.deltaTime);
        turret.localEulerAngles = new Vector3(0, turret.localEulerAngles.y, 0); // only allow yaw rotation
    }

    void RotateCannon(Vector3 targetPosition)
    {
        cannon.forward = Vector3.Slerp(cannon.forward, targetPosition - cannon.position, cannonSpeed * Time.deltaTime);
        cannon.localEulerAngles = new Vector3(Mathf.Clamp(-Mathf.DeltaAngle(cannon.localEulerAngles.x, 0), -60, 30), 0, 0); // only allow pitch rotation and clamp it
    }