0
\$\begingroup\$

The script is attached to the Main Camera.

At the top :

[Header("Camera Orbit")]
public bool orbitCamera = false;
public float rotationSpeed;
public Vector3 offset;

Then in the Start()

private void Start()
{
    offset = new Vector3(player.position.x, player.position.y, player.position.z);
}

And in the Update()

private void Update()
{
    if (orbitCamera)
    {
        transform.eulerAngles += rotationSpeed * new Vector3(-Input.GetAxis("Mouse Y"), Input.GetAxis("Mouse X"), 0);
 
        offset = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * rotationSpeed, Vector3.up) * offset;
            transform.position = player.position + offset;
    }
 }

The problem is the offset or something e else position the camera too far in a big radius from the player. I can rotate the camera around the player with the mouse and also move the camera up down left right all around with the mouse the something with the part that rotates the camera around the player make the camera position to be very far from the player.

How can I make that it will not change the distance between the camera and the player? Not that the camera will be on the player but if I start the game and the distance between the camera and the player is 4 then keep this distance and use the mouse to rotate around the player at a 4 distance radius.

Now the distance radius is very very far.

This is a screenshot showing the camera and the player distance when running the game :

The camera distance from player when running the game

but then when it's getting to the offset part in the Update this part :

offset = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * rotationSpeed, Vector3.up) * offset;
transform.position = player.position + offset;

Then this happens :

The camera is too far from the player the player is marked with red circle

I marked with red circle the player to show how fat the camera is from the player.

What I'm trying to do the main goal is to be able to use the mouse to rotate the camera up down left right and also to rotate the camera around the player.

Technically it's working but I don't want the camera to be too far from the player with the offset part I want to keep the distance from the camera as it is before running the game.

The full script :

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

public class CameraController : MonoBehaviour
{
    [Header("Camera Transitions")]
    public bool startTransitions = false;
    public bool waitBeforeStart = false;
    public float transitionTimeToWait;
    public Transform transitionTarget;
    public float movementSpeed;

    [Header("Follow/Look AT")]
    public Transform follow;
    public Transform lookAt;

    [Header("Camera Orbit")]
    public bool orbitCamera = false;
    public float rotationSpeed;

    [Header("Player")]
    public Transform player;
    public Vector3 offset;

    private void Start()
    {
        offset = new Vector3(player.position.x, player.position.y, player.position.z);

        if (waitBeforeStart)
        {
            startTransitions = false;

            StartCoroutine(TimeToStartTransition());
        }
    }

    private void Update()
    {
        if (orbitCamera)
        {
            transform.eulerAngles += rotationSpeed * new Vector3(-Input.GetAxis("Mouse Y"), Input.GetAxis("Mouse X"), 0);
            
            offset = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * rotationSpeed, Vector3.up) * offset;
            transform.position = player.position + offset;
        }

        if (startTransitions && transitionTarget != null)
        {
            transform.position = Vector3.MoveTowards(transform.position, transitionTarget.position, movementSpeed * Time.deltaTime);

            if (transform.position == transitionTarget.position)
            {
                transform.gameObject.SetActive(false);
                transitionTarget.gameObject.SetActive(true);
            }
        }
    }

    IEnumerator TimeToStartTransition()
    {
        yield return new WaitForSeconds(transitionTimeToWait);

        startTransitions = true;
    }
}
\$\endgroup\$
3
  • 1
    \$\begingroup\$ I recommend adding transform.eulerAngles += to your list of "code to never write again". Accumulating Euler angles with a round-trip conversion to a quaternion and back between each increment will almost certainly not behave the way you expect. This has caused trouble for other devs in the past that we've had to clear up here. \$\endgroup\$
    – DMGregory
    May 31, 2021 at 10:34
  • \$\begingroup\$ @DMGregory so to use transform.eulerAngles += also for the second part of the mouse rotation instead of using Quaternion.AngleAxis like I'm doing now? or one transform.eulerAngles += for everything ? \$\endgroup\$ May 31, 2021 at 14:32
  • 1
    \$\begingroup\$ I am saying never use transform.eulerAngles += at all. Not twice, not once. Never. \$\endgroup\$
    – DMGregory
    May 31, 2021 at 14:41

1 Answer 1

0
\$\begingroup\$

Yeah, this line

offset = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * rotationSpeed, Vector3.up) * offset;

is the problem. And, honestly, I don't understand what you expected it to do and why you used it, because to me the line just makes no sense in this use case.

I'll give 2 approaches I would use for the 3rd person camera:

1st approach:
Adjusting the offset depending on the current camera position and rotation. In this approach you need to use the player.position as the base point, and then add some offset using the camera transform's directional vectors : transform.forward, transform.right.
For example, to get a point that is 4 units behind the camera, i. e. 4 units back along the current camera forward direction you do this:

Vector3 point = transform.forward * -4f;

To get a point that is 4 units back and 2 units up relative to the current camera position and orientation and also take into account the player's position:

Vector3 point = player.position + transform.forward * -4f + transform.up * 2f;

Or, using the offset vector:

Vector3 offset = new Vector3(0, 2, -4);

void Update()
{
    Vector3 point = player.position + transform.forward * offset.z + transform.up * offset.y;
    transform.position = point;
}

2nd approch:
Construct the camera object from 2 game objects and let Unity do the translations automatically. In this method you create a root empty game object and attach a new object to it as a child. The camera component should be on the child. Then you set the offset for the child that will represent the local position of the child relative to the root (parent). You then move and rotate the root object to follow the player, rotate with mouse, and the child object will keep standing at the predefined offset from the root object no matter what. The camera controller script could be on any of the objects, you just need to provide references to the root and child game objects. For example:

/*
| Root game object -> this script
    ^
    | Child game object -> Camera component
*/

Transform child;
Vector3 offset = new Vector3(0, 2, 4);

void Start()
{
    // Assuming the script is attached to the root object and the child object name is "Child"
    child = transform.Find("Child");
    child.localPosition = offset;
}

void Update()
{
    // Make the root object follow the player:
    transform.position = player.position;
    // Do your camera rotation code.
    // The child holding the Camera will stay at (0, 2, 4) offset from the root object relative to the camera current orientation automagically.
}
\$\endgroup\$

You must log in to answer this question.

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