0
\$\begingroup\$

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 make 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 happens when I move the camera several times. (See the images for more explanation)

visual explanation

demo

For example, I always start the game on the 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.

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) :

public class TiltControl : MonoBehaviour
{

    // Define the faces of the cube
    public enum CubeFace
    {
        Front,
        Back,
        Left,
        Right,
        Top,
        Bottom
    }

    // Define the cardinal directions
    public enum CardinalDirection
    {
        North,
        East,
        South,
        West
    }

    // Dictionary to store adjacent faces for each face and direction
    private Dictionary<CubeFace, List<CubeFace>> adjacentFaces;

    private CubeFace currentCameraFace = CubeFace.Top;

    private void Start()
    {
        // Initialize the adjacent faces dictionary based on the current face
        adjacentFaces = new Dictionary<CubeFace, List<CubeFace>>();
        adjacentFaces[CubeFace.Front] = new List<CubeFace> { CubeFace.Bottom, CubeFace.Left, CubeFace.Top, CubeFace.Right };
        adjacentFaces[CubeFace.Back] = new List<CubeFace> { CubeFace.Top, CubeFace.Right,CubeFace.Bottom, CubeFace.Left};
        adjacentFaces[CubeFace.Left] = new List<CubeFace> { CubeFace.Front, CubeFace.Top,  CubeFace.Back, CubeFace.Bottom };
        adjacentFaces[CubeFace.Right] = new List<CubeFace> { CubeFace.Front, CubeFace.Bottom,CubeFace.Back, CubeFace.Top };
        adjacentFaces[CubeFace.Top] = new List<CubeFace> { CubeFace.Front,  CubeFace.Right,CubeFace.Back, CubeFace.Left};
        adjacentFaces[CubeFace.Bottom] = new List<CubeFace> { CubeFace.Back,  CubeFace.Left, CubeFace.Front, CubeFace.Right };
    }

    private void LateUpdate()
    {
         if (cameraJoystick.Horizontal < -0.25)
         {
             ChangeFaceToDir(CardinalDirection.West);
         }
         else if (cameraJoystick.Horizontal > 0.25)
         {
             ChangeFaceToDir(CardinalDirection.East);
         }
         else if (cameraJoystick.Vertical > 0.25)
         {
             ChangeFaceToDir(CardinalDirection.North);
         }
         else if (cameraJoystick.Vertical < -0.25)
         {
             ChangeFaceToDir(CardinalDirection.South);
         }
    }

    private void ChangeFace(CubeFace face)
    {
        currentCameraFace = face;
        cameraText.SetText(currentCameraFace.ToString());
        newPosition = cameraPoints[(int)face];
    }

    private void ChangeFaceToDir(CardinalDirection dir)
    {
     
        Debug.Log("toDir" + dir);
        currentCameraFace = GetAdjacentFace(currentCameraFace, dir);
        ChangeFace(currentCameraFace);

    }

    private CubeFace GetAdjacentFace(CubeFace face, CardinalDirection direction)
    {
        if (adjacentFaces.ContainsKey(face))
        {

            Vector2Int dirCard = GetCardinalDirection(direction);
            Vector3 rotated = (-1 * myCamera.right * dirCard.x) + (myCamera.forward * dirCard.y);

            Transform newTransform = Instantiate(cameraPoints[0]);
            newTransform.position = myCamera.position;
            newTransform.Rotate(myCamera.forward,Vector3.Angle(myCamera.right,rotated));


            
            Debug.Log("newTransform" + newTransform.right.normalized);

            int dir;

            dir = (int)GetCardinalFromDirection(new Vector2Int((int)newTransform.right.normalized.x,(int)newTransform.right.normalized.z));

            Destroy(newTransform.gameObject);

            Debug.Log("dir" + dir);


            return adjacentFaces[face][dir];
        }

        Debug.LogWarning("Invalid face: " + face);
        return face;
    }

    private Vector2Int GetCardinalDirection(CardinalDirection direction)
    {
        switch (direction)
        {
            case CardinalDirection.North: return new Vector2Int(1, 0);
            case CardinalDirection.East: return new Vector2Int(0, 1);
            case CardinalDirection.South: return new Vector2Int(-1, 0);
            case CardinalDirection.West: return new Vector2Int(0, -1);
            default: return new Vector2Int();
        }
    }

    private CardinalDirection GetCardinalFromDirection(Vector2Int direction)
    {
        if (direction == new Vector2Int(1, 0))
        {
            return CardinalDirection.North;
        }
        else if (direction == new Vector2Int(0, 1))
        {
            return CardinalDirection.East;

        }
        else if (direction == new Vector2Int(-1, 0))
        {
            return CardinalDirection.South;

        }
        else if (direction == new Vector2Int(0, -1))
        {
            return CardinalDirection.West;

        }
        return CardinalDirection.North;
    }

Code explanation:

  1. I initialize the 4 possible neighbouring faces that one face can transition to (maybe this is the problem, as is fixed positions and the camera rotates spherically changing the orientation).

  2. I check the input from the touch Joystick, that has just 4 valid directions (N,S,E,W)

  3. Then in GetAdacentFace I should get the correct face depending on the Joystick direction, but here I get the wrong value in some cases. I do some calculations with the orientation of the main camera and the Joystick direction (and it sometimes returns the wrong face)

  4. With this returned face, I move the camera to that face

\$\endgroup\$
5
  • \$\begingroup\$ My approach to this would be to create a camera rig with the camera parented to a focus-point that is inside the cube and then rotate the camera around the cube by rotating the focus-point by 90° around its local x or y axis. Would refactoring your camera setup like that be an option for you? \$\endgroup\$
    – Philipp
    Jul 13, 2023 at 12:40
  • \$\begingroup\$ Thanks, yes I can re-parent my camera. I'll give a try to this approach and let you know. I am struggling with this some days... \$\endgroup\$ Jul 13, 2023 at 13:23
  • \$\begingroup\$ Oh no, just a little problem I want to the camera be static at top when I tilt the cube... I have to rotate a bit the cube to move the ball and the camera has to stay at position in this mechanic \$\endgroup\$ Jul 13, 2023 at 13:26
  • \$\begingroup\$ But you give me an idea, maybe I can use the focus point... \$\endgroup\$ Jul 13, 2023 at 13:27
  • \$\begingroup\$ Yeah It's working now, Thanks! \$\endgroup\$ Jul 13, 2023 at 13:45

1 Answer 1

1
\$\begingroup\$

I just needed the approach that https://gamedev.stackexchange.com/users/21890/philipp said

A rig inside the cube and a focus point inside the rig. and the following code solve this:

private void ChangeFaceToDir(CardinalDirection dir)
{     
    Debug.Log("toDir" + dir);
    //cameraText.SetText(currentCameraFace.ToString());

    switch (dir)
    {
        case CardinalDirection.North:
            rigPoint.Rotate(Vector3.right, 90f,Space.Self);
            break;
        case CardinalDirection.South:
            rigPoint.Rotate(Vector3.right, -90f, Space.Self);
            break;
        case CardinalDirection.East:
            rigPoint.Rotate(Vector3.forward, -90f, Space.Self);

            break;
        case CardinalDirection.West:
            rigPoint.Rotate(Vector3.forward, 90f, Space.Self);

            break;
        default:
            break;
    }
    newPosition = focusPoint;

}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .