Advertisement
mysterious_3D

ThirdPersonCharacterController.js

Oct 7th, 2019
125
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.  
  2. // The speed when walking
  3. var walkSpeed = 3.0;
  4. // after trotAfterSeconds of walking we trot with trotSpeed
  5. var trotSpeed = 4.0;
  6. // when pressing "Fire3" button (cmd) we start running
  7. var runSpeed = 6.0;
  8.  
  9. var inAirControlAcceleration = 3.0;
  10.  
  11. // How high do we jump when pressing jump and letting go immediately
  12. var jumpHeight = 0.5;
  13. // We add extraJumpHeight meters on top when holding the button down longer while jumping
  14. var extraJumpHeight = 2.5;
  15.  
  16. // The gravity for the character
  17. var gravity = 20.0;
  18. // The gravity in controlled descent mode
  19. var controlledDescentGravity = 2.0;
  20. var speedSmoothing = 10.0;
  21. var rotateSpeed = 500.0;
  22. var trotAfterSeconds = 3.0;
  23.  
  24. var canJump = true;
  25. var canControlDescent = true;
  26. var canWallJump = true;
  27.  
  28. private var jumpRepeatTime = 0.05;
  29. private var wallJumpTimeout = 0.15;
  30. private var jumpTimeout = 0.15;
  31. private var groundedTimeout = 0.25;
  32.  
  33. // The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.
  34. private var lockCameraTimer = 0.0;
  35.  
  36. // The current move direction in x-z
  37. private var moveDirection = Vector3.zero;
  38. // The current vertical speed
  39. private var verticalSpeed = 0.0;
  40. // The current x-z move speed
  41. private var moveSpeed = 0.0;
  42.  
  43. // The last collision flags returned from controller.Move
  44. private var collisionFlags : CollisionFlags;
  45.  
  46. // Are we jumping? (Initiated with jump button and not grounded yet)
  47. private var jumping = false;
  48. private var jumpingReachedApex = false;
  49.  
  50. // Are we moving backwards (This locks the camera to not do a 180 degree spin)
  51. private var movingBack = false;
  52. // Is the user pressing any keys?
  53. private var isMoving = true;
  54. // When did the user start walking (Used for going into trot after a while)
  55. private var walkTimeStart = 0.0;
  56. // Last time the jump button was clicked down
  57. private var lastJumpButtonTime = -10.0;
  58. // Last time we performed a jump
  59. private var lastJumpTime = -1.0;
  60. // Average normal of the last touched geometry
  61. private var wallJumpContactNormal : Vector3;
  62. private var wallJumpContactNormalHeight : float;
  63.  
  64. // the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)
  65. private var lastJumpStartHeight = 0.0;
  66. // When did we touch the wall the first time during this jump (Used for wall jumping)
  67. private var touchWallJumpTime = -1.0;
  68.  
  69. private var inAirVelocity = Vector3.zero;
  70.  
  71. private var lastGroundedTime = 0.0;
  72.  
  73. private var lean = 0.0;
  74. private var slammed = false;
  75.  
  76. private var isControllable = true;
  77.  
  78. function Awake ()
  79. {
  80.     moveDirection = transform.TransformDirection(Vector3.forward);
  81. }
  82.  
  83. // This next function responds to the "HidePlayer" message by hiding the player.
  84. // The message is also 'replied to' by identically-named functions in the collision-handling scripts.
  85. // - Used by the LevelStatus script when the level completed animation is triggered.
  86.  
  87. function HidePlayer()
  88. {
  89.     GameObject.Find("rootJoint").GetComponent(SkinnedMeshRenderer).enabled = false; // stop rendering the player.
  90.     isControllable = false; // disable player controls.
  91. }
  92.  
  93. // This is a complementary function to the above. We don't use it in the tutorial, but it's included for
  94. // the sake of completeness. (I like orthogonal APIs; so sue me!)
  95.  
  96. function ShowPlayer()
  97. {
  98.     GameObject.Find("rootJoint").GetComponent(SkinnedMeshRenderer).enabled = true; // start rendering the player again.
  99.     isControllable = true;  // allow player to control the character again.
  100. }
  101.  
  102.  
  103. function UpdateSmoothedMovementDirection ()
  104. {
  105.     var cameraTransform = Camera.main.transform;
  106.     var grounded = IsGrounded();
  107.    
  108.     // Forward vector relative to the camera along the x-z plane   
  109.     var forward = cameraTransform.TransformDirection(Vector3.forward);
  110.     forward.y = 0;
  111.     forward = forward.normalized;
  112.  
  113.     // Right vector relative to the camera
  114.     // Always orthogonal to the forward vector
  115.     var right = Vector3(forward.z, 0, -forward.x);
  116.  
  117.     var v = Input.GetAxisRaw("Vertical");
  118.     var h = Input.GetAxisRaw("Horizontal");
  119.  
  120.     // Are we moving backwards or looking backwards
  121.     if (v < -0.2)
  122.         movingBack = true;
  123.     else
  124.         movingBack = false;
  125.    
  126.     var wasMoving = isMoving;
  127.     isMoving = Mathf.Abs (h) > 0.1 || Mathf.Abs (v) > 0.1;
  128.        
  129.     // Target direction relative to the camera
  130.     var targetDirection = h * right + v * forward;
  131.    
  132.     // Grounded controls
  133.     if (grounded)
  134.     {
  135.         // Lock camera for short period when transitioning moving & standing still
  136.         lockCameraTimer += Time.deltaTime;
  137.         if (isMoving != wasMoving)
  138.             lockCameraTimer = 0.0;
  139.  
  140.         // We store speed and direction seperately,
  141.         // so that when the character stands still we still have a valid forward direction
  142.         // moveDirection is always normalized, and we only update it if there is user input.
  143.         if (targetDirection != Vector3.zero)
  144.         {
  145.             // If we are really slow, just snap to the target direction
  146.             if (moveSpeed < walkSpeed * 0.9 && grounded)
  147.             {
  148.                 moveDirection = targetDirection.normalized;
  149.             }
  150.             // Otherwise smoothly turn towards it
  151.             else
  152.             {
  153.                 moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);
  154.                
  155.                 moveDirection = moveDirection.normalized;
  156.             }
  157.         }
  158.        
  159.         // Smooth the speed based on the current target direction
  160.         var curSmooth = speedSmoothing * Time.deltaTime;
  161.        
  162.         // Choose target speed
  163.         //* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
  164.         var targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0);
  165.    
  166.         // Pick speed modifier
  167.         if (Input.GetButton ("Fire3"))
  168.         {
  169.             targetSpeed *= runSpeed;
  170.         }
  171.         else if (Time.time - trotAfterSeconds > walkTimeStart)
  172.         {
  173.             targetSpeed *= trotSpeed;
  174.         }
  175.         else
  176.         {
  177.             targetSpeed *= walkSpeed;
  178.         }
  179.        
  180.         moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);
  181.        
  182.         // Reset walk time start when we slow down
  183.         if (moveSpeed < walkSpeed * 0.3)
  184.             walkTimeStart = Time.time;
  185.     }
  186.     // In air controls
  187.     else
  188.     {
  189.         // Lock camera while in air
  190.         if (jumping)
  191.             lockCameraTimer = 0.0;
  192.  
  193.         if (isMoving)
  194.             inAirVelocity += targetDirection.normalized * Time.deltaTime * inAirControlAcceleration;
  195.     }
  196.    
  197.  
  198.        
  199. }
  200.  
  201. function ApplyWallJump ()
  202. {
  203.     // We must actually jump against a wall for this to work
  204.     if (!jumping)
  205.         return;
  206.  
  207.     // Store when we first touched a wall during this jump
  208.     if (collisionFlags == CollisionFlags.CollidedSides)
  209.     {
  210.         touchWallJumpTime = Time.time;
  211.     }
  212.  
  213.     // The user can trigger a wall jump by hitting the button shortly before or shortly after hitting the wall the first time.
  214.     var mayJump = lastJumpButtonTime > touchWallJumpTime - wallJumpTimeout && lastJumpButtonTime < touchWallJumpTime + wallJumpTimeout;
  215.     if (!mayJump)
  216.         return;
  217.    
  218.     // Prevent jumping too fast after each other
  219.     if (lastJumpTime + jumpRepeatTime > Time.time)
  220.         return;
  221.    
  222.        
  223.     if (Mathf.Abs(wallJumpContactNormal.y) < 0.2)
  224.     {
  225.         wallJumpContactNormal.y = 0;
  226.         moveDirection = wallJumpContactNormal.normalized;
  227.         // Wall jump gives us at least trotspeed
  228.         moveSpeed = Mathf.Clamp(moveSpeed * 1.5, trotSpeed, runSpeed);
  229.     }
  230.     else
  231.     {
  232.         moveSpeed = 0;
  233.     }
  234.    
  235.     verticalSpeed = CalculateJumpVerticalSpeed (jumpHeight);
  236.     DidJump();
  237.     SendMessage("DidWallJump", null, SendMessageOptions.DontRequireReceiver);
  238. }
  239.  
  240. function ApplyJumping ()
  241. {
  242.     // Prevent jumping too fast after each other
  243.     if (lastJumpTime + jumpRepeatTime > Time.time)
  244.         return;
  245.  
  246.     if (IsGrounded()) {
  247.         // Jump
  248.         // - Only when pressing the button down
  249.         // - With a timeout so you can press the button slightly before landing    
  250.         if (canJump && Time.time < lastJumpButtonTime + jumpTimeout) {
  251.             verticalSpeed = CalculateJumpVerticalSpeed (jumpHeight);
  252.             SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
  253.         }
  254.     }
  255. }
  256.  
  257.  
  258. function ApplyGravity ()
  259. {
  260.     if (isControllable) // don't move player at all if not controllable.
  261.     {
  262.         // Apply gravity
  263.         var jumpButton = Input.GetButton("Jump");
  264.        
  265.         // * When falling down we use controlledDescentGravity (only when holding down jump)
  266.         var controlledDescent = canControlDescent && verticalSpeed <= 0.0 && jumpButton && jumping;
  267.        
  268.         // When we reach the apex of the jump we send out a message
  269.         if (jumping && !jumpingReachedApex && verticalSpeed <= 0.0)
  270.         {
  271.             jumpingReachedApex = true;
  272.             SendMessage("DidJumpReachApex", SendMessageOptions.DontRequireReceiver);
  273.         }
  274.    
  275.         // * When jumping up we don't apply gravity for some time when the user is holding the jump button
  276.         //   This gives more control over jump height by pressing the button longer
  277.         var extraPowerJump =  IsJumping () && verticalSpeed > 0.0 && jumpButton && transform.position.y < lastJumpStartHeight + extraJumpHeight;
  278.        
  279.         if (controlledDescent)         
  280.             verticalSpeed -= controlledDescentGravity * Time.deltaTime;
  281.         else if (extraPowerJump)
  282.             return;
  283.         else if (IsGrounded ())
  284.             verticalSpeed = 0.0;
  285.         else
  286.             verticalSpeed -= gravity * Time.deltaTime;
  287.     }
  288. }
  289.  
  290. function CalculateJumpVerticalSpeed (targetJumpHeight : float)
  291. {
  292.     // From the jump height and gravity we deduce the upwards speed
  293.     // for the character to reach at the apex.
  294.     return Mathf.Sqrt(2 * targetJumpHeight * gravity);
  295. }
  296.  
  297. function DidJump ()
  298. {
  299.     jumping = true;
  300.     jumpingReachedApex = false;
  301.     lastJumpTime = Time.time;
  302.     lastJumpStartHeight = transform.position.y;
  303.     touchWallJumpTime = -1;
  304.     lastJumpButtonTime = -10;
  305. }
  306.  
  307. function Update() {
  308.    
  309.     if (!isControllable)
  310.     {
  311.         // kill all inputs if not controllable.
  312.         Input.ResetInputAxes();
  313.     }
  314.  
  315.     if (Input.GetButtonDown ("Jump"))
  316.     {
  317.         lastJumpButtonTime = Time.time;
  318.     }
  319.  
  320.     UpdateSmoothedMovementDirection();
  321.    
  322.     // Apply gravity
  323.     // - extra power jump modifies gravity
  324.     // - controlledDescent mode modifies gravity
  325.     ApplyGravity ();
  326.  
  327.     // Perform a wall jump logic
  328.     // - Make sure we are jumping against wall etc.
  329.     // - Then apply jump in the right direction)
  330.     if (canWallJump)
  331.         ApplyWallJump();
  332.  
  333.     // Apply jumping logic
  334.     ApplyJumping ();
  335.    
  336.     // Calculate actual motion
  337.     var movement = moveDirection * moveSpeed + Vector3 (0, verticalSpeed, 0) + inAirVelocity;
  338.     movement *= Time.deltaTime;
  339.    
  340.     // Move the controller
  341.     var controller : CharacterController = GetComponent(CharacterController);
  342.     wallJumpContactNormal = Vector3.zero;
  343.     collisionFlags = controller.Move(movement);
  344.    
  345.     // Set rotation to the move direction
  346.     if (IsGrounded())
  347.     {
  348.         if(slammed) // we got knocked over by an enemy. We need to reset some stuff
  349.         {
  350.             slammed = false;
  351.             controller.height = 2;
  352.             transform.position.y += 0.75;
  353.         }
  354.        
  355.         transform.rotation = Quaternion.LookRotation(moveDirection);
  356.            
  357.     }  
  358.     else
  359.     {
  360.         if(!slammed)
  361.         {
  362.             var xzMove = movement;
  363.             xzMove.y = 0;
  364.             if (xzMove.sqrMagnitude > 0.001)
  365.             {
  366.                 transform.rotation = Quaternion.LookRotation(xzMove);
  367.             }
  368.         }
  369.     }  
  370.    
  371.     // We are in jump mode but just became grounded
  372.     if (IsGrounded())
  373.     {
  374.         lastGroundedTime = Time.time;
  375.         inAirVelocity = Vector3.zero;
  376.         if (jumping)
  377.         {
  378.             jumping = false;
  379.             SendMessage("DidLand", SendMessageOptions.DontRequireReceiver);
  380.         }
  381.     }
  382. }
  383.  
  384. function OnControllerColliderHit (hit : ControllerColliderHit )
  385. {
  386. //  Debug.DrawRay(hit.point, hit.normal);
  387.     if (hit.moveDirection.y > 0.01)
  388.         return;
  389.     wallJumpContactNormal = hit.normal;
  390. }
  391.  
  392. function GetSpeed () {
  393.     return moveSpeed;
  394. }
  395.  
  396. function IsJumping () {
  397.     return jumping && !slammed;
  398. }
  399.  
  400. function IsGrounded () {
  401.     return (collisionFlags & CollisionFlags.CollidedBelow) != 0;
  402. }
  403.  
  404. function SuperJump (height : float)
  405. {
  406.     verticalSpeed = CalculateJumpVerticalSpeed (height);
  407.     collisionFlags = CollisionFlags.None;
  408.     SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
  409. }
  410.  
  411. function SuperJump (height : float, jumpVelocity : Vector3)
  412. {
  413.     verticalSpeed = CalculateJumpVerticalSpeed (height);
  414.     inAirVelocity = jumpVelocity;
  415.    
  416.     collisionFlags = CollisionFlags.None;
  417.     SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
  418. }
  419.  
  420. function Slam (direction : Vector3)
  421. {
  422.     verticalSpeed = CalculateJumpVerticalSpeed (1);
  423.     inAirVelocity = direction * 6;
  424.     direction.y = 0.6;
  425.     Quaternion.LookRotation(-direction);
  426.     var controller : CharacterController = GetComponent(CharacterController);
  427.     controller.height = 0.5;
  428.     slammed = true;
  429.     collisionFlags = CollisionFlags.None;
  430.     SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
  431. }
  432.  
  433. function GetDirection () {
  434.     return moveDirection;
  435. }
  436.  
  437. function IsMovingBackwards () {
  438.     return movingBack;
  439. }
  440.  
  441. function GetLockCameraTimer ()
  442. {
  443.     return lockCameraTimer;
  444. }
  445.  
  446. function IsMoving ()  : boolean
  447. {
  448.     return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5;
  449. }
  450.  
  451. function HasJumpReachedApex ()
  452. {
  453.     return jumpingReachedApex;
  454. }
  455.  
  456. function IsGroundedWithTimeout ()
  457. {
  458.     return lastGroundedTime + groundedTimeout > Time.time;
  459. }
  460.  
  461. function IsControlledDescent ()
  462. {
  463.     // * When falling down we use controlledDescentGravity (only when holding down jump)
  464.     var jumpButton = Input.GetButton("Jump");
  465.     return canControlDescent && verticalSpeed <= 0.0 && jumpButton && jumping;
  466. }
  467.  
  468. function Reset ()
  469. {
  470.     gameObject.tag = "Player";
  471. }
  472. // Require a character controller to be attached to the same game object
  473. @script RequireComponent(CharacterController)
  474. @script AddComponentMenu("Third Person Player/Third Person Controller")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement