Need help replicating a camera design with Cinemachine

Hi!

I’ve discovered an issue with my current camera setup and I’d like to know how to approach this.
I’m trying to replicate a camera system from an old game (Crash Bandicoot from the original playstation to be more precise, for study purposes).

I’ve been using a Cinemachine Virtual Camera with Framing Transposer body. What I need:
-The camera is mostly behind the player, doesn’t really rotate on the z axis, and needs to follow him while on the ground and jumping so he’s always on the screen.
-I need to have proper dead-zone control (the deadzone has to be wider when the player needs to walk towards the camera)
-I need to control the distance between the player and the camera, somehow (the distance from the player has to increase when the player needs to walk towards the camera).
I think that’s all.

So, almost everything works perfectly with my current iteration, but the issue I have is related to tracking the player when he’s jumping.
See, right now, if I jump, since the framing transposer works by keeping the distance, the virtual camera moves as much vertically to keep the proper distance, and it looks kinda off and weird.
In the original game, when you jump, the camera follows the character with rotation (you jump, the camera rotates upwards without moving, and when you fall it rotates downwards to its original orientation).

  1. I’ve been experimenting, and by using a Transposer instead of a Framing Transposer, I’ve been able to replicate this behaviour perfectly. But the Transposer has no concept of a dead zone! So it’s failing on that.

  2. I tried removing the body, and using a Composer, and while you can control the rotation with the Screen, and the dead zone works perfectly, then I’d need to implement the entire following behaviour via script, and that sounds like a nightmare.

  3. If I add a Transposer and a Composer at the same time. The camera follows the player, you can control the rotation with the Composer’s Screen field, but the Dead Zone does not work for some reason (maybe because the transposer’s lack of Dead Zone knowledge overrides the Composer’s? I have no idea)

Well, I’ve tried my best.
How to approach this situation? Did I understand something wrong?

I can post any kind of code, video, and anything to illustrate my case if necessary.

Thanks in advance!

1 Like

Hi!

Could you show us videos of the exact behaviour you are expecting?

And a video showing what you have in the current iteration were everything works almost perfectly? A couple of screen shots of your setup could be helpful too :slight_smile:

I think you are close, and with a little tweaking you’ll get w

1 Like

Thanks!

Pictures regarding the configuration of my Virtual Camera:

Images

The Virtual Camera is not following the player exactly, but a gameobject called “CameraFollowTarget”.
It’s a little camera on top of the player’s head (you can perfectly see it on the video) that follows the player with a Smooth Damp and some custom logic, so I have an additional level of control over the camera movement.

The “CameraReposition3D” script changes the xDamping, dead zone width, distance, and tracked offset depending on where the player looks forward OR looking at the camera.
You can see all of this in the second video.

This video that I recorded to study, illustrates with some gameplay what I mean:
Video1

ab056s

-The camera distances itself from the player when he’s facing the camera
-When the player jumps the camera follows by rotation instead of repositioning
-There’s some amount of deadzone/softzone control (the player is not at all times at the center of the view!)

This video illustrates my current setup:
Video2

00a8h4

You can see in the scene how the camera “moves” vertically instead of staying in place and rotating, because it’s the nature of the Framing Transposer, I guess!

It reminds me that I forgot to mention another one of the variants I tried:
If I made it so that the little cam on top of the player’s head didn’t follow the player vertically while jumping.
Instead, I made it so the player jumping made the Virtual Camera rotate (emptied the “look at” field). What happened was so that, in order to keep the distance at all times with the follow, the camera rotated around it, instead of staying in place and just goddamn looking upwards. It makes sense, but argh, so frustating!

Oof, I’m just venting at this point.

(By the way my current implementation of the dead zone/softzone control sucks, it’s not replicating properly what’s shown in the gameplay, but it’s on my todo list and it doesn’t seem like it’ll give me this kind of trouble, so no biggie. Still: oops)

If I need to post something else, please let me know, and thanks!

1 Like

Have you tried Framing Transposer (Body) and Composer (Aim) together? That should produce the effect you are looking for:
6998732--826925--FT+C.gif

I don’t know if that works because (maybe I’m doing something wrong here) but having those two configured at the same time, the second I edit either the Tracked Offsets or the Composer’s Screen or anything about rotation really, the camera starts spinning like absolutely crazy nonstop and the game view goes complely nuts.

And I need to control those parameters to replicate the various effects.

I tried with a fresh new camera. Literally disabled all my vcams, and went Cinemachine > Create Virtual Camera > just selecting framing transposer + Composer, dragging my followTarget gameobject on both “follow” and “look at” slots and hit play. The second I edit any of those parameters I mentioned: boom, spining time.

Framing Transposer only controls the position of the camera, it does not control the orientation. Body is position, Aim is orientation of the vcam.

Since your Framing Transposer is very close to what you’d like to have, what you could do is to create a Cinemachine extension. This extension will look at the player when the player jumps.
Possibly you will want to restrict the look at axes to only affect one.

You could make a custom cinemachine extension and apply the rotation there. Take
CinemachineCameraOffset.cs as an example, and modify it.

Transform.LookAt - Transform lookat - Google Search

If you’d like, then you can DM me with your project and I will have a look. It is much easier to see what’s needed that way :slight_smile:

1 Like

It’s 1.30 am over here, so it’s getting pretty late, but tomorrow I’ll make a branch in my git after sorting a little bit the uncommited mess I have and I’ll post the link, I’m making all my code public.
I’ll also take a look at your information when I can.

Thanks!

