Search Unity

Framing Transposer and control over the screen position of multiple targets

Discussion in 'Cinemachine' started by taylank, Sep 21, 2018.

  1. taylank

    taylank

    Joined:
    Nov 3, 2012
    Posts:
    182
    I'm trying to use the framing transposer for a 2d map traversal scene. I have a bunch of territories on the map and I want to display the path the player would take to their chosen territory. So I have set up a vcam with a framing transposer and a target group, where I set the targets dynamically from the waypoints on the path.

    Here's the catch though: I want to reserve the left side of the screen for a UI panel, and I don't want this panel to overlap with the screen position of any of the waypoints. I tried using the deadzone/soft zone settings to indicate that. Problem is, when framing the waypoints, the framing transposer only takes into account a singular average or center position that it gets from the target group. It does not seem concerned with fitting the bounding box into the deadzone area, which is what I need it to do.

    Obviously it's already doing similar math to fit the bounding box into the whole screen area, I just want to be able to specify the safe screen area for that. Any idea how to achieve this?
     
    Last edited: Sep 24, 2018
  2. taylank

    taylank

    Joined:
    Nov 3, 2012
    Posts:
    182
    Looks like I'm gonna have to answer my own question. In case anyone else needs the same trick, here is how I achieved the result I wanted:
    - First off, I created a copy of CinemachineFramingTransposer and renamed it to BoundsFramingTransposer. Did the same for its editor counterpart. The changes I made could have easily been added to the standard issue FramingTransposer, but I didn't want them overwritten with updates to CM. Hence the copying and renaming.
    - Second, you will want to make sure that when the camera distance is adjusted, it takes into account the available deadzone space. So the GetTargetHeight(Bounds b) function becomes this:
    Code (CSharp):
    1. float GetTargetHeight(Bounds b)
    2.         {          
    3.             float framingSize = Mathf.Max(Epsilon, m_GroupFramingSize);          
    4.             switch (m_GroupFramingMode)
    5.             {
    6.                 case FramingMode.Horizontal:
    7.                     return b.size.x / (framingSize * VcamState.Lens.Aspect * (m_SoftZoneWidth + Epsilon));
    8.                 case FramingMode.Vertical:
    9.                     return b.size.y / (framingSize * (m_SoftZoneHeight + Epsilon));
    10.                 default:
    11.                 case FramingMode.HorizontalAndVertical:
    12.                     return Mathf.Max(
    13.                         b.size.x / (framingSize * VcamState.Lens.Aspect * (m_SoftZoneWidth + Epsilon)),
    14.                         b.size.y / (framingSize * (m_SoftZoneHeight + Epsilon)));
    15.             }
    16.         }
    - Then the movement along xy plane needs to be adjusted based on the target bounds minmax, rather than a single point like in FramingTransposer. So I modified the OrthoOffsetToScreenBounds function as such:
    Code (CSharp):
    1. private Vector3 OrthoOffsetToScreenBounds(CinemachineTargetGroup group, CameraState curState, Rect screenRect)
    2.         {
    3.             Bounds targetBounds2D;
    4.             Bounds bounds = group.BoundingBox;
    5.             Vector3 fwd = curState.RawOrientation * Vector3.forward;
    6.             var bMatrix = Matrix4x4.TRS(
    7.                     curState.RawPosition,
    8.                     curState.RawOrientation, Vector3.one);
    9.             targetBounds2D = group.GetViewSpaceBoundingBox(bMatrix);
    10.            
    11.             if (screenRect.size == Vector2.zero)
    12.             {
    13.                 return (Vector2) targetBounds2D.center - screenRect.center;
    14.             }
    15.  
    16.             // Bring it to the edge of screenRect, if outside.  Leave it alone if inside.
    17.             Vector3 delta = Vector3.zero;
    18.             if (targetBounds2D.min.x < screenRect.xMin)
    19.                 delta.x += targetBounds2D.min.x - screenRect.xMin;
    20.             if (targetBounds2D.max.x > screenRect.xMax)
    21.                 delta.x += targetBounds2D.max.x - screenRect.xMax;
    22.             if (targetBounds2D.min.y < screenRect.yMin)
    23.                 delta.y += targetBounds2D.min.y - screenRect.yMin;
    24.             if (targetBounds2D.max.y > screenRect.yMax)
    25.                 delta.y += targetBounds2D.max.y - screenRect.yMax;
    26.             return delta;
    27.         }
    - As a final touch I ditched the hard vs soft damping for a uniform damping to the camera offset, mostly because I couldn't figure out how to get rid of the jitter that would otherwise be present.
    Code (CSharp):
    1. cameraOffset = Damper.Damp(
    2.                     cameraOffset, new Vector3(m_XDamping, m_YDamping, m_ZDamping), deltaTime);
    I've attached the files in case anyone else finds it useful.
     

    Attached Files:

  3. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,720
    Hey @taylank well done!
    Sorry no one got to this before you did it yourself, but thanks for sharing.
     
  4. taylank

    taylank

    Joined:
    Nov 3, 2012
    Posts:
    182
    Sorry for the necro but my hack for bounding box based framing no longer works after switching to Cinemachine's PackageManager version, due to certain classes being inaccessible due to protection level.

    Are there any plans to actually add this as a feature? As I mentioned in the original post, the existing group framing transposer is unable to guarantee a specified bounding box fitting into a predetermined screen area. Or are there plans to make the system extensible so one can code new transposers and alike?
     
  5. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,720
    Yes, some classes got aggressively moved to internal. Maybe too aggressively. Which ones do you need back?
     
  6. taylank

    taylank

    Joined:
    Nov 3, 2012
    Posts:
    182
    PositionPredictor, for one.
    I had also modified CinemachineTargetGroup to gather bounding box information, and I think that's also out of my reach now.
     
  7. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,720
    PositionPredictor has been made public in CM 2.2.8 (to be released very soon), and there is also some bounding box stuff in TargetGroup. If you need TargetGroup to do more, you can always subclass it and add extra stuff.
     
    Last edited: Dec 7, 2018