Search Unity

  1. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  2. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  3. Want more efficiency in your development work? Sign up to receive weekly tech and creative know-how from Unity experts.
    Dismiss Notice
  4. Participate with students all over the world and build projects to teach people. Join now!
    Dismiss Notice
  5. Build games and experiences that can load instantly and without install. Explore the Project Tiny Preview today!
    Dismiss Notice
  6. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  7. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

Cinemachine Metroidvania/Megaman Style Camera

Discussion in 'Cinemachine' started by JonnyRage, Jun 14, 2018.

  1. JonnyRage

    JonnyRage

    Joined:
    May 30, 2018
    Posts:
    4
    I'm new to Unity and Cinemachine, so forgive me if this is a stupid question or has been answered before. I am trying to create a camera system for a 2d platformer using cinemachine that is similar to Mega Man, Shovel Knight, or Celeste where the camera kind of snaps to the new area/level once the player crosses a certain boundary and there is even a little bit of slow down during this transition. I also want to be able to have irregular sized rooms such as narrow corridors you might fall down or traverse or odd shaped rooms. I tried creating multiple vCams to handle this. For example in my prototype I have one large horizontal room and then a drop off down a narrow hallway. I created two separate polygon collider 2d components for each room and marked them as triggers and also 2 vCams. Let's call the objects that have the colliders "Starting Room" and "Big Drop". I set these as the Bounding shapes on each of my cameras named respectively 'vCamStartingRoom' and 'vCamBigDrop' the confine mode is 2D and confine screen edges is checked. The priority is the same on both cameras (the default priority of 10). When the player collides with the trigger 'Big Drop' I have a script attached that was trying to do the following:


    public class VCamController : MonoBehaviour
    {
    private GameObject startcam;
    private GameObject transitionCam;
    void Start()
    {
    startcam = GameObject.Find("vCamStartingRoom");
    transitionCam = GameObject.Find("vCamBigDrop");

    }
    private void OnTriggerEnter2D(Collider2D collision)
    {
    if(collision.CompareTag("Player"))
    {
    Debug.Log("The Player Entered");
    startcam.SetActive(false);
    transitionCam.SetActive(true);
    }

    }
    }

    It seems like it switches the camera but the 'vCamBigDrop' does not respect the confining space because the orthographic size of the camera is larger than the confinement area and I don't know how to get it to be the correct size of the narrower hallway since it is set to follow the player. The result is that the camera shows areas of the level it is not supposed to. How can I achieve this and the 'snapping' effect I described above? I'm sure there is a better way to go about this. I also thought of using a state driven camera and somehow switching between cameras based on the room the player was in, but that didn't seem to work well for me either since it required animator. Any help would be appreciated. Some Examples of the types of maps I would like to be able to implement from attached. Celeste_Maps_Ch3_Sec2_(w3000).jpg MegaManIII-SnakeMan.png Thanks!
     
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    1,822
    Why can't you just make the orthographic size of the big drop vcam small enough to fit in the space?
     
  3. JonnyRage

    JonnyRage

    Joined:
    May 30, 2018
    Posts:
    4
    That makes sense thanks. I did try that initially, but I think there was some other issues going on where it kept throwing errors saying my camera variable was a null reference so it wasn't making the switch properly. I believe I resolved that. Also, the fact that I couldn't really place the camera where I wanted since it was set to follow the player was throwing me off. What I did was temporarily raise the priority of the camera I wanted to work with and then moved the player to the area it was confined to and resized the camera there. Is there any way I can change the orthographic size and not be as zoomed in? Also, do you have any ideas as to how I would achieve the snapping transition I described above? Is toggling the active status the best way to handle this, or is it better to change the priority of the camera?
    Thanks again for the help.
     
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    1,822
    Changing the ortho size is the same thing as zooming. If you didn't zoom, you'd see outside the game area! If you don't mind that, then you could just make the bounds collider a little bigger than the game area, and size the camera to fit in that.

    Toggling the active status or the priority will give the same results visually. The difference is that when the camera is inactive it consumes less resources, so that might be of interest to you.

    You could do the snapping by using a custom blend curve when blending to the second camera. You'll need the latest CM (available from Package Manager on Unity 2018) for that feature.
     
    Last edited: Jun 14, 2018
  5. JonnyRage

    JonnyRage

    Joined:
    May 30, 2018
    Posts:
    4
    That's kind of what I figured...I had a feeling I was asking a stupid question but I went for it anyway! If the zoom level is too much then I can go the route you said, or I could scale my tile sizes or character sizes in combination with the orthographic view until I get a look I am happy with. I will look into downloading the latest CM so I can experiment with the custom blend curve. Are there any tutorials or a section in the documentation that you could point me towards to help me get started with that?
     
  6. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    1,822
    No tutorials that I'm aware of, but it's pretty straightforward. To make a custom blend curve (in this example for the brain's default blend) you do this:

    upload_2018-6-14_15-49-11.png

    This will expose a curve editor which you can click on and draw the curve you want:

    upload_2018-6-14_15-50-5.png

    To make custom blends for specific vcams you would first create a custom blends asset:

    upload_2018-6-14_15-51-46.png

    Then add an entry for each custom blend you want to define:

    upload_2018-6-14_15-52-49.png
     
  7. JonnyRage

    JonnyRage

    Joined:
    May 30, 2018
    Posts:
    4
    Very Helpful thanks! I've installed unity 2018 and I will give it a try once I get the latest version of Cinemachine installed using the package manager.
     
  8. allaze-eroler

    allaze-eroler

    Joined:
    Jun 9, 2013
    Posts:
    5
    would be nice to know how is going with your problem, i have a similar problem with my game but instead, it's the confiner that i created for each room like this:


    the problem i have is that each time i change room, the camera haven't switched room position...

    hope i will know it soon :)
     
  9. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    1,822
    Can you describe your problem a little more precisely?
    Do you have multiple rooms, each with a separate collider boundary?
    Are the rooms connected, or separate?
    Are you combining the colliders into a CompositeCollider2D?
    Are you trying to dynamically re-target the collider in the Confiner extension?
    What do you do to change rooms?
     
  10. allaze-eroler

    allaze-eroler

    Joined:
    Jun 9, 2013
    Posts:
    5
    i somehow found a solution to the problem with it, i just have to do this:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Cinemachine;
    5.  
    6. public class DoorsScript : MonoBehaviour {
    7.     public Transform momoo = null; // main player that will be teleported to a new room.
    8.     public BoxCollider2D doorA = null; // the first door from the first room.
    9.     public BoxCollider2D doorB = null; // second door to the next room.
    10.     public CinemachineVirtualCamera cameraRoomA = null; // first camera will be turned off while teleporting.
    11.     public CinemachineVirtualCamera cameraRoomB = null; // new camera that will be turned on while teleporting.
    12.     public Vector2 doorPosition; // door position of the next room.
    13.     public bool doorEntered = false; // this bool will activate or not the "teleportation".
    14.  
    15.  
    16.  
    17.     void Update()
    18.     {
    19.         Vector2 newPos = doorB.transform.position; // it will tell what is the new position of the main player.
    20.  
    21.  
    22.         /*
    23.         this part of code is the important feature that will lead you to the teleportation system.
    24.         it will tell the character to change the place when the button is pressed.
    25.         */
    26.         if(Input.GetKeyUp(KeyCode.Mouse1) && (doorEntered))
    27.             {          
    28.                 cameraRoomB.gameObject.SetActive(true);
    29.                 cameraRoomA.gameObject.SetActive(false);
    30.                 momoo.transform.position = newPos;
    31.                 doorEntered = false;
    32.                 Debug.Log("Button pressed.");
    33.             }
    34.     }
    35.  
    36.     void OnTriggerEnter2D(Collider2D other)
    37.     {
    38.         /*
    39.         this trigger will enable the teleportation feature with the help of the bool when entered.
    40.         */
    41.         if((!doorEntered) && other.tag == "Player")
    42.             {
    43.                 doorEntered = true;
    44.                 Debug.Log("I touched the door");
    45.             }
    46.     }
    47.  
    48.         /*
    49.         it will tell what is the new position of the character.
    50.         */
    51.         void OnTriggerStay2D(Collider2D other)
    52.             {
    53.                 Vector2 newPos = doorB.transform.position;
    54.                 Debug.Log("click right available");
    55.             }
    56.  
    57.  
    58.     /*
    59.     when exiting, the teleportation will be disabled.
    60.     */
    61.     void OnTriggerExit2D(Collider2D other)
    62.         {
    63.             if(other.tag == "Player")
    64.                 {
    65.                     doorEntered = false;
    66.                     Debug.Log("I no longer touched the door");
    67.                 }
    68.         }
    69. }
    as you can see, i managed to switch the camera to a different position, now i have one problem: how to tell the character to stop at the new position rather continuing to move, here the click to move script:

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UnityEngine.Animations;
    4. using UnityEngine.Sprites;
    5.  
    6. public class ClickToMove : MonoBehaviour
    7. {
    8.     public CharacterController2D controller;
    9.     public DoorsScript newPosition;
    10.     public Vector2 targetPosition;
    11.     private Animator anim;
    12.     public Transform target;
    13.  
    14.     //public RuntimeAnimatorController[] variousAnimators;
    15.  
    16.     public float walkSpeed = 5f;
    17.     Rigidbody2D rb;
    18.  
    19.  
    20.     void Start()
    21.     {
    22.         //it will call the animator attached to the character.
    23.         anim = GetComponent<Animator>();
    24.  
    25.  
    26.         /*
    27.         //when the character is idle, it will animate the random idle animations based on time.
    28.         int pickAnumber = Random.Range(1, 2);//exclusive never prints the last only goes 1 to 2
    29.         Debug.Log(pickAnumber);
    30.  
    31.         //randJumpInt is the parameter in animator
    32.         //pickAnumber random number from 1 to 2 from above
    33.         anim.SetInteger("IdleIndex", pickAnumber);
    34.         */
    35.     }
    36.  
    37.  
    38.     // Update is called once per frame.
    39.     void Update()
    40.         {
    41.  
    42.             //when clicked, the character start moving.
    43.             if (Input.GetKeyDown(KeyCode.Mouse0))
    44.             {
    45.                 //with this, it will tell the character where to go.
    46.                 targetPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    47.                 anim.SetBool("Walking", true);
    48.             }
    49.  
    50.             else
    51.                 anim.SetBool("Walking", false);
    52.  
    53.             //it will keep the character stuck on the ground regardless of click position.
    54.             Vector2 newPos = transform.position;
    55.             newPos.x = Vector2.MoveTowards(transform.position, targetPosition, Time.deltaTime * walkSpeed).x;
    56.             transform.position = newPos;
    57.            
    58.             //this little line of code is important to keep the character completely idle.
    59.             targetPosition.y = transform.position.y;
    60.  
    61.  
    62.             //it will activate the walk animation based on the movement.
    63.             if (Vector2.Distance(transform.position, targetPosition) > 0)
    64.             {
    65.                 anim.SetBool("Walking", true);
    66.                 //Debug.Log("I should be walking");
    67.             }
    68.  
    69.             else
    70.             {
    71.                 anim.SetBool("Walking", false);
    72.                 //Debug.Log("I should be idle");
    73.             }
    74.  
    75.  
    76.             //the character will be flipped regardless of direction.
    77.             Vector2 newScale = transform.localScale;
    78.             if (targetPosition.x < transform.position.x)
    79.             {
    80.                 newScale.x = -1;
    81.                 //Debug.Log("I'm going left.");
    82.             }
    83.             //  this (targetPosition.x > transform.position.x) make the character
    84.             //  to face in the different direction after the character walked.
    85.             else if (targetPosition.x > transform.position.x)
    86.             {
    87.                 newScale.x = 1;
    88.             }
    89.             transform.localScale = newScale;
    90.           }
    91.  
    92.     }
    it's still a work in progress but you can see how it work.