0
\$\begingroup\$

I would like my camera to inherit the player in the hierarchy, and get closer when there is a wall or floor between the player and the camera.

Right now I am trying to raycast from the player to the desired position, and set the camera as close as possible to that desired position. But my camera is flicking all over the place.

Both of these scripts are attached to the player's camera, which inherits from the player.

.. RotateCamera adjusts x rotation of the camera with mouse movement, and sends the desired y-position to the FollowCamera script (y-position should shift to keep player in view). The way this is working is a little hacky, but I'd really like to first focus on the raycast situation before improving this code.

using UnityEngine;

public class RotateCamera : MonoBehaviour
{
    [SerializeField] private float rotSpeed, downAngleLimit;
    [Header("Up angle limit should be negative number")]
    [SerializeField] private float upAngleLimit;
    [SerializeField] private FollowCamera followCamera;
    
    private Vector3 oglocpos;
    private Transform tx;
    
    void Start()
    {
        tx = transform;
        oglocpos = tx.localPosition;
        upAngleLimit += 360;
    }

    void Update()
    {
        GetInput();
    }

    private void LateUpdate()
    {
        ClampAngle();
    }

    void GetInput()
    {
        float mouseY = Input.GetAxis("Mouse Y");
        Rotate(mouseY);
    }

    void Rotate(float degrees)
    {  
        // Just takes care of up/down rotation
        tx.Rotate(Vector3.left * (degrees * rotSpeed * Time.deltaTime));
        ShiftCamera(tx.localEulerAngles.x);
    }

    void ShiftCamera(float degrees)
    {
        // Determines desired y-position by linear equation with current x-angle as variable
        var pos = oglocpos;
        
        if (degrees < upAngleLimit)
        {
            pos.y = .05f * degrees; // f(x) = .05x
        }
        else if (degrees > upAngleLimit)
        {
            pos.y = (.05f * degrees - 18); // f(x) = .05x - 18
        }

        // send this desired position (in local coordinates) to the raycasting script
        followCamera.desiredPosition = pos;
        
    }
    
    void ClampAngle()
    {
        Vector3 eul = Vector3.zero;

        var loceul = tx.localEulerAngles;
        eul.x = loceul.x;
        if (eul.x > upAngleLimit)
        {
            eul.x = Mathf.Clamp(eul.x, upAngleLimit, 360);
        }
        else if (eul.x < downAngleLimit)
        {
            eul.x = Mathf.Clamp(eul.x, 0, downAngleLimit);
        }

        tx.localEulerAngles = eul;
    }
}

.. FollowCamera does the raycasting and actual camera adjustment. I'm sure the issue is in this second script, but I don't know how else to approach it.

using UnityEngine;

public class FollowCamera : MonoBehaviour
{
    public Vector3 desiredPosition;
    [SerializeField] private Transform ptx; // player transform
    [SerializeField] private LayerMask layerMask;

    void Update()
    {
        // Send raycast from player to camera's desired position (in world space)
        if (Physics.Raycast(ptx.position, transform.TransformPoint(desiredPosition), out var hit, 6f, layerMask))
        {
            // turn that into a local coordinate and send the camera there
            var lochitpos = transform.InverseTransformPoint(hit.point);
            transform.localPosition = lochitpos;
        }
        else
        {
            transform.localPosition = desiredPosition;
        }
    }
}
\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

If you're reading this and need help, I've decided to just use Cinemachine.. the effort for a manual camera wasn't worth it.

I recommend you take this angular logic from Brackeys, but use a Rigidbody and set its velocity manually based on that given direction. That gives you non-jittery movement and less hassle.

This is also a good video for Cinemachine that helped me

My code looks like:

void MoveAndRotate()
{

    // Change target angle if moving
    if (moveState != MoveState.Still)
    {
        // Translate the input to an angle in degrees
        // Add camera rotation for camera based movement
        targetAngle = Mathf.Atan2(inpDir.x, inpDir.z) 
            * Mathf.Rad2Deg + ctx.eulerAngles.y;
        movDir = (Quaternion.Euler(0, targetAngle, 0) * Vector3.forward);
    }
    else
    {
        movDir = Vector3.zero;
    }
    
    // Smooth out the angle for transform rotation
    realAngle = Mathf.SmoothDampAngle(transform.eulerAngles.y, 
        targetAngle, ref turnVelocity, turnSmoothTime);
    // Set the transforms Y-Rotation to the smooth angle
    transform.rotation = Quaternion.Euler(0, realAngle, 0);

    // Set Rigidbody velocity by camera direction
    movDir = (movDir.normalized * realSpeed);
    movDir.y = rb.velocity.y;
    rb.velocity = movDir;
}
\$\endgroup\$

You must log in to answer this question.

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