Advertisement
Guest User

Untitled

a guest
May 6th, 2016
102
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 14.80 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. namespace RootMotion.Demos {
  5.  
  6.     /// <summary>
  7.     /// Third person character controller. This class is based on the ThirdPersonCharacter.cs of the Unity Exmaple Assets.
  8.     /// </summary>
  9.     public class CharacterThirdPerson : CharacterBase {
  10.  
  11.         // Is the character always rotating to face the move direction or is he strafing?
  12.         [System.Serializable]
  13.         public enum MoveMode {
  14.             Directional,
  15.             Strafe
  16.         }
  17.  
  18.         // Animation state
  19.         public struct AnimState {
  20.             public Vector3 moveDirection; // the forward speed
  21.             public bool jump; // should the character be jumping?
  22.             public bool crouch; // should the character be crouching?
  23.             public bool onGround; // is the character grounded
  24.             public bool isStrafing; // should the character always rotate to face the move direction or strafe?
  25.             public float yVelocity; // y velocity of the character
  26.         }
  27.  
  28.         [Header("References")]
  29.         public CharacterAnimationBase characterAnimation; // the animation controller
  30.         public UserControlThirdPerson userControl; // user input
  31.         public CameraController cam; // Camera controller (optional). If assigned will update the camera in LateUpdate only if character moves
  32.  
  33.         [Header("Movement")]
  34.         public MoveMode moveMode; // Is the character always rotating to face the move direction or is he strafing?
  35.         public bool smoothPhysics = true; // If true, will use interpolation to smooth out the fixed time step.
  36.         public float smoothAccelerationTime = 0.2f; // The smooth acceleration of the speed of the character (using Vector3.SmoothDamp)
  37.         public float linearAccelerationSpeed = 3f; // The linear acceleration of the speed of the character (using Vector3.MoveTowards)
  38.         public float platformFriction = 7f;                 // the acceleration of adapting the velocities of moving platforms
  39.         public float groundStickyEffect = 4f;               // power of 'stick to ground' effect - prevents bumping down slopes.
  40.         public float maxVerticalVelocityOnGround = 3f;      // the maximum y velocity while the character is grounded
  41.         public float velocityToGroundTangentWeight = 0f;    // the weight of rotating character velocity vector to the ground tangent
  42.  
  43.         [Header("Rotation")]
  44.         public bool lookInCameraDirection; // should the character be looking in the same direction that the camera is facing
  45.         public float turnSpeed = 5f;                    // additional turn speed added when the player is moving (added to animation root rotation)
  46.         public float stationaryTurnSpeedMlp = 1f;           // additional turn speed added when the player is stationary (added to animation root rotation)
  47.  
  48.         [Header("Jumping and Falling")]
  49.         public float airSpeed = 6f; // determines the max speed of the character while airborne
  50.         public float airControl = 2f; // determines the response speed of controlling the character while airborne
  51.         public float jumpPower = 12f; // determines the jump force applied when jumping (and therefore the jump height)
  52.         public float jumpRepeatDelayTime = 0f;          // amount of time that must elapse between landing and being able to jump again
  53.  
  54.         [Header("Wall Running")]
  55.  
  56.         [SerializeField] LayerMask wallRunLayers; // walkable vertical surfaces
  57.         public float wallRunMaxLength = 1f;                 // max duration of a wallrun
  58.         public float wallRunMinMoveMag = 0.6f;              // the minumum magnitude of the user control input move vector
  59.         public float wallRunMinVelocityY = -1f;             // the minimum vertical velocity of doing a wall run
  60.         public float wallRunRotationSpeed = 1.5f;           // the speed of rotating the character to the wall normal
  61.         public float wallRunMaxRotationAngle = 70f;         // max angle of character rotation
  62.         public float wallRunWeightSpeed = 5f;               // the speed of blending in/out the wall running effect
  63.  
  64.         [Header("Crouching")]
  65.         public float crouchCapsuleScaleMlp = 0.6f;          // the capsule collider scale multiplier while crouching
  66.  
  67.         public bool onGround { get; private set; }
  68.         public AnimState animState = new AnimState();
  69.  
  70.         protected Vector3 moveDirection; // The current move direction of the character in Strafe move mode
  71.         private Animator animator;
  72.         private Vector3 normal, platformVelocity, platformAngularVelocity;
  73.         private RaycastHit hit;
  74.         private float jumpLeg, jumpEndTime, forwardMlp, groundDistance, lastAirTime, stickyForce;
  75.         private Vector3 wallNormal = Vector3.up;
  76.         private Vector3 moveDirectionVelocity;
  77.         private float wallRunWeight;
  78.         private float lastWallRunWeight;
  79.         private Vector3 fixedDeltaPosition;
  80.         private Quaternion fixedDeltaRotation;
  81.         private bool fixedFrame;
  82.         private float wallRunEndTime;
  83.  
  84.         // Use this for initialization
  85.         protected override void Start () {
  86.             base.Start();
  87.  
  88.             animator = GetComponent<Animator>();
  89.             if (animator == null) animator = characterAnimation.GetComponent<Animator>();
  90.  
  91.             wallNormal = Vector3.up;
  92.             onGround = true;
  93.             animState.onGround = true;
  94.  
  95.             if (cam != null) cam.enabled = false;
  96.         }
  97.  
  98.         void OnAnimatorMove() {
  99.             Move (animator.deltaPosition, animator.deltaRotation);
  100.         }
  101.  
  102.         // When the Animator moves
  103.         public override void Move(Vector3 deltaPosition, Quaternion deltaRotation) {
  104.             // Accumulate delta position, update in FixedUpdate to maintain consitency
  105.             fixedDeltaPosition += deltaPosition;
  106.             fixedDeltaRotation *= deltaRotation;
  107.         }
  108.  
  109.         void FixedUpdate() {
  110.             if (animator != null && animator.updateMode == AnimatorUpdateMode.AnimatePhysics) {
  111.                 smoothPhysics = false;
  112.                 characterAnimation.smoothFollow = false;
  113.             }
  114.  
  115.             // Smoothing out the fixed time step
  116.             r.interpolation = smoothPhysics? RigidbodyInterpolation.Interpolate: RigidbodyInterpolation.None;
  117.             characterAnimation.smoothFollow = smoothPhysics;
  118.  
  119.             // Move
  120.             MoveFixed(fixedDeltaPosition);
  121.             fixedDeltaPosition = Vector3.zero;
  122.  
  123.             transform.rotation *= fixedDeltaRotation;
  124.             fixedDeltaRotation = Quaternion.identity;
  125.  
  126.             Rotate();
  127.  
  128.             GroundCheck (); // detect and stick to ground
  129.  
  130.             // Friction
  131.             if (userControl.state.move == Vector3.zero && groundDistance < airborneThreshold * 0.5f) HighFriction();
  132.             else ZeroFriction();
  133.            
  134.             if (onGround) {
  135.                 // Jumping
  136.                 animState.jump = Jump();
  137.             } else {
  138.                
  139.                 // Additional gravity
  140.                 r.AddForce((Physics.gravity * gravityMultiplier) - Physics.gravity);
  141.             }
  142.            
  143.             // Scale the capsule colllider while crouching
  144.             ScaleCapsule(userControl.state.crouch? crouchCapsuleScaleMlp: 1f);
  145.            
  146.  
  147.             fixedFrame = true;
  148.  
  149.         }
  150.  
  151.         protected virtual void Update() {
  152.             // Fill in animState
  153.             animState.onGround = onGround;
  154.             animState.moveDirection = GetMoveDirection();
  155.             animState.yVelocity = Mathf.Lerp(animState.yVelocity, r.velocity.y, Time.deltaTime * 10f);
  156.             animState.crouch = userControl.state.crouch;
  157.             animState.isStrafing = moveMode == MoveMode.Strafe;
  158.         }
  159.  
  160.         protected virtual void LateUpdate() {
  161.             if (cam == null) return;
  162.            
  163.             cam.UpdateInput();
  164.            
  165.             if (!fixedFrame && r.interpolation == RigidbodyInterpolation.None) return;
  166.            
  167.             // Update camera only if character moves
  168.             cam.UpdateTransform(r.interpolation == RigidbodyInterpolation.None? Time.fixedDeltaTime: Time.deltaTime);
  169.            
  170.             fixedFrame = false;
  171.         }
  172.  
  173.         private void MoveFixed(Vector3 deltaPosition) {
  174.             // Process horizontal wall-running
  175.             WallRun();
  176.            
  177.             Vector3 velocity = deltaPosition / Time.deltaTime;
  178.            
  179.             // Add velocity of the rigidbody the character is standing on
  180.             velocity += new Vector3(platformVelocity.x, 0f, platformVelocity.z);
  181.            
  182.             if (onGround) {
  183.                 // Rotate velocity to ground tangent
  184.                 if (velocityToGroundTangentWeight > 0f) {
  185.                     Quaternion rotation = Quaternion.FromToRotation(transform.up, normal);
  186.                     velocity = Quaternion.Lerp(Quaternion.identity, rotation, velocityToGroundTangentWeight) * velocity;
  187.                 }
  188.             } else {
  189.                 // Air move
  190.                 Vector3 airMove = new Vector3 (userControl.state.move.x * airSpeed, 0f, userControl.state.move.z * airSpeed);
  191.                 velocity = Vector3.Lerp(r.velocity, airMove, Time.deltaTime * airControl);
  192.             }
  193.            
  194.             if (onGround && Time.time > jumpEndTime) {
  195.                 r.velocity = r.velocity - transform.up * stickyForce * Time.deltaTime;
  196.             }
  197.            
  198.             // Vertical velocity
  199.             velocity.y = Mathf.Clamp(r.velocity.y, r.velocity.y, onGround? maxVerticalVelocityOnGround: r.velocity.y);
  200.            
  201.             r.velocity = velocity;
  202.            
  203.             // Dampering forward speed on the slopes
  204.             float slopeDamper = !onGround? 1f: GetSlopeDamper(-deltaPosition / Time.deltaTime, normal);
  205.             forwardMlp = Mathf.Lerp(forwardMlp, slopeDamper, Time.deltaTime * 5f);
  206.         }
  207.  
  208.         // Processing horizontal wall running
  209.         private void WallRun() {
  210.             bool canWallRun = CanWallRun();
  211.  
  212.             // Remove flickering in and out of wall-running
  213.             if (wallRunWeight > 0f && !canWallRun) wallRunEndTime = Time.time;
  214.             if (Time.time < wallRunEndTime + 0.5f) canWallRun = false;
  215.  
  216.             wallRunWeight = Mathf.MoveTowards(wallRunWeight, (canWallRun? 1f: 0f), Time.deltaTime * wallRunWeightSpeed);
  217.            
  218.             if (wallRunWeight <= 0f) {
  219.                 // Reset
  220.                 if (lastWallRunWeight > 0f) {
  221.                     transform.rotation = Quaternion.LookRotation(new Vector3(transform.forward.x, 0f, transform.forward.z), Vector3.up);
  222.                     wallNormal = Vector3.up;
  223.                 }
  224.             }
  225.             lastWallRunWeight = wallRunWeight;
  226.            
  227.             if (wallRunWeight <= 0f) return;
  228.            
  229.             // Make sure the character won't fall down
  230.             if (onGround && r.velocity.y < 0f) r.velocity = new Vector3(r.velocity.x, 0f, r.velocity.z);
  231.            
  232.             // transform.forward flattened
  233.             Vector3 f = transform.forward;
  234.             f.y = 0f;
  235.            
  236.             // Raycasting to find a walkable wall
  237.             RaycastHit velocityHit = new RaycastHit();
  238.             velocityHit.normal = Vector3.up;
  239.             Physics.Raycast(onGround? transform.position: capsule.bounds.center, f, out velocityHit, 3f, wallRunLayers);
  240.            
  241.             // Finding the normal to rotate to
  242.             wallNormal = Vector3.Lerp(wallNormal, velocityHit.normal, Time.deltaTime * wallRunRotationSpeed);
  243.            
  244.             // Clamping wall normal to max rotation angle
  245.             wallNormal = Vector3.RotateTowards(Vector3.up, wallNormal, wallRunMaxRotationAngle * Mathf.Deg2Rad, 0f);
  246.            
  247.             // Get transform.forward ortho-normalized to the wall normal
  248.             Vector3 fW = transform.forward;
  249.             Vector3 nW = wallNormal;
  250.             Vector3.OrthoNormalize(ref nW, ref fW);
  251.            
  252.             // Rotate from upright to wall normal
  253.             transform.rotation = Quaternion.Slerp(Quaternion.LookRotation(f, Vector3.up), Quaternion.LookRotation(fW, wallNormal), wallRunWeight);
  254.         }
  255.  
  256.         // Should the character be enabled to do a wall run?
  257.         private bool CanWallRun() {
  258.             if (Time.time < jumpEndTime - 0.1f) return false;
  259.             if (Time.time > jumpEndTime - 0.1f + wallRunMaxLength) return false;
  260.             if (r.velocity.y < wallRunMinVelocityY) return false;
  261.             if (userControl.state.move.magnitude < wallRunMinMoveMag) return false;
  262.            
  263.             return true;
  264.         }
  265.  
  266.         // Get the move direction of the character relative to the character rotation
  267.         private Vector3 GetMoveDirection() {
  268.             switch(moveMode) {
  269.             case MoveMode.Directional:
  270.                 moveDirection = Vector3.SmoothDamp(moveDirection, new Vector3(0f, 0f, userControl.state.move.magnitude), ref moveDirectionVelocity, smoothAccelerationTime);
  271.                 moveDirection = Vector3.MoveTowards(moveDirection, new Vector3(0f, 0f, userControl.state.move.magnitude), Time.deltaTime * linearAccelerationSpeed);
  272.                 return moveDirection * forwardMlp;
  273.             case MoveMode.Strafe:
  274.                 moveDirection = Vector3.SmoothDamp(moveDirection, userControl.state.move, ref moveDirectionVelocity, smoothAccelerationTime);
  275.                 moveDirection = Vector3.MoveTowards(moveDirection, userControl.state.move, Time.deltaTime * linearAccelerationSpeed);
  276.                 return transform.InverseTransformDirection(moveDirection);
  277.             }
  278.  
  279.             return Vector3.zero;
  280.         }
  281.  
  282.         // Rotate the character
  283.         protected virtual void Rotate() {
  284.             if (platformAngularVelocity != Vector3.zero) transform.rotation = Quaternion.Euler(platformAngularVelocity) * transform.rotation;
  285.        
  286.             float angle = GetAngleFromForward(GetForwardDirection());
  287.            
  288.             if (userControl.state.move == Vector3.zero) angle *= (1.01f - (Mathf.Abs(angle) / 180f)) * stationaryTurnSpeedMlp;
  289.  
  290.             // Rotating the character
  291.             RigidbodyRotateAround(characterAnimation.GetPivotPoint(), transform.up, angle * Time.deltaTime * turnSpeed);
  292.         }
  293.  
  294.         // Which way to look at?
  295.         private Vector3 GetForwardDirection() {
  296.             bool isMoving = userControl.state.move != Vector3.zero;
  297.  
  298.             switch (moveMode) {
  299.             case MoveMode.Directional:
  300.                 if (isMoving) return userControl.state.move;
  301.                 return lookInCameraDirection? userControl.state.lookPos - r.position: transform.forward;
  302.             case MoveMode.Strafe:
  303.                 if (isMoving) return userControl.state.lookPos - r.position;
  304.                 return lookInCameraDirection? userControl.state.lookPos - r.position: transform.forward;
  305.             }
  306.  
  307.             return Vector3.zero;
  308.         }
  309.  
  310.         protected virtual bool Jump() {
  311.             // check whether conditions are right to allow a jump:
  312.             if (!userControl.state.jump) return false;
  313.             if (userControl.state.crouch) return false;
  314.             if (!characterAnimation.animationGrounded) return false;
  315.             if (Time.time < lastAirTime + jumpRepeatDelayTime) return false;
  316.  
  317.             // Jump
  318.             onGround = false;
  319.             jumpEndTime = Time.time + 0.1f;
  320.  
  321.             Vector3 jumpVelocity = userControl.state.move * airSpeed;
  322.             r.velocity = jumpVelocity;
  323.             r.velocity += transform.up * jumpPower;
  324.  
  325.             return true;
  326.         }
  327.  
  328.         // Is the character grounded?
  329.         private void GroundCheck () {
  330.             Vector3 platformVelocityTarget = Vector3.zero;
  331.             platformAngularVelocity = Vector3.zero;
  332.             float stickyForceTarget = 0f;
  333.  
  334.             // Spherecasting
  335.             hit = GetSpherecastHit();
  336.  
  337.             //normal = hit.normal;
  338.             normal = transform.up;
  339.             groundDistance = r.position.y - hit.point.y;
  340.  
  341.             // if not jumping...
  342.             bool findGround = Time.time > jumpEndTime && r.velocity.y < jumpPower * 0.5f;
  343.  
  344.             if (findGround) {
  345.                 bool g = onGround;
  346.                 onGround = false;
  347.  
  348.                 // The distance of considering the character grounded
  349.                 float groundHeight = !g? airborneThreshold * 0.5f: airborneThreshold;
  350.  
  351.                 Vector3 horizontalVelocity = r.velocity;
  352.                 horizontalVelocity.y = 0f;
  353.                
  354.                 float velocityF = horizontalVelocity.magnitude;
  355.  
  356.                 if (groundDistance < groundHeight) {
  357.                     // Force the character on the ground
  358.                     stickyForceTarget = groundStickyEffect * velocityF * groundHeight;
  359.  
  360.                     // On moving platforms
  361.                     if (hit.rigidbody != null) {
  362.                         platformVelocityTarget = hit.rigidbody.GetPointVelocity(hit.point);
  363.                         platformAngularVelocity = Vector3.Project(hit.rigidbody.angularVelocity, transform.up);
  364.                     }
  365.  
  366.                     // Flag the character grounded
  367.                     onGround = true;
  368.                 }
  369.             }
  370.  
  371.             // Interpolate the additive velocity of the platform the character might be standing on
  372.             platformVelocity = Vector3.Lerp(platformVelocity, platformVelocityTarget, Time.deltaTime * platformFriction);
  373.  
  374.             stickyForce = stickyForceTarget;//Mathf.Lerp(stickyForce, stickyForceTarget, Time.deltaTime * 5f);
  375.  
  376.             // remember when we were last in air, for jump delay
  377.             if (!onGround) lastAirTime = Time.time;
  378.         }
  379.     }
  380. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement