issue with framing transposer during blends

Hey so I have a basic 2 camera setup in my 2D game, with a “close” camera and a “far” camera, which are basically copies of each other except the far one has double the orthographic size. They both have a framing transposer set to track the player.

So during the game I check how close a player is to the nearest wall. If the player is close to a wall, I bump up the priority on the close cam, and if they are far from any walls I bump it up on the far cam.

It works pretty good most of the time but there are occasions where players can get off the screen, so it’s kind of a critical failure for the camera when that happens.

It took me awhile to figure it out, but it seems to be a problem during mid-blends with fast movement.

I replicated an exaggerated example in this github project:

The framing tranposer settings are extremely tight in the example just to try to keep the player right in the middle:

Switching the cameras faster then the transition time, together with fast movements can cause the player to go off the screen:
3316967--257921--giphy.gif

I guess this is probably somewhat expected behavior given how mid-blends seem to be implemented, but, what else can I do to get the behavior I want?

I considered just going to a single cam and then manually lerping the orthographic size on it, but I’ve used slightly different framing configurations on the far/close cam as well as slightly different confiner polygons for the different zooms, so I’d hate to lose the other utility of having 2 completely separate vcams for close and far.

You said you have two cameras, and both are perfectly fixed on the player.
But if that’s the case, I fail to understand how the player can ever not be perfectly in the center of the sceen then.

No matter how you blend or what cameras are participating, the player should always be in the center of the sceen (as long as all cameras show the player in the center obvioulsy)

There must be some other bug elsewhere, unless I misunderstood how you have setup your scene.
Can you explain it again please?

Have you tried testing each individual camera to see if the player ever goes out of the visible area?

That’s basically it. Basically when it transitions from mid blend then it seems to freeze the position of the camera and blend from there rather than continuing to use the framing transposer.

You can try the project yourself on github.

Well then it definitely sounds like a bug.

Try this:
There is a virtual camera called something like “mixing camera” or so.
It takes its children (all vcams) and “mixes” them.
It has some sliders / values for each camera to know how much each camera should contribute to the final mix.
Try this one and do the interpolation that way instead of relying on the way its supposed to be done for now (aka how you’re doing it at the moment); just for testing.

I cant open the project currently but I can take a look in a few days.

Hmm yeah, I haven’t had time to test it out but thinking about it I think using the mixing camera is going to be the way to go, as it should just give me the straight blend between the 2 cameras without introducing this mid-blend/snapshot idea in the regular system. I will test it out soon and report back.

Also make sure you have the latest version.
I vaguely remember someone else talking about a similar problem.

That whole “snapshot” thing is almost certainly a bug, but maybe it was fixed in the newest version already

Thanks for helping out @dadude123
@jdeuce - are you still getting this issue after running the latest from the Asset Store?

Yeah the test repo was made with what I believe is latest copy from the store.

I tried downloading cinemachine again from the store but it didn’t make a difference.

I updated the demo with a background to make it easier to see in the gifs. The demos just have the player going up and down in a loop with an animator controller, and then it just uses a simple camera controller script to flip between the close / far cam depending on how close the white player dot is to the red wall.

So in “normal” mode the Camera controller script looks like this:

void Update () {
        Distance = Vector2.Distance(player.transform.position, wall.position);

        if (Distance > FarThreshold) {
            farCam.m_Priority = activePriority;
            closeCam.m_Priority = inactivePriority;
        } else {
            closeCam.m_Priority = activePriority;
            farCam.m_Priority = inactivePriority;
        }
    }
}

and generates this result… it’s fine for the first loop but the second time it triggers a mid-blend and then lets the player go off the screen:

3319789--258273--cinemachine-normal.gif

Using a mixing camera controller with a script like this:

    void Update () {

        Distance = Vector2.Distance(player.transform.position, wall.position);

        float rate = Time.deltaTime / transitionTime;

        if (Distance > FarThreshold) {
            mixingCamera.m_Weight0 = Mathf.Clamp01(mixingCamera.m_Weight0 - rate);
            mixingCamera.m_Weight1 = Mathf.Clamp01(mixingCamera.m_Weight1 + rate);
        } else {
            mixingCamera.m_Weight1 = Mathf.Clamp01(mixingCamera.m_Weight1 - rate);
            mixingCamera.m_Weight0 = Mathf.Clamp01(mixingCamera.m_Weight0 + rate);
        }
    }

Generates this result, which does seem to be the correct behavior for this test case:

3319789--258274--cinemachine-mixing2.gif

Both test cases are on the github link.

I’ll download the thing and take a look.

edit 1:
In the mixing camera scene the first thing i noticed is that the far threshold is really small and the time is extremely high (10s).

I changed the distance threshold to 10 and the time to 0.5s and it looks fine to me.
In any case I cant really see anything wrong with it in the mixing scene.
I’ll check out the other one now…

edit 2:
The normal scene definitely has some bug.
I will investigate.

edit 3:
When you turn the blend time to something lower it works fine.
The problem appears when another blend gets triggered while the old one is not yet done.

  1. Far => Close (starts working fine)
  2. While (1) is still in progress, “Far” gets triggered
  3. This causes a new blend to start “MidBlend(Far=>Close) => Far”

The solution is straightforward (from a logical perspective at least)
What could happen: Cinemachine keeps the old blend around, evaluates it, and uses it as input for the new blend. Blend-ception style haha.

However I imagine this getting pretty complicated in code. And by pretty complicated I mean making sure this stuff doesn’t generate GC pressure.

edit 4:
Yep, this scenario seems to be best solved using the mixing camera.
Having cinemachine do “cascading blends” would be nice though.

I have an idea how it can be solved perfectly though:

It could be solved by having each blend as a struct.
And then creating something like a “List”.
Where each BlendState has stuff like time, timeRemaining, progress, … and course 2 sources (A and B).

And both A and B can be either a virtual camera (like a root source), or an index into that same list! (so blending between another blend that is still ongoing…)

Things to consider:

  • every time something gets added or removed in the list, all indices of the containing elements have to be updated

  • dealing with a setup like that with a pure List<> will be a huge fucking mess in no time haha.
    But creating a custom container “CinemachineBlendContainer” or something like that, would solve it nicely.
    You could add more blending by doing “BlendTowards(vCam, time)” and to know where to blend from, it would automatically refer to the last entry in its internal list.
    The container would always refer to the currently active virtual camera as a root source to start a blend with BlendTowards().

  • negative: would probably take an hour or two to implement

  • positive: arbitrary blend cascades, one less corner case that users have to think about.

  • the scenario @jdeuce is dealing with should be supported out of the box, so maybe it is worth taking the time.

edit 5:

I was overtinking it a little bit.
You don’t even need to index into the list at all.
All you’d have is a stack of BlendStates.
First one in the stack blends from a vcam to another vcam.
And every BlendState on top, just blends from the previous blendstate (its result), to another vcam. Every frame cinemachine would simply traverse the list downwards, interpolating through and through. No indexing tricks needed. As long as the hypothetical “BlendState” I’m suggesting is a struct everything should be fine (as in not garbage collection pressure).

I tuned the numbers to make the bug trigger everytime. In the real game I was use a long transition time to zoom out and a short one to zoom in, as well as more realistic distance. Most of the time it works pretty good. It maybe only shows up in the real style game every 10 play sessions if the right combination of things happens. But if I switch to mixing camera I think it will work 100%.

Edit: Appreciate you taking a look, what you’re finding so far seems to agree with what I’ve found.

Thanks to both of you @jdeuce and @dadude123 for your efforts on this.
Indeed CM does not support cascading blends, and that is exactly the issue here. We’ll add it to our feature request list.

Thanks @Gregoryl , I should be good using Mixing camera.

Also might be worthwhile to note that back and forth blending between 2 cameras is a lot simpler than arbitrary cascading blends, and so it might be an easier feature for you to add in the meantime. Like if you just special case the mid blend when it goes back to the same camera. I think this is probably the most common reason people would run into trouble with the way the blends are now.

2 Likes

@dadude123 @jdeuce FYI we’ve added support for cascading blends. It will be in the next CM release. Thanks again for bringing this up.

1 Like

Nice! I didn’t expect that.
Can’t wait for the new version. Any idea when it will be released and what it will include?

Btw: Is there a way to record the rotation of a dolly path as well and then apply it to the camera?
Also, a hotkey to add a new point to the currently selected dolly track and instantly set it to the current scene view (position and rotation).
Or is something similar in the pipeline?

No ETA on that at the moment.

Not sure I understand what you’re looking for in the first question. If you mean having the dolly camera move along the path and take both its position and orientation from the path, then the way to do it is to have no LookAt target on the camera, and set the “Camera Up” (which is really the camera orientation) to “Path”:

3346215--261518--upload_2018-1-8_10-56-46.png

For the second question, there is something similar already. Kind of the reverse of what you’ve suggested.

3346215--261519--upload_2018-1-8_10-59-21.png

Clicking on the waypoint number (blue arrow) will set the scene view to look at the waypoint. Clicking on the icon (red arrow) will reposition the waypoint to match the scene view camera position.

Sorry for not being clear :slight_smile:
At the moment waypoints only have a position.
But I want it to also have a rotation; just like a normal transform (except the scale of course).
Roll alone in addition is not enough.

So the path has a full quaternion at every point, so you can make the camera look at different locations while moving along the track.

Otherwise that is really really cumbersome to do and generally much worse (you’d have to make tons of virtual cameras and then blend between them, which is just an awful solution)

Imagine this scenario: I want to make an intro sequence that shows of various locations in a map. With the dolly camera as it is right now your camera can either only look forwards on the track. Or optionally you can make it look at some fixed points (or blend between them). But there is no pure dolly-track-only solution which would make this so much easier.

I hope that makes sense, let me know if something is unclear.

Yes, I was aware of that, but a hotkey to instantly add a new point and “click” that button you listed here (and even better if the new waypoints would also have a rotation and also set that :smile:)

Could that be added to cinemachine?

Cinemachine would be perfect to make these “fly through sequences”, but having to setup a explicit look target for each waypoint is way too much work.

Ah, I understand now.

For your intro scene maybe the simplest solution is just to animate the transform of the camera in the old-fashioned way, by recording an animation track in Timeline. You can still make it a vcam - just put “Do Nothing” in Aim and Body.

We have no immediate plans to add quaternions to the path points. However, it’s pretty easy to implement your own path - just derive a new behaviour from CinemachinePathBase. So long as it inherits from that, you can use it in TrackedDolly.

I’ll add the hotkey idea to our feature-request list.