Aight, I uploaded my current setup in this branch on my git repository.
[deprecated]

Alternatively here’s a mega link with the project (in case cloning goes badly or anything, I haven’t tried setting up a project from the repository myself).
[deprecated]

There’s a few commented lines of code here and there for experimentation, and I believe I have to update the documentation/naming of a few methods, but otherwise it should be on point.

I’ll take a look at what you posted yesterday when I can.

Thanks again for your time and help!

P.S: Probably those git branch and mega link will be dead when the problem is solved because the code will be obscenely deprecated in the future, so for future google-proofing if we make it to a solution I’ll make it/post it very clearly in here :slight_smile:

1 Like

Gitlab worked :slight_smile: I’ll have a look at the project.

1 Like

You have a couple custom scripts that do some special adjustment magic to change the vcam parameters from forward facing player and backwards facing player. In general, I would advise against this. Instead, you could set up two vcams, vcam player forward facing, and vcam player backward facing. Then based on the player orientation, you switch between these two vcams.
This will make the setup cleaner and easier to modify. :wink:

However, you are very close to what you’d like to have so I wrote two custom extensions to get what I think you’d like. I expect that you will need to tweak and add some functionality to them. Once you add these to your project, you can add them to your vcam through the Add Extension interface on your vcam.

The first one forces the camera height to a set value:

using UnityEngine;
using Cinemachine;

/// <summary>
/// An add-on module for Cinemachine Virtual Camera that forces the camera height to be a specific value.
/// </summary>
[AddComponentMenu("")] // Hide in menu
[ExecuteAlways]
public class CMLockHeight : CinemachineExtension
{
    [Tooltip("Locks camera position on the Y axis")]
    public bool LockHeight = true;

    [Tooltip("Height to lock")]
    public float Height;
 
    /// <summary>
    /// Locks the camera height.
    /// </summary>
    /// <param name="vcam">The virtual camera being processed</param>
    /// <param name="stage">The current pipeline stage</param>
    /// <param name="state">The current virtual camera state</param>
    /// <param name="deltaTime">The current applicable deltaTime</param>
    protected override void PostPipelineStageCallback(
        CinemachineVirtualCameraBase vcam,
        CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
    {
        if (stage == CinemachineCore.Stage.Finalize)
        {
            if (LockHeight)
            {
                var position = state.RawPosition;
                position.y = Height;
                state.RawPosition = position;
            }
        }
    }
}

The second one looks at the transform you provide, but only along the X axis. (If you set the LookAt target on your vcam, then you could get that target from state → state.HasLookAt, state.ReferenceLookAt):

using UnityEngine;
using Cinemachine;

/// <summary>
/// An add-on module for Cinemachine Virtual Camera that looks at the target along the X axis only.
/// </summary>
[AddComponentMenu("")] // Hide in menu
[ExecuteAlways]
public class CMLookAt : CinemachineExtension
{
    [Tooltip("Transform to look at")]
    public Transform LookAt;
 
    /// <summary>
    /// Looks at the player along the x axis.
    /// </summary>
    /// <param name="vcam">The virtual camera being processed</param>
    /// <param name="stage">The current pipeline stage</param>
    /// <param name="state">The current virtual camera state</param>
    /// <param name="deltaTime">The current applicable deltaTime</param>
    protected override void PostPipelineStageCallback(
        CinemachineVirtualCameraBase vcam,
        CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
    {
        if (stage == CinemachineCore.Stage.Body)
        {
            var diff = LookAt.transform.position - vcam.transform.position;
            var euler = Quaternion.LookRotation(diff, Vector3.up).eulerAngles;
            euler.y = 0;
            euler.z = 0;
            state.RawOrientation = Quaternion.Euler(euler);
        }
    }
}
2 Likes

You’re absolutely right and I should’ve done that. I’m spending some time refactoring my code, that’s a little bit of a mess now that you’ve said it.

Regarding this, You’re not going to believe me.
I’m really happy that you posted a solution but I -literally- cranked up Unity an hour ago and arrived at a different solution the exact moment you made your post, which honestly feels kinda bad because I’m incredibly thankful you spent some time crafting a fix for me but I still arrived at a different on my part, even if it’s a the same time.

What I did in the end, was the following:

  • Used a Framing Transposer (I was already using) + Cinemachine Recomposer extension (that already exists). I have to do this because the Cinemachine documentation specifically states that Framing Transposer does not care about the Look At target (so the Composer is impossible to use for me).
  • My Camera Follow Target (the small camera) stays at the same height at all times instead of moving vertically with the player (that way the Virtual Camera doesn’t “jump” with it since it’s constrained to keep the distance at all times), and when the character is at a different height than ground level, the Camera Follow Target tilts vertically (at ground level, the angle is always 0, it tilts upwards when I jump, tilts downwards when I go below ground level)
  • My Virtual Camera contains a reference to the Camera Follow Target (makes sense), so I get the tilt angle and apply it to the Cinemachine Recomposer Tilt property

Voilà! It works like magic.
I’m posting a video comparison here.

Comparison j334x8

With this I can keep a constant distance with my player, and still tilt the camera instead of making it follow the height at all times.
You’ve seen it’s a kinda complicated setup but it’s the simulation I was looking for.
Now it’s all a matter of simplifying the camera setup to what you said (for forward and backwards camera), and tweaking the parameters.
Now this made me really happy.

Thank you for your hard work and all your time, I really appreciate it!

1 Like

Nice, I forgot about that :slight_smile:

Well done :wink:

1 Like