mvaganov

Unity3D's default ThirdPersonController in C#

Jun 16th, 2013
261
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. // Require a character controller to be attached to the same game object
  5. [RequireComponent (typeof (CharacterController))]
  6.     public class ThirdPersonControllerCS : MonoBehaviour {
  7.    
  8.     public AnimationClip idleAnimation;
  9.     public AnimationClip walkAnimation;
  10.     public AnimationClip runAnimation;
  11.     public AnimationClip jumpPoseAnimation;
  12.    
  13.     public float walkMaxAnimationSpeed = 0.75f;
  14.     public float trotMaxAnimationSpeed = 1.0f;
  15.     public float runMaxAnimationSpeed = 1.0f;
  16.     public float jumpAnimationSpeed = 1.15f;
  17.     public float landAnimationSpeed = 1.0f;
  18.    
  19.     private Animation _animation;
  20.    
  21.     enum CharacterState {
  22.         Idle = 0,
  23.         Walking = 1,
  24.         Trotting = 2,
  25.         Running = 3,
  26.         Jumping = 4,
  27.     }
  28.    
  29.     private CharacterState _characterState;
  30.    
  31.     // The speed when walking
  32.     public float walkSpeed = 2.0f;
  33.     // after trotAfterSeconds of walking we trot with trotSpeed
  34.     public float trotSpeed = 4.0f;
  35.     // when pressing "Fire3" button (cmd) we start running
  36.     public float runSpeed = 6.0f;
  37.    
  38.     public float inAirControlAcceleration = 3.0f;
  39.    
  40.     // How high do we jump when pressing jump and letting go immediately
  41.     public float jumpHeight = 0.5f;
  42.    
  43.     // The gravity for the character
  44.     public float gravity = 20.0f;
  45.     // The gravity in controlled descent mode
  46.     public float speedSmoothing = 10.0f;
  47.     public float rotateSpeed = 500.0f;
  48.     public float trotAfterSeconds = 3.0f;
  49.    
  50.     public bool canJump = true;
  51.    
  52.     private float jumpRepeatTime = 0.05f;
  53.     private float jumpTimeout = 0.15f;
  54.     private float groundedTimeout = 0.25f;
  55.  
  56.     // The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.
  57.     private float lockCameraTimer = 0.0f;
  58.    
  59.     // The current move direction in x-z
  60.     private Vector3 moveDirection = Vector3.zero;
  61.     // The current vertical speed
  62.     private float verticalSpeed = 0.0f;
  63.     // The current x-z move speed
  64.     private float moveSpeed = 0.0f;
  65.  
  66.     // The last collision flags returned from controller.Move
  67.     private CollisionFlags collisionFlags;
  68.  
  69.     // Are we jumping? (Initiated with jump button and not grounded yet)
  70.     private bool jumping = false;
  71.     private bool jumpingReachedApex = false;
  72.  
  73.     // Are we moving backwards (This locks the camera to not do a 180 degree spin)
  74.     private bool movingBack = false;
  75.     // Is the user pressing any keys?
  76.     private bool isMoving = false;
  77.     // When did the user start walking (Used for going into trot after a while)
  78.     private float walkTimeStart = 0.0f;
  79.     // Last time the jump button was clicked down
  80.     private float lastJumpButtonTime = -10.0f;
  81.     // Last time we performed a jump
  82.     private float lastJumpTime = -1.0f;
  83.    
  84.     // the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)
  85. //  private float lastJumpStartHeight = 0.0f;
  86.    
  87.    
  88.     private Vector3 inAirVelocity = Vector3.zero;
  89.    
  90.     private float lastGroundedTime = 0.0f;
  91.    
  92.     private bool isControllable = true;
  93.  
  94.     void Awake ()
  95.     {
  96.         moveDirection = transform.TransformDirection(Vector3.forward);
  97.         _animation = GetComponent<Animation>();
  98.         if(!_animation)
  99.             Debug.Log("The character you would like to control doesn't have animations. Moving her might look weird.");
  100.         if(!idleAnimation) {
  101.             _animation = null;
  102.             Debug.Log("No idle animation found. Turning off animations.");
  103.         }
  104.         if(!walkAnimation) {
  105.             _animation = null;
  106.             Debug.Log("No walk animation found. Turning off animations.");
  107.         }
  108.         if(!runAnimation) {
  109.             _animation = null;
  110.             Debug.Log("No run animation found. Turning off animations.");
  111.         }
  112.         if(!jumpPoseAnimation && canJump) {
  113.             _animation = null;
  114.             Debug.Log("No jump animation found and the character has canJump enabled. Turning off animations.");
  115.         }
  116.     }
  117.  
  118.     void UpdateSmoothedMovementDirection ()
  119.     {
  120.         Transform cameraTransform = Camera.main.transform;
  121.         bool grounded = IsGrounded();
  122.        
  123.         // Forward vector relative to the camera along the x-z plane   
  124.         Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);
  125.         forward.y = 0;
  126.         forward = forward.normalized;
  127.    
  128.         // Right vector relative to the camera
  129.         // Always orthogonal to the forward vector
  130.         Vector3 right = new Vector3(forward.z, 0, -forward.x);
  131.    
  132.         float v = Input.GetAxisRaw("Vertical");
  133.         float h = Input.GetAxisRaw("Horizontal");
  134.    
  135.         // Are we moving backwards or looking backwards
  136.         if (v < -0.2f)
  137.             movingBack = true;
  138.         else
  139.             movingBack = false;
  140.        
  141.         bool wasMoving = isMoving;
  142.         isMoving = Mathf.Abs (h) > 0.1f || Mathf.Abs (v) > 0.1f;
  143.            
  144.         // Target direction relative to the camera
  145.         Vector3 targetDirection = h * right + v * forward;
  146.        
  147.         // Grounded controls
  148.         if (grounded)
  149.         {
  150.             // Lock camera for short period when transitioning moving & standing still
  151.             lockCameraTimer += Time.deltaTime;
  152.             if (isMoving != wasMoving)
  153.                 lockCameraTimer = 0.0f;
  154.    
  155.             // We store speed and direction seperately,
  156.             // so that when the character stands still we still have a valid forward direction
  157.             // moveDirection is always normalized, and we only update it if there is user input.
  158.             if (targetDirection != Vector3.zero)
  159.             {
  160.                 // If we are really slow, just snap to the target direction
  161.                 if (moveSpeed < walkSpeed * 0.9f && grounded)
  162.                 {
  163.                     moveDirection = targetDirection.normalized;
  164.                 }
  165.                 // Otherwise smoothly turn towards it
  166.                 else
  167.                 {
  168.                     moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);
  169.                    
  170.                     moveDirection = moveDirection.normalized;
  171.                 }
  172.             }
  173.            
  174.             // Smooth the speed based on the current target direction
  175.             var curSmooth = speedSmoothing * Time.deltaTime;
  176.            
  177.             // Choose target speed
  178.             //* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
  179.             var targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0f);
  180.        
  181.             _characterState = CharacterState.Idle;
  182.            
  183.             // Pick speed modifier
  184.             if (Input.GetKey (KeyCode.LeftShift) || Input.GetKey (KeyCode.RightShift))
  185.             {
  186.                 targetSpeed *= runSpeed;
  187.                 _characterState = CharacterState.Running;
  188.             }
  189.             else if (Time.time - trotAfterSeconds > walkTimeStart)
  190.             {
  191.                 targetSpeed *= trotSpeed;
  192.                 _characterState = CharacterState.Trotting;
  193.             }
  194.             else
  195.             {
  196.                 targetSpeed *= walkSpeed;
  197.                 _characterState = CharacterState.Walking;
  198.             }
  199.            
  200.             moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);
  201.            
  202.             // Reset walk time start when we slow down
  203.             if (moveSpeed < walkSpeed * 0.3f)
  204.                 walkTimeStart = Time.time;
  205.         }
  206.         // In air controls
  207.         else
  208.         {
  209.             // Lock camera while in air
  210.             if (jumping)
  211.                 lockCameraTimer = 0.0f;
  212.    
  213.             if (isMoving)
  214.                 inAirVelocity += targetDirection.normalized * Time.deltaTime * inAirControlAcceleration;
  215.         }
  216.     }
  217.  
  218.  
  219.     void ApplyJumping ()
  220.     {
  221.         // Prevent jumping too fast after each other
  222.         if (lastJumpTime + jumpRepeatTime > Time.time)
  223.             return;
  224.    
  225.         if (IsGrounded()) {
  226.             // Jump
  227.             // - Only when pressing the button down
  228.             // - With a timeout so you can press the button slightly before landing    
  229.             if (canJump && Time.time < lastJumpButtonTime + jumpTimeout) {
  230.                 verticalSpeed = CalculateJumpVerticalSpeed (jumpHeight);
  231.                 SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
  232.             }
  233.         }
  234.     }
  235.  
  236.  
  237.     void ApplyGravity ()
  238.     {
  239.         if (isControllable) // don't move player at all if not controllable.
  240.         {
  241.             // Apply gravity
  242. //          bool jumpButton = Input.GetButton("Jump");
  243.            
  244.             // When we reach the apex of the jump we send out a message
  245.             if (jumping && !jumpingReachedApex && verticalSpeed <= 0.0)
  246.             {
  247.                 jumpingReachedApex = true;
  248.                 SendMessage("DidJumpReachApex", SendMessageOptions.DontRequireReceiver);
  249.             }
  250.             if (IsGrounded ())
  251.                 verticalSpeed = 0.0f;
  252.             else
  253.                 verticalSpeed -= gravity * Time.deltaTime;
  254.         }
  255.     }
  256.  
  257.     float CalculateJumpVerticalSpeed (float targetJumpHeight)
  258.     {
  259.         // From the jump height and gravity we deduce the upwards speed
  260.         // for the character to reach at the apex.
  261.         return Mathf.Sqrt(2 * targetJumpHeight * gravity);
  262.     }
  263.  
  264.     void DidJump ()
  265.     {
  266.         jumping = true;
  267.         jumpingReachedApex = false;
  268.         lastJumpTime = Time.time;
  269. //      lastJumpStartHeight = transform.position.y;
  270.         lastJumpButtonTime = -10;
  271.        
  272.         _characterState = CharacterState.Jumping;
  273.     }
  274.  
  275.     void Update()
  276.     {
  277.        
  278.         if (!isControllable)
  279.         {
  280.             // kill all inputs if not controllable.
  281.             Input.ResetInputAxes();
  282.         }
  283.    
  284.         if (Input.GetButtonDown ("Jump"))
  285.         {
  286.             lastJumpButtonTime = Time.time;
  287.         }
  288.    
  289.         UpdateSmoothedMovementDirection();
  290.        
  291.         // Apply gravity
  292.         // - extra power jump modifies gravity
  293.         // - controlledDescent mode modifies gravity
  294.         ApplyGravity ();
  295.    
  296.         // Apply jumping logic
  297.         ApplyJumping ();
  298.        
  299.         // Calculate actual motion
  300.         Vector3 movement = moveDirection * moveSpeed + new Vector3 (0, verticalSpeed, 0) + inAirVelocity;
  301.         movement *= Time.deltaTime;
  302.        
  303.         // Move the controller
  304.         CharacterController controller = GetComponent<CharacterController>();
  305.         collisionFlags = controller.Move(movement);
  306.        
  307.         // ANIMATION sector
  308.         if(_animation) {
  309.             if(_characterState == CharacterState.Jumping)
  310.             {
  311.                 if(!jumpingReachedApex) {
  312.                     _animation[jumpPoseAnimation.name].speed = jumpAnimationSpeed;
  313.                     _animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
  314.                     _animation.CrossFade(jumpPoseAnimation.name);
  315.                 } else {
  316.                     _animation[jumpPoseAnimation.name].speed = -landAnimationSpeed;
  317.                     _animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
  318.                     _animation.CrossFade(jumpPoseAnimation.name);              
  319.                 }
  320.             }
  321.             else
  322.             {
  323.                 if(controller.velocity.sqrMagnitude < 0.1f) {
  324.                     _animation.CrossFade(idleAnimation.name);
  325.                 }
  326.                 else
  327.                 {
  328.                     if(_characterState == CharacterState.Running) {
  329.                         _animation[runAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, runMaxAnimationSpeed);
  330.                         _animation.CrossFade(runAnimation.name);   
  331.                     }
  332.                     else if(_characterState == CharacterState.Trotting) {
  333.                         _animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, trotMaxAnimationSpeed);
  334.                         _animation.CrossFade(walkAnimation.name);  
  335.                     }
  336.                     else if(_characterState == CharacterState.Walking) {
  337.                         _animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, walkMaxAnimationSpeed);
  338.                         _animation.CrossFade(walkAnimation.name);  
  339.                     }
  340.                    
  341.                 }
  342.             }
  343.         }
  344.         // ANIMATION sector
  345.        
  346.         // Set rotation to the move direction
  347.         if (IsGrounded())
  348.         {
  349.            
  350.             transform.rotation = Quaternion.LookRotation(moveDirection);
  351.                
  352.         }  
  353.         else
  354.         {
  355.             var xzMove = movement;
  356.             xzMove.y = 0;
  357.             if (xzMove.sqrMagnitude > 0.001f)
  358.             {
  359.                 transform.rotation = Quaternion.LookRotation(xzMove);
  360.             }
  361.         }  
  362.        
  363.         // We are in jump mode but just became grounded
  364.         if (IsGrounded())
  365.         {
  366.             lastGroundedTime = Time.time;
  367.             inAirVelocity = Vector3.zero;
  368.             if (jumping)
  369.             {
  370.                 jumping = false;
  371.                 SendMessage("DidLand", SendMessageOptions.DontRequireReceiver);
  372.             }
  373.         }
  374.     }
  375.  
  376.     void OnControllerColliderHit (ControllerColliderHit hit )
  377.     {
  378.     //  Debug.DrawRay(hit.point, hit.normal);
  379.         if (hit.moveDirection.y > 0.01)
  380.             return;
  381.     }
  382.  
  383.     public float GetSpeed () {
  384.         return moveSpeed;
  385.     }
  386.  
  387.     public bool IsJumping () {
  388.         return jumping;
  389.     }
  390.  
  391.     public bool IsGrounded () {
  392.         return (collisionFlags & CollisionFlags.CollidedBelow) != 0;
  393.     }
  394.  
  395.     public Vector3 GetDirection () {
  396.         return moveDirection;
  397.     }
  398.  
  399.     public bool IsMovingBackwards () {
  400.         return movingBack;
  401.     }
  402.  
  403.     public float GetLockCameraTimer ()
  404.     {
  405.         return lockCameraTimer;
  406.     }
  407.  
  408.     public bool IsMoving ()
  409.     {
  410.         return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5;
  411.     }
  412.  
  413.     public bool HasJumpReachedApex ()
  414.     {
  415.         return jumpingReachedApex;
  416.     }
  417.  
  418.     public bool IsGroundedWithTimeout ()
  419.     {
  420.         return lastGroundedTime + groundedTimeout > Time.time;
  421.     }
  422.  
  423.     public void Reset ()
  424.     {
  425.         gameObject.tag = "Player";
  426.     }
  427. }
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×