-1
\$\begingroup\$

I'm trying to rebuild the camera movement shown in this video.

For demonstration purposes, I have changed the FoV to a higher value in this video as this allows a larger view.

The player's Y input rotates the chest.

The camera is automatically positioned and rotated according to the chest rotation.

However, at a certain Y-position / height, the camera seems to switch from orbit behaviour to a simple up-down movement. At least that is what it looks like for me when I watch the video.

I would like to recreate this by changing my camera script attached below, but I don't know how because the position seems coupled with the rotation.

Thank you.

 using UnityEngine;
 using System.Collections;

 [AddComponentMenu("Camera-Control/Mouse Orbit with zoom")]
 public class MouseOrbitImproved : MonoBehaviour
 {

public Transform target;
public float distance = 5.0f;
public float xSpeed = 120.0f;
public float ySpeed = 120.0f;

public float yMinLimit = -20f;
public float yMaxLimit = 80f;

public float distanceMin = .5f;
public float distanceMax = 15f;

private Rigidbody rigidbody;

float x = 0.0f;
float y = 0.0f;

// Use this for initialization
void Start()
{
    Vector3 angles = transform.eulerAngles;
    x = angles.y;
    y = angles.x;

    rigidbody = GetComponent<Rigidbody>();

    // Make the rigid body not change rotation
    if (rigidbody != null)
    {
        rigidbody.freezeRotation = true;
    }
}

void LateUpdate()
{
    if (target)
    {
        x += Input.GetAxis("Mouse X") * xSpeed * distance * 0.02f;
        y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;

        y = ClampAngle(y, yMinLimit, yMaxLimit);

        Quaternion rotation = Quaternion.Euler(y, x, 0);

        distance = Mathf.Clamp(distance - Input.GetAxis("Mouse ScrollWheel") * 5, distanceMin, distanceMax);

        RaycastHit hit;
        if (Physics.Linecast(target.position, transform.position, out hit))
        {
            distance -= hit.distance;
        }

        Vector3 negDistance = new Vector3(0.0f, 0.0f, -distance);
        Vector3 position = rotation * negDistance + target.position;

        transform.rotation = rotation;
        transform.position = position;
    }
}

public static float ClampAngle(float angle, float min, float max)
{
    if (angle < -360F)
        angle += 360F;
    if (angle > 360F)
        angle -= 360F;
    return Mathf.Clamp(angle, min, max);
}
}
\$\endgroup\$
  • \$\begingroup\$ What did you try (show us the code), and exactly how do your results differ from what you want? \$\endgroup\$ – DMGregory Mar 15 at 12:00
  • \$\begingroup\$ @DMGregory I believe (not sure however if that's correct) that I saw how the camera behaves, and I have tried to put that in code, but when it comes to algebra, I tend to get lost. \$\endgroup\$ – tmighty Mar 15 at 23:47
2
\$\begingroup\$

I can't speak to exactly the way the Resident Evil camera was implemented.

What I can say is that it's common practice to use curves to control camera movement. So for instance, you could use a curve to control the blend between two behaviour styles, parametrized by the camera's pitch. Something like this:

public AnimationCurve orbitCylinderTransition;
public float cylinderRatio = 0.8f;

Vector3 GetCameraOffset(float pitch, float yaw, float nominalDistance) {
    Vector3 direction = Quaternion.Euler(pitch, yaw, 0) * Vector3.back;

    Vector3 planar = direction;
    planar.y = 0;
    float horizontalLength = planar.magnitude;

    Vector3 onSphere = nominalDistance * direction;

    Vector3 onCylinder = onSphere; 
    if(horizontalLength > 0f) 
       onCylinder *= cylinderRatio / horizontalLength;

    float blend = orbitCylinderTransition.Evaluate(pitch);

    return Vector3.Lerp(onSphere, onCylinder, blend);
}

This computes two possible positions for the camera: one on a sphere around the orbit point, and one on a vertical cylinder, both at the desired angle relative to the orbit point.

Next, it uses the pitch variable to select a sample point on a designer-editable curve to get a blend weight between 0 and 1, with zero being purely spherical orbit behaviour, and one being purely cylinder behaviour. We can control the radius of the cylinder relative to the sphere's using the ratio variable - here I chose something a little under 1, since it looks like as we lower the camera, it swings inward a little bit before starting to drop straight down.

By shaping this curve in the editor, you can precisely control where you want this transition to occur, and how you want to ease in & out of it. This makes it easy to tune for feel, without needing to reverse-engineer a specific math expression to capture the exact transition rule you want.

You might find the sphere / cylinder blend is not quite right for the feel you want, and instead you want to use a curve to control the ratio o nominal distance to final distance by camera pitch instead. Or maybe you want to stack multiple curves. Once you expose this in the editor you can much more freely tweak & tune, whatever model you choose.

\$\endgroup\$
  • \$\begingroup\$ That is really interesting. Thank you. Can you tell me how to mix this CameraOffset with the Orbit Camera script? \$\endgroup\$ – tmighty Mar 16 at 10:50
  • \$\begingroup\$ Any way you want to or find useful. What I wanted to show you here was the option of using a curve, and sampling from it using your camera inputs, not a specific code implementation. \$\endgroup\$ – DMGregory Mar 16 at 13:23
  • 1
    \$\begingroup\$ It looks like you can apply this as a scaling factor on your distance variable. currentDistance = distance * someCurveBasedMultiplierOfYourChoosing; then use currentDistance for further positioning logic this frame. \$\endgroup\$ – DMGregory Mar 16 at 15:24

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.