Search Unity

Proper Platformer Camera

Discussion in 'Scripting' started by Cooper37, Jun 18, 2017.

  1. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Hi all! I'm working on my 3D platformer project and nit-picking at the details. One of the things I've been trying to do is to create a proper, traditional platformer camera. My camera works, however, the camera jumps when the player jumps. Traditional cameras stays the same height when the player jumps, and updates it height when the player has landed on a high platform, or is falling like here:



    Does anyone know what I can add to my current code to make this work? It doesn't seem incredibly hard, just have no idea how to go about it. Thanks for any help, and please ask questions if any!

    MainCamera.cs
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class MainCamera : MonoBehaviour {
    6.     public Transform target;
    7.     public LayerMask layermask;
    8.     public float distance = 18.0f;
    9.  
    10.     public float xSpeed = 130.0f;
    11.     public float ySpeed = 130.0f;
    12.  
    13.     [Header("Position Detail")]
    14.     public float xOffset = 0.0f;
    15.     public float yOffset = 4.0f;
    16.  
    17.     public float yMinLimit = -20f;
    18.     public float yMaxLimit = 75.1f;
    19.  
    20.     public float smoothTime = 0.1f;
    21.  
    22.     public bool showCursor = false;
    23.  
    24.     float x = 0.0f;
    25.     float y = 0.0f;
    26.     float xSmooth = 0.0f;
    27.     float ySmooth = 0.0f;
    28.     float xVelocity = 0.0f;
    29.     float yVelocity = 0.0f;
    30.  
    31.     Vector3 posSmooth = Vector3.one;
    32.     Vector3 posVelocity = Vector3.one;
    33.  
    34.  
    35.     void Update(){
    36.         if(y > yMaxLimit) y = yMaxLimit;
    37.         if(y < yMinLimit) y = yMinLimit;
    38.     }
    39.     void Start () {
    40.         Cursor.visible = showCursor ? true : false;
    41.  
    42.         Vector3 angles = transform.eulerAngles;
    43.         x = angles.y; y = angles.x;
    44.  
    45.         transform.parent = null;
    46.     }
    47.     void LateUpdate () {
    48.         if (target) {
    49.             x += Input.GetAxis ("Mouse X") * xSpeed * 0.02f;
    50.             y -= Input.GetAxis ("Mouse Y") * ySpeed * 0.02f;
    51.  
    52.             xSmooth = Mathf.SmoothDamp (xSmooth, x, ref xVelocity, smoothTime);
    53.             ySmooth = Mathf.SmoothDamp (ySmooth, y, ref yVelocity, smoothTime);
    54.  
    55.             ySmooth = ClampAngle (ySmooth, yMinLimit, yMaxLimit);
    56.  
    57.             Quaternion rotation = Quaternion.Euler (ySmooth, xSmooth, 0);
    58.  
    59.             posSmooth = Vector3.SmoothDamp (posSmooth, target.position, ref posVelocity, smoothTime);
    60.             transform.rotation = rotation;
    61.             transform.position = rotation * new Vector3 (xOffset, yOffset, -distance) + posSmooth;
    62.  
    63.  
    64.             RaycastHit hit;
    65.             if (Physics.Linecast (target.position, transform.position, out hit, layermask)) {
    66.                 float tempDistance = Vector3.Distance(target.position,hit.point);
    67.                 Vector3 position = rotation * new Vector3(xOffset, yOffset, -tempDistance) + posSmooth;
    68.                 transform.position = position;
    69.             }
    70.             Camera.main.fieldOfView = Control.STEALTH ? Mathf.Lerp(Camera.main.fieldOfView, 45f, Time.deltaTime * 0.8f) :
    71.                 Mathf.Lerp(Camera.main.fieldOfView, 60f, Time.deltaTime * 0.8f);
    72.         }
    73.     }
    74.     static float ClampAngle (float angle, float min, float max) {
    75.         if (angle < -360)  angle += 360;
    76.         if (angle > 360) angle -= 360;
    77.  
    78.         return Mathf.Clamp (angle, min, max);
    79.     }
    80. }
    81.  
     
  2. cstooch

    cstooch

    Joined:
    Apr 16, 2014
    Posts:
    354
    I can't tell, but it looks like the player is actually controlling the tilt of the camera, and I can't really see the camera going up and down at all. But I'll give an idea of how you can do what I think you're trying. Try positioning the two main axes of the camera based on your player's location, like you normally would (grounded or not, this will always update each frame, or whatever). For the other axis (the height), only set this when the player is grounded (of course doing lerp or something to change the camera height gradually).

    How you detect whether they're grounded or not.. there's multiple methods. One being raycasting downward, another being collision detection.. there's more methods than that though. The Unity player controller actually even has an IsGrounded (i think that's the name) flag you can use. So maybe try that?
     
  3. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    That was the first method I tried, actually. One of the player's abilities is that they can move upwards into the air when inside of a wind tunnel, or slowly hover downward. In these cases, the player is not grounded, so the camera never updates the height. Maybe some of this gameplay can also demonstrate what I'm after @54seconds:

     
  4. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I'm not sure how this would play out, since I don't really have a platform game to simulate it on..
    Code (csharp):
    1.  
    2.  bool adjust = false;
    3.  Vector3 p;
    4.  
    5.     void LateUpdate () {
    6.         if (Mathf.Abs(transform.position.y - player.position.y) > 1f)
    7.             adjust = true;
    8.         else if (adjust && Mathf.Abs(transform.position.y - player.position.y) < .1f)
    9.         {
    10.             transform.position = new Vector3(transform.position.x, player.position.y, transform.position.z);
    11.             adjust = false;
    12.         }
    13.         if (adjust) {
    14.             // didn't do any x or z position adjustments.
    15.             p = new Vector3(transform.position.x, player.position.y, transform.position.z);
    16.             transform.position = Vector3.MoveTowards(transform.position, p, 5f * Time.deltaTime);
    17.         }
    18.    }
    19.  
    Note: obviously the values for distance(difference) and speed will probably need tweaking, if this works at all for you. I spent a few mins just mucking around with a capsule moving up n down to test this out lol.

    In the first video, it did appear that some height changes could be made without the camera adjusting, but in the second video it looks like the height is always adjusted (just smoothly) -- maybe I just imagine it but .. :) Oh well.
     
    Last edited: Jun 18, 2017
    cstooch likes this.
  5. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Okay, I see what you did there. However, this kinda would destroy my current script, and would have to rebuild the rest around this method. I tried implementing it and it got all crazy on me lol I'm thinking of changing or working with variables in my script that deals with height, probably the yOffset. These such variables may change based on certain conditions like being grounded for one. But what about being (or almost being) off the screen?
     
  6. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Ok.. obviously you shouldn't use my script in place of yours..:)
    That was just an idea of how to work that portion of it, and I copied it directly out of VS, after messing around with it in Unity for a bit. ;)
    You could check if it's close to off the screen, I guess, if that's what you'd like..
     
  7. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    I think that's what I may do, but not sure. After I changed line 59 to this:
    Code (csharp):
    1.  
    2. posSmooth = Vector3.SmoothDamp (posSmooth, new Vector3(target.position.x, 0, target.position.z), ref posVelocity, smoothTime);
    3.  
    the camera now moves without using the height. Now, all I gotta do is replace 0 with a variable that changes based off of a few conditions...just not 100% sure where to go from here though lol
     
  8. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Sure, I mean that sounds reasonable :) Maybe it'll be something like the code I made, and maybe something a bit different..but sounds like you're close.
     
  9. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Yeah, I think I can use a piece of your code to finish this off. Thanks for your help btw! :)
     
  10. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Eh, no problem. I hope you get it working how you'd like ;)
     
  11. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Code (csharp):
    1.  
    2. Vector3 pos = Camera.main.WorldToViewportPoint (target.position);
    3. if (target.GetComponent<CharacterController> ().isGrounded || adjust) {
    4.             yHeight = Mathf.Lerp (yHeight, target.transform.position.y, .15f);
    5.             adjust = false;
    6.         }
    7.         if (pos.y < 0.3f || 0.7 < pos.y) adjust = true;
    8.  
    This works for the most part. yHeight controls the height of the camera and adjust if the player is on the ground or too high or below the camera's view. The only problem is : 0.7 < pos.y. How can I make it to where when this is true, adjust will STAY true until the player touches the ground, even when pos.y becomes less than 0.7 later?
     
  12. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    First off - glad it's mostly working.
    Are you saying/asking:
    Code (csharp):
    1.  if(.7f < pos.y){
    2.    if(!grounded) keepAdjusting = true;
    3.    adjust = true;
    4.   }
    Something like that?

    nb. I realize I 'shortened' your actual code, but just wrote this for "on topic" :)
     
  13. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Yeah, that's what I'm trying to do
     
  14. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Well, use something like that if it's correct? :)

    I admit to getting a tiny bit lost along the way, but if it works.. great :)
     
  15. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Yeah, sorry if you're lost lol it's just the nature of coding, when .7 < pos.y is true, adjust will be true as long as that condition is true. If it's not, it turns to false, which is what I don't want in this case. I would have to find a workaround
     
  16. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    err, yes, like when I wrote the latest code snippet.. and asked if that's what you meant, you said yes.
    so why not just use that (with appropriate modifications?).
     
  17. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    I thought you meant like logically, yes, but the code itself didn't work
     
  18. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Well b/c the variables I used were just to give you an idea.. for instance "grounded" wasn't even in your code, I don't think. Plus, you'd have to add the 'keepAdjusting' to your logic flow, too, and see that it influences something. (plus you'd have to declare it.. and so on) :) :)
     
  19. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    LOL I did this already, naturally...however it doesn't seem to keep any of the booleans true. Perhaps its the first conditions?
    Code (csharp):
    1.  
    2. if (target.GetComponent<CharacterController> ().isGrounded || (adjust && keepAdjusting)) {
    3.             yHeight = Mathf.Lerp (yHeight, target.transform.position.y, .15f);
    4.             adjust = false;
    5.         }
    6.         if (pos.y < 0.3f || 0.7 < pos.y) {
    7.             if (!target.GetComponent<CharacterController> ().isGrounded)
    8.                 keepAdjusting = true;
    9.             adjust = true;
    10.         }
    11.  
     
  20. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    unless I'm hazy on the details, didn't you want that once keepAdjusting was true, you'd always run the (top portion of) code, until you are on the ground?
    If that's so, I'd change (adjust && keepAdjusting) to : adjust || keepAdjusting
    Then add logic to switching keepAdjusting to false once you land (+and if it's true).
     
  21. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Yes, I wanted to run the top code until the player touches the ground, changing it from && to ||, takes me to my original problem of the camera jumping with the player
     
  22. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Do you ever turn keepAdjusting to false?
     
  23. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Yes, in place of adjust = false;
     
  24. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Oh okay.. I was thinking you wanted to keep adjust as it was and only set "keepAdjusting" to false when you land on the ground, which was your condition, as I understood it before.
     
  25. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Hmm...well for some reason this works! There are cases though where when the player immediately jumps after landing, the camera jumps with the player, unless I wait like half a second, then it resets. lol idk but this is close enough, thanks for your help good sir! :)
    Code (csharp):
    1.  
    2. if (adjust)
    3.             yHeight = Mathf.Lerp (yHeight, target.transform.position.y, .15f);
    4.         if (target.GetComponent<CharacterController> ().isGrounded)
    5.             adjust = false;
    6.         if (pos.y < 0.3f || pos.y > 0.8f) adjust = true;
    7.  
     
  26. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Cool, cool.. glad ya got something ya liked :) You're welcome. Enjoy your game making!