Advertisement
mvaganov

Unity3D's default ThirdPersonCamera in C#

Jun 16th, 2013
228
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.05 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. public class ThirdPersonCameraCS : MonoBehaviour {
  5.     public Transform cameraTransform;
  6.     private Transform _target;
  7.  
  8.     // The distance in the x-z plane to the target
  9.     public float distance = 7.0f;
  10.     // the height we want the camera to be above the target
  11.     public float height = 3.0f;
  12.  
  13.     public float angularSmoothLag = 0.3f;
  14.     public float angularMaxSpeed = 15.0f;
  15.     public float heightSmoothLag = 0.3f;
  16.     public float snapSmoothLag = 0.2f;
  17.     public float snapMaxSpeed = 720.0f;
  18.  
  19.     public float clampHeadPositionScreenSpace = 0.75f;
  20.     public float lockCameraTimeout = 0.2f;
  21.        
  22.     private Vector3 headOffset = Vector3.zero;
  23.     private Vector3 centerOffset = Vector3.zero;
  24.    
  25.     private float heightVelocity = 0.0f;
  26.     private float angleVelocity = 0.0f;
  27.     private bool snap = false;
  28.     private ThirdPersonControllerCS controller;
  29.     private float targetHeight = 100000.0f;
  30.    
  31.     public bool canUseMouseLook = true;
  32.     public float sensitivityX = 15;
  33.     public float sensitivityY = 15;
  34.     public float minimumY = -89;
  35.     public float maximumY = 89;
  36.     private float mouseLookAngleOffsetX = 0;
  37.     private float mouseLookAngleOffsetY = 0;
  38.    
  39.     void Awake ()
  40.     {
  41.         if(!cameraTransform && Camera.main)
  42.             cameraTransform = Camera.main.transform;
  43.         if(!cameraTransform) {
  44.             Debug.Log("Please assign a camera to the ThirdPersonCamera script.");
  45.             enabled = false;   
  46.         }
  47.         _target = transform;
  48.         if (_target)
  49.         {
  50.             controller = _target.GetComponent<ThirdPersonControllerCS>();
  51.         }
  52.         if (controller)
  53.         {
  54.             CharacterController characterController = (CharacterController)_target.collider;
  55.             centerOffset = characterController.bounds.center - _target.position;
  56.             headOffset = centerOffset;
  57.             headOffset.y = characterController.bounds.max.y - _target.position.y;
  58.         }
  59.         else
  60.             Debug.Log("Please assign a target to the camera that has a ThirdPersonController script attached.");
  61.         Cut(_target, centerOffset);
  62.     }
  63.  
  64.     void DebugDrawStuff ()
  65.     {
  66.         Debug.DrawLine(_target.position, _target.position + headOffset);
  67.     }
  68.  
  69.     float AngleDistance (float a, float b)
  70.     {
  71.         a = Mathf.Repeat(a, 360);
  72.         b = Mathf.Repeat(b, 360);
  73.         return Mathf.Abs(b - a);
  74.     }
  75.    
  76.     void Apply (Transform dummyTarget, Vector3 dummyCenter)
  77.     {
  78.         // Early out if we don't have a target
  79.         if (!controller)
  80.             return;
  81.         Vector3 targetCenter = _target.position + centerOffset;
  82.         Vector3 targetHead = _target.position + headOffset;
  83.    
  84.     //  DebugDrawStuff();
  85.    
  86.         // Calculate the current & target rotation angles
  87.         float originalTargetAngle = _target.eulerAngles.y;
  88.         float currentAngle = cameraTransform.eulerAngles.y;
  89.    
  90.         // Adjust real target angle when camera is locked
  91.         float targetAngle = originalTargetAngle;
  92.        
  93.         // When pressing Fire2 (alt) the camera will snap to the target direction real quick.
  94.         // It will stop snapping when it reaches the target
  95.         if (Input.GetButton("Fire2"))
  96.         {
  97.             snap = true;
  98.             if(canUseMouseLook)
  99.             {
  100.                 mouseLookAngleOffsetX += Input.GetAxis("Mouse X") * sensitivityX;
  101.                 mouseLookAngleOffsetY += Input.GetAxis("Mouse Y") * sensitivityY;
  102.                 Mathf.Repeat(mouseLookAngleOffsetX, 360);
  103.                 mouseLookAngleOffsetY = Mathf.Clamp(mouseLookAngleOffsetY, minimumY, maximumY);
  104.             }
  105.         }
  106.         else if(Input.GetAxis("Vertical") != 0 || Input.GetAxis("Horizontal") != 0)
  107.         {
  108.             mouseLookAngleOffsetX = 0;
  109.             mouseLookAngleOffsetY = 0;
  110.         }
  111.         targetAngle += mouseLookAngleOffsetX;
  112.        
  113.         if (snap)
  114.         {
  115.             // We are close to the target, so we can stop snapping now!
  116.             if (AngleDistance (currentAngle, originalTargetAngle) < 3.0)
  117.                 snap = false;
  118.             currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angleVelocity, snapSmoothLag, snapMaxSpeed);
  119.         }
  120.         // Normal camera motion
  121.         else
  122.         {
  123.             if (controller.GetLockCameraTimer () < lockCameraTimeout)
  124.             {
  125.                 targetAngle = currentAngle;
  126.             }
  127.             // Lock the camera when moving backwards!
  128.             // * It is really confusing to do 180 degree spins when turning around.
  129.             if (AngleDistance (currentAngle, targetAngle) > 160 && controller.IsMovingBackwards ())
  130.                 targetAngle += 180;
  131.             currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angleVelocity, angularSmoothLag, angularMaxSpeed);
  132.         }
  133.         // When jumping don't move camera upwards but only down!
  134.         if (controller.IsJumping ())
  135.         {
  136.             // We'd be moving the camera upwards, do that only if it's really high
  137.             float newTargetHeight = targetCenter.y + height;
  138.             if (newTargetHeight < targetHeight || newTargetHeight - targetHeight > 5)
  139.                 targetHeight = targetCenter.y + height;
  140.         }
  141.         // When walking always update the target height
  142.         else
  143.         {
  144.             targetHeight = targetCenter.y + height;
  145.         }
  146.    
  147.         // Damp the height
  148.         float currentHeight = cameraTransform.position.y;
  149.         currentHeight = Mathf.SmoothDamp (currentHeight, targetHeight, ref heightVelocity, heightSmoothLag);
  150.    
  151.         // Convert the angle into a rotation, by which we then reposition the camera
  152.         Quaternion currentRotation = Quaternion.Euler (0, currentAngle, 0);
  153.        
  154.         // Set the position of the camera on the x-z plane to:
  155.         // distance meters behind the target
  156.         cameraTransform.position = targetCenter;
  157.         cameraTransform.position += currentRotation * Vector3.back * distance;
  158.    
  159.         // Set the height of the camera
  160.         Vector3 temp = cameraTransform.position;
  161.         temp.y = currentHeight;
  162.         cameraTransform.position = temp;
  163.        
  164.         // Always look at the target   
  165.         SetUpRotation(targetCenter, targetHead);
  166.     }
  167.    
  168.     void LateUpdate () {
  169.         Apply (transform, Vector3.zero);
  170.     }
  171.    
  172.     void Cut (Transform dummyTarget, Vector3 dummyCenter)
  173.     {
  174.         var oldHeightSmooth = heightSmoothLag;
  175.         var oldSnapMaxSpeed = snapMaxSpeed;
  176.         var oldSnapSmooth = snapSmoothLag;
  177.        
  178.         snapMaxSpeed = 10000f;
  179.         snapSmoothLag = 0.001f;
  180.         heightSmoothLag = 0.001f;
  181.        
  182.         snap = true;
  183.         Apply (transform, Vector3.zero);
  184.        
  185.         heightSmoothLag = oldHeightSmooth;
  186.         snapMaxSpeed = oldSnapMaxSpeed;
  187.         snapSmoothLag = oldSnapSmooth;
  188.     }
  189.  
  190.     void SetUpRotation (Vector3 centerPos, Vector3 headPos)
  191.     {
  192.         // Now it's getting hairy. The devil is in the details here, the big issue is jumping of course.
  193.         // * When jumping up and down we don't want to center the guy in screen space.
  194.         //  This is important to give a feel for how high you jump and avoiding large camera movements.
  195.         //  
  196.         // * At the same time we dont want him to ever go out of screen and we want all rotations to be totally smooth.
  197.         //
  198.         // So here is what we will do:
  199.         //
  200.         // 1. We first find the rotation around the y axis. Thus he is always centered on the y-axis
  201.         // 2. When grounded we make him be centered
  202.         // 3. When jumping we keep the camera rotation but rotate the camera to get him back into view if his head is above some threshold
  203.         // 4. When landing we smoothly interpolate towards centering him on screen
  204.         Vector3 cameraPos = cameraTransform.position;
  205.         Vector3 offsetToCenter = centerPos - cameraPos;
  206.        
  207.         // Generate base rotation only around y-axis
  208.         Quaternion yRotation = Quaternion.LookRotation(new Vector3(offsetToCenter.x, 0, offsetToCenter.z));
  209.    
  210.         Vector3 relativeOffset = Vector3.forward * distance + Vector3.down * height;
  211.         cameraTransform.rotation = yRotation * Quaternion.LookRotation(relativeOffset);
  212.    
  213.         // Calculate the projected center position and top position in world space
  214.         Ray centerRay = cameraTransform.camera.ViewportPointToRay(new Vector3(0.5f, 0.5f, 1));
  215.         Ray topRay = cameraTransform.camera.ViewportPointToRay(new Vector3(0.5f, clampHeadPositionScreenSpace, 1));
  216.    
  217.         Vector3 centerRayPos = centerRay.GetPoint(distance);
  218.         Vector3 topRayPos = topRay.GetPoint(distance);
  219.        
  220.         float centerToTopAngle = Vector3.Angle(centerRay.direction, topRay.direction);
  221.        
  222.         float heightToAngle = centerToTopAngle / (centerRayPos.y - topRayPos.y);
  223.    
  224.         float extraLookAngle = heightToAngle * (centerRayPos.y - centerPos.y);
  225.         if (extraLookAngle < centerToTopAngle)
  226.         {
  227.             extraLookAngle = 0;
  228.         }
  229.         else
  230.         {
  231.             extraLookAngle = extraLookAngle - centerToTopAngle;
  232.             cameraTransform.rotation *= Quaternion.Euler(-extraLookAngle, 0, 0);
  233.         }
  234.     }
  235.    
  236.     Vector3 GetCenterOffset ()
  237.     {
  238.         return centerOffset;
  239.     }
  240. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement