Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Rotating around 3D cube Camera Controller

Discussion in 'Scripting' started by wagon347, Jul 13, 2023.

  1. wagon347

    wagon347

    Joined:
    Mar 28, 2019
    Posts:
    6
    I am Unity game developer with more than 2 years experience. I am working in a personal project, a mobile game for Android in Unity.

    I am trying to do a camera controller for a 3d Cube Game in Unity. I have a controller working, I can position the camera in the six faces of the cube. But The problem went when I move the camera several times. (See the image for more explanation)

    For example, I always start the game on Top face, but after several moves the controller starts to move West, East when it should move North, South. I understand what the problem is, that the camera depends on the previous movements since it is rotating spherically. but I don't know what data structures to use or how to fix this. description of the problem

    see this - current state cube 3D game .gif


    I know is a complex problem that requires 3D Math, but maybe some of you have faced this before or have a better aproach, Thank you.

    Here is some relevant code I wrote(with the help of chatGPT) :


    Code (CSharp):
    1. public class TiltControl : MonoBehaviour
    2. {
    3.  
    4.     // Define the faces of the cube
    5.     public enum CubeFace
    6.     {
    7.         Front,
    8.         Back,
    9.         Left,
    10.         Right,
    11.         Top,
    12.         Bottom
    13.     }
    14.  
    15.     // Define the cardinal directions
    16.     public enum CardinalDirection
    17.     {
    18.         North,
    19.         East,
    20.         South,
    21.         West
    22.     }
    23.  
    24.     // Dictionary to store adjacent faces for each face and direction
    25.     private Dictionary<CubeFace, List<CubeFace>> adjacentFaces;
    26.  
    27.     private CubeFace currentCameraFace = CubeFace.Top;
    28.  
    29.     private void Start()
    30.     {
    31.         // Initialize the adjacent faces dictionary based on the current face
    32.         adjacentFaces = new Dictionary<CubeFace, List<CubeFace>>();
    33.         adjacentFaces[CubeFace.Front] = new List<CubeFace> { CubeFace.Bottom, CubeFace.Left, CubeFace.Top, CubeFace.Right };
    34.         adjacentFaces[CubeFace.Back] = new List<CubeFace> { CubeFace.Top, CubeFace.Right,CubeFace.Bottom, CubeFace.Left};
    35.         adjacentFaces[CubeFace.Left] = new List<CubeFace> { CubeFace.Front, CubeFace.Top,  CubeFace.Back, CubeFace.Bottom };
    36.         adjacentFaces[CubeFace.Right] = new List<CubeFace> { CubeFace.Front, CubeFace.Bottom,CubeFace.Back, CubeFace.Top };
    37.         adjacentFaces[CubeFace.Top] = new List<CubeFace> { CubeFace.Front,  CubeFace.Right,CubeFace.Back, CubeFace.Left};
    38.         adjacentFaces[CubeFace.Bottom] = new List<CubeFace> { CubeFace.Back,  CubeFace.Left, CubeFace.Front, CubeFace.Right };
    39. }
    40.  
    41.     private void LateUpdate()
    42.     {
    43.          if (cameraJoystick.Horizontal < -0.25)
    44.             {
    45.                 ChangeFaceToDir(CardinalDirection.West);
    46.             }
    47.             else if (cameraJoystick.Horizontal > 0.25)
    48.             {
    49.                 ChangeFaceToDir(CardinalDirection.East);
    50.             }
    51.             else if (cameraJoystick.Vertical > 0.25)
    52.             {
    53.                 ChangeFaceToDir(CardinalDirection.North);
    54.             }
    55.             else if (cameraJoystick.Vertical < -0.25)
    56.             {
    57.                 ChangeFaceToDir(CardinalDirection.South);
    58.             }
    59.  
    60. }
    61.  
    62.     private void ChangeFace(CubeFace face)
    63.     {
    64.         currentCameraFace = face;
    65.         cameraText.SetText(currentCameraFace.ToString());
    66.         newPosition = cameraPoints[(int)face];
    67.     }
    68.  
    69.     private void ChangeFaceToDir(CardinalDirection dir)
    70.     {
    71.    
    72.         Debug.Log("toDir" + dir);
    73.         currentCameraFace = GetAdjacentFace(currentCameraFace, dir);
    74.         ChangeFace(currentCameraFace);
    75.  
    76.     }
    77.  
    78.     private CubeFace GetAdjacentFace(CubeFace face, CardinalDirection direction)
    79.     {
    80.         if (adjacentFaces.ContainsKey(face))
    81.         {
    82.  
    83.             Vector2Int dirCard = GetCardinalDirection(direction);
    84.             Vector3 rotated = (-1 * myCamera.right * dirCard.x) + (myCamera.forward * dirCard.y);
    85.  
    86.             Transform newTransform = Instantiate(cameraPoints[0]);
    87.             newTransform.position = myCamera.position;
    88.             newTransform.Rotate(myCamera.forward,Vector3.Angle(myCamera.right,rotated));
    89.  
    90.  
    91.            
    92.             Debug.Log("newTransform" + newTransform.right.normalized);
    93.  
    94.             int dir;
    95.  
    96.             dir = (int)GetCardinalFromDirection(new Vector2Int((int)newTransform.right.normalized.x,(int)newTransform.right.normalized.z));
    97.  
    98.             Destroy(newTransform.gameObject);
    99.  
    100.             Debug.Log("dir" + dir);
    101.  
    102.  
    103.             return adjacentFaces[face][dir];
    104.         }
    105.  
    106.         Debug.LogWarning("Invalid face: " + face);
    107.         return face;
    108.     }
    109.  
    110.     private Vector2Int GetCardinalDirection(CardinalDirection direction)
    111.     {
    112.         switch (direction)
    113.         {
    114.             case CardinalDirection.North: return new Vector2Int(1, 0);
    115.             case CardinalDirection.East: return new Vector2Int(0, 1);
    116.             case CardinalDirection.South: return new Vector2Int(-1, 0);
    117.             case CardinalDirection.West: return new Vector2Int(0, -1);
    118.             default: return new Vector2Int();
    119.         }
    120.     }
    121.  
    122.     private CardinalDirection GetCardinalFromDirection(Vector2Int direction)
    123.     {
    124.         if (direction == new Vector2Int(1, 0))
    125.         {
    126.             return CardinalDirection.North;
    127.         }
    128.         else if (direction == new Vector2Int(0, 1))
    129.         {
    130.             return CardinalDirection.East;
    131.  
    132.         }
    133.         else if (direction == new Vector2Int(-1, 0))
    134.         {
    135.             return CardinalDirection.South;
    136.  
    137.         }
    138.         else if (direction == new Vector2Int(0, -1))
    139.         {
    140.             return CardinalDirection.West;
    141.  
    142.         }
    143.         return CardinalDirection.North;
    144.     }
    145.  
    Code explanantion:

    Firstly, I initialize the 4 possible faces that one face can have (maybe this is the problem, as is fixed positions and the camera rotates spherically changing the orientation) I check the input from touch Joysctick, that just have 4 directions valid N,S,E,W Then in GetAdacentFace I should get the correct face depending on the Joystick direction, but I get wrong, I do some calculations with the orientation of the main camera and the Joystick direction (and returns the wrong face) With this returned face I move the camera to the face
     
  2. ijmmai

    ijmmai

    Joined:
    Jun 9, 2023
    Posts:
    188
    I am most likely oversimplifying things, but is it important to know the exact faces of the cube, or is it just a point of view. In other words instead of all the calculating, could it be as easy as rotating 90 or -90 on the desired axis?

    It looks like a 3D maze, so I assume there is a ball/player somewhere which will prevent you from rotating the cube instead of the camere
     
  3. wagon347

    wagon347

    Joined:
    Mar 28, 2019
    Posts:
    6
    No I can't rotate the cube, I have to rotate the camera, because it's a physics-based game and when you move the cube the ball gets pushed cause of inertial force.
     
  4. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class TiltControl : MonoBehaviour
    6. {
    7.     Vector3 goal;
    8.     Vector3 up;
    9.  
    10.     const float distance=3;
    11.  
    12.     void Start()
    13.     {
    14.         up=transform.up;
    15.         goal=Vector3.forward*distance;
    16.     }
    17.  
    18.     void Update()
    19.     {
    20.         if (Vector3.Dot(transform.forward,-goal.normalized)>0.999f) // are we nearly there yet?
    21.         {
    22.             Vector3 d=new Vector3(Input.GetAxisRaw("Horizontal"),Input.GetAxisRaw("Vertical"),0);
    23.             if (d.x!=0)
    24.             {
    25.                 up=Vector3Int.RoundToInt(transform.up);
    26.                 goal=Vector3Int.RoundToInt(transform.right*d.x*distance);
    27.             }
    28.             else if (d.y!=0)
    29.             {
    30.                 up=Vector3Int.RoundToInt(transform.forward*d.y);
    31.                 goal=Vector3Int.RoundToInt(transform.up*d.y*distance);
    32.             }
    33.         }
    34.         transform.position=Vector3.Slerp(transform.position,goal,8f*Time.deltaTime);
    35.         transform.LookAt(Vector3.zero,up);
    36.     }
    37. }
    38.  
     
  5. wagon347

    wagon347

    Joined:
    Mar 28, 2019
    Posts:
    6
    Thank you I already found a solution:

    Adding a camera rig inside the cube and rotating this rig:


    Code (CSharp):
    1. private void ChangeFaceToDir(CardinalDirection dir) {
    2.  
    3.     Debug.Log("toDir" + dir);
    4.     //cameraText.SetText(currentCameraFace.ToString());
    5.  
    6.     switch (dir)
    7.     {
    8.         case CardinalDirection.North:
    9.             rigPoint.Rotate(Vector3.right, 90f,Space.Self);
    10.             break;
    11.         case CardinalDirection.South:
    12.             rigPoint.Rotate(Vector3.right, -90f, Space.Self);
    13.             break;
    14.         case CardinalDirection.East:
    15.             rigPoint.Rotate(Vector3.forward, -90f, Space.Self);
    16.  
    17.             break;
    18.         case CardinalDirection.West:
    19.             rigPoint.Rotate(Vector3.forward, 90f, Space.Self);
    20.  
    21.             break;
    22.         default:
    23.             break;
    24.     }
    25.     newPosition = focusPoint;
    26.  
    27. }
     
  6. ijmmai

    ijmmai

    Joined:
    Jun 9, 2023
    Posts:
    188
    Yep that is what I assumed.

    Great to see you found a solution :)
     
  7. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    I was curious about how I'd go about this myself, so I ended up writing this on a whim, namely to show how this can be done with quaternions:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class CameraOrbitRotator : MonoBehaviour
    4. {
    5.    [SerializeField]
    6.    private float _rotationAmount = 90f;
    7.  
    8.    [SerializeField]
    9.    private float _rotationSpeed = 10f;
    10.  
    11.    private Quaternion _targetRotation;
    12.  
    13.    private void Awake()
    14.    {
    15.        _targetRotation = transform.rotation;
    16.    }
    17.      
    18.    private void Update()
    19.    {
    20.        UpdateTargetRotation();
    21.        RotateAroundToDirection();
    22.    }
    23.  
    24.    private void UpdateTargetRotation()
    25.    {
    26.        if (!ReachedTarget())
    27.        {
    28.            return;
    29.        }
    30.  
    31.        int hoz = Input.GetKeyDown(KeyCode.D) ? -1 : 0;
    32.        hoz += Input.GetKeyDown(KeyCode.A) ? 1 : 0;
    33.  
    34.        int vert = Input.GetKeyDown(KeyCode.W) ? 1 : 0;
    35.        vert += Input.GetKeyDown(KeyCode.S) ? -1 : 0;
    36.  
    37.        Vector3 axis = Vector3.zero;
    38.        int dir = 0;
    39.  
    40.        if (hoz != 0)
    41.        {
    42.            axis = transform.up;
    43.            dir = hoz;
    44.        }
    45.        else if (vert != 0)
    46.        {
    47.            axis = transform.right;
    48.            dir = vert;
    49.        }
    50.  
    51.        Quaternion rotation = Quaternion.AngleAxis(_rotationAmount * dir, axis);
    52.        _targetRotation = rotation * _targetRotation;
    53.    }
    54.  
    55.    private void RotateAroundToDirection()
    56.    {
    57.        if (ReachedTarget())
    58.        {
    59.            return;
    60.        }
    61.  
    62.        Quaternion cRotation = transform.rotation;
    63.        Quaternion rTowards = Quaternion.RotateTowards(cRotation, _targetRotation, _rotationSpeed);
    64.        transform.rotation = rTowards;
    65.    }
    66.  
    67.    private bool ReachedTarget()
    68.    {
    69.        Quaternion cRotation = transform.rotation;
    70.        float rDot = Quaternion.Dot(cRotation, _targetRotation);
    71.        return Mathf.Approximately(rDot, 1);
    72.    }
    73. }
    74.  
    This would go onto an empty the camera is a child of. The main principle is that we just have a target rotation, and we figure out an axis to rotate around on, generate said rotation, and apply that to the target rotation.