Advertisement
Guest User

PlatformerPhysics

a guest
Jul 21st, 2013
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 16.18 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. public class PlatformerPhysics : MonoBehaviour
  5. {
  6.     ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  7.     //NOTE: changing these numbers will only change the default values of the script, not the values of an object the script is already applied to
  8.     //If you already applied the script to an object, you have to change the values in the inspector to get an actual change
  9.     ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  10.  
  11.     //Configurable variables regarding movement
  12.     public float accelerationWalking    = 35;       //Character acceleration while walking
  13.     public float accelerationSprinting  = 60;       //Character acceleration while sprinting
  14.     public float maxSpeedWalking        = 15;       //Maximum character speed while walking
  15.     public float maxSpeedSprinting      = 20;       //Maximum character speed while sprinting
  16.     public float moveFriction           = 0.9f;     //Friction multiplier if the character is on ground and no moving buttons are pressed
  17.     public float speedToStopAt          = 5.0f;     //If the character's speed falls below this while being on the ground, the character stops
  18.     public float airFriction            = 0.98f;    //Air friction is always applied to the character
  19.     public float maxGroundWalkingAngle  = 30.0f;    //Maximum angle the ground can be for the character to still be able to jump off and not slide down
  20.     public float crouchColliderScale    = 0.5f;     //Multiplier to the Y-size of the collider when crouching
  21.     public float crouchedAccelMultiplier= 0.1f;     //Maximum speed factor while crouched
  22.  
  23.     //Configurable variables regarding jumping
  24.     public float jumpVelocity           = 12;       //Velocity while jumping
  25.     public int jumpTimeFrames           = 15;       //Amount of frames the jump can be held, the player can release the jump button earlier for a lower jump
  26.     public float crouchDownwardForce    = 20;       //Extra gravity added to the character if the crouch button is pressed
  27.     public bool canDoubleJump           = false;    //Whether the character can double jump or not
  28.     public bool canWallJump             = true;     //Whether the character can do a wall jump or not
  29.     public float wallJumpVelocity       = 15;       //Sideways velocity when doing a walljump
  30.     public float wallStickyness         = 0.5f;     //Amount of seconds the player has to move away from a wall to let go of it. The idea behind this is that players can press the opposite direction to prepare for a walljump without immediately letting go of the wall
  31.     public float gravityMultiplier      = 3.5f;     //Amount of gravity applied to the character compared to the rest of the physics world
  32.  
  33.  
  34.  
  35.     //Private variables, no need to configure these
  36.     bool mOnGround                      = false;    //Are we on the ground or not?
  37.     bool mSprinting                     = false;    //Are we sprinting or not?
  38.     bool mCrouching                     = false;    //Are we crouching or not?
  39.     bool mTryingToUncrouch              = false;    //Are we trying to get out of crouch at the moment?
  40.     Vector3 mGroundDirection            = Vector3.right; //The direction of the ground we are standing on
  41.  
  42.     bool mInJump                        = false;    //Are we in a jump
  43.     bool mJumpPressed                   = false;    //Was the jump button still pressed this frame?
  44.     bool mSecondJumpLeft                = true;     //Do we have our second jump left (for double jump)
  45.     int mJumpFramesLeft                 = 0;        //Amount of frames left that we can hold the jump button to jump higher
  46.  
  47.     bool mOnWall                        = false;    //Are we on a wall? (being on the ground while against a wall will keep this false)
  48.     bool mWallIsOnRightSide             = false;    //Is the wall on the right side of us?
  49.     float mWallStickynessLeft           = 0;        //Amount of seconds left the player needs to press the opposite direction of the wall to let go of it
  50.  
  51.     float mStoppingForce                = 0;        //This variable holds whether or not a player was moving this frame, if a player doesnt press move, the character will slowly stop
  52.     bool mGoingRight                    = true;     //Are we going to the right?
  53.    
  54.     float mCharacterHeight;                         //Character bounding box height
  55.     float mCharacterWidth;                          //Character bounding box width
  56.  
  57.     Vector3 mStartPosition;                         //Position used for respawning
  58.  
  59.     float origColliderCenterY;                      //Original sizes of collision box
  60.     float origColliderSizeY;
  61.  
  62.     public void Start ()
  63.     {
  64.         mStartPosition = transform.position;
  65.         RecalcBounds();
  66.         origColliderCenterY = ((BoxCollider)collider).center.y;
  67.         origColliderSizeY = ((BoxCollider)collider).size.y;
  68.  
  69.         if (!rigidbody)
  70.             Debug.LogError("The PlatformerPhysics component requires a rigidbody.");
  71.  
  72.         if (rigidbody.useGravity)
  73.             Debug.LogWarning("You shouldn't turn on 'use gravity' on the platformer rigidbody. This will give strange behaviour.");
  74.     }
  75.  
  76.     public void Reset() //resets all private variables to their starting values
  77.     {
  78.         mOnGround = false;
  79.         mSprinting = false;
  80.         StopCrouch();
  81.         mGroundDirection = Vector3.right;
  82.         mInJump = false;
  83.         mJumpPressed = false;
  84.         mSecondJumpLeft = true;
  85.         mJumpFramesLeft = 0;
  86.         mOnWall = false;
  87.         mWallIsOnRightSide = false;
  88.         mStoppingForce = 0;
  89.         transform.position = mStartPosition;
  90.         rigidbody.velocity = Vector3.zero;
  91.         mGoingRight = true;
  92.     }
  93.  
  94.  
  95.     //Player update
  96.     void FixedUpdate ()
  97.     {
  98.         UpdateWallInfo();           //Check the sides to see if we are against a wall
  99.         UpdateGroundInfo();         //Check below to see if we are on the ground
  100.  
  101.         UpdateJumping();
  102.         UpdateCrouching();
  103.         ApplyGravity();
  104.         ApplyMovementFriction();
  105.     }
  106.  
  107.  
  108.     //Called when the player presses a walking button (direction -1.0f is full left, and 1.0f is full right)
  109.     public void Walk(float direction)
  110.     {
  111.         //See if we need to stick to a wall
  112.         if (mOnWall && mWallStickynessLeft > 0)
  113.         {
  114.             //remove time from the stickyness left
  115.             if ((mWallIsOnRightSide && direction < 0) ||
  116.                 (!mWallIsOnRightSide && direction > 0))
  117.             {
  118.                 mWallStickynessLeft -= Time.fixedDeltaTime;
  119.             }
  120.  
  121.             //see if we just released the wall
  122.             if (mWallStickynessLeft <= 0)
  123.             {
  124.                 SendAnimMessage("ReleasedWall");
  125.             }
  126.  
  127.             return;
  128.         }
  129.  
  130.         //get an acceleration amount
  131.         float accel = accelerationWalking;
  132.         if (mSprinting)
  133.             accel = accelerationSprinting;
  134.         if (mCrouching && mOnGround)
  135.             accel = accelerationWalking * crouchedAccelMultiplier;
  136.  
  137.         //apply actual force
  138.         rigidbody.AddForce(mGroundDirection * direction * accel, ForceMode.Acceleration);
  139.  
  140.         mStoppingForce = 1 - Mathf.Abs(direction);
  141.  
  142.         if (direction < 0 && mGoingRight)
  143.         {
  144.             mGoingRight = false;
  145.             SendAnimMessage("GoLeft");
  146.         }
  147.         if (direction > 0 && !mGoingRight)
  148.         {
  149.             mGoingRight = true;
  150.             SendAnimMessage("GoRight");
  151.         }
  152.     }
  153.  
  154.  
  155.     //Called when the player holds down the jump key
  156.     public void Jump()
  157.     {
  158.         mJumpPressed = true;
  159.  
  160.         //See if we can start a jump
  161.         if (mJumpFramesLeft == 0 && !mInJump && !mCrouching)
  162.         {
  163.             if (!mOnGround && mSecondJumpLeft && canDoubleJump) //Second jump
  164.             {
  165.                 mSecondJumpLeft = false;
  166.  
  167.                 mJumpFramesLeft = jumpTimeFrames;
  168.                 mInJump = true;
  169.  
  170.                 SendAnimMessage("StartedJump");
  171.             }
  172.  
  173.             if (mOnGround || mOnWall) //First jump
  174.             {
  175.                 mSecondJumpLeft = true;
  176.  
  177.                 mJumpFramesLeft = jumpTimeFrames;
  178.                 mInJump = true;
  179.  
  180.                 if (mOnWall) //A wall jump needs sideways velocity as well
  181.                 {
  182.                     if (mWallIsOnRightSide)
  183.                         rigidbody.velocity += wallJumpVelocity * Vector3.left;
  184.                     else
  185.                         rigidbody.velocity += wallJumpVelocity * Vector3.right;
  186.  
  187.                     SendAnimMessage("StartedWallJump");
  188.                 }
  189.                 else
  190.                 {
  191.                     SendAnimMessage("StartedJump");
  192.                 }
  193.             }
  194.         }
  195.  
  196.         //Check if we are in the middle of a jump
  197.         if(mJumpFramesLeft != 0)
  198.         {
  199.             Vector3 vel = rigidbody.velocity;
  200.             vel.y = jumpVelocity;
  201.             rigidbody.velocity = vel;
  202.         }
  203.     }
  204.  
  205.  
  206.     //Called when the player presses the crouch button
  207.     public void Crouch()
  208.     {
  209.         if (!mCrouching) //make sure we aren't crouching
  210.         {
  211.             mCrouching = true;
  212.  
  213.             CrouchCollider();
  214.  
  215.             RecalcBounds();
  216.  
  217.             SendAnimMessage("StartedCrouching");
  218.         }
  219.     }
  220.  
  221.     public void CrouchCollider()
  222.     {
  223.         //change collider scale    
  224.         BoxCollider myCollider = (BoxCollider)collider;
  225.  
  226.         Vector3 center = myCollider.center;
  227.         Vector3 size = myCollider.size;
  228.  
  229.         size.y = origColliderSizeY * crouchColliderScale;
  230.         center.y = origColliderCenterY * crouchColliderScale;
  231.  
  232.         myCollider.size = size;
  233.         myCollider.center = center;
  234.     }
  235.  
  236.     //Called when the player releases the crouch button
  237.     public void UnCrouch()
  238.     {
  239.         mTryingToUncrouch = true; //try to uncrouch if possible
  240.     }
  241.  
  242.     //Called when actually going out of crouch
  243.     void StopCrouch()
  244.     {
  245.         mTryingToUncrouch = false;
  246.         mCrouching = false;
  247.         UnCrouchCollider();
  248.         RecalcBounds();
  249.         SendAnimMessage("StoppedCrouching");
  250.     }
  251.  
  252.     public void UnCrouchCollider()
  253.     {
  254.         //reset collider scale
  255.         BoxCollider myCollider = (BoxCollider)collider;
  256.  
  257.         Vector3 center = myCollider.center;
  258.         Vector3 size = myCollider.size;
  259.  
  260.         size.y = origColliderSizeY;
  261.         center.y = origColliderCenterY;
  262.  
  263.         myCollider.size = size;
  264.         myCollider.center = center;
  265.     }
  266.  
  267.     //Called when the player presses the sprint button
  268.     public void StartSprint()
  269.     {
  270.         mSprinting = true;
  271.         SendAnimMessage("StartedSprinting");
  272.     }
  273.  
  274.     //Called when the player releases the sprint button
  275.     public void StopSprint()
  276.     {
  277.         mSprinting = false;
  278.         SendAnimMessage("StoppedSprinting");
  279.     }
  280.  
  281.  
  282.     void ApplyGravity()
  283.     {
  284.         if (!mOnGround) //basic gravity, only applied when we are not on the ground
  285.         {
  286.             rigidbody.AddForce(Physics.gravity * gravityMultiplier, ForceMode.Acceleration);
  287.         }
  288.  
  289.         if (mCrouching) //extra gravity for when we are holding crouch
  290.         {
  291.             rigidbody.AddForce(Vector3.down * crouchDownwardForce, ForceMode.Acceleration);
  292.         }
  293.     }
  294.  
  295.     void UpdateCrouching()
  296.     {
  297.         if (mTryingToUncrouch && CanUnCrouch())
  298.         {
  299.             StopCrouch();
  300.         }
  301.     }
  302.  
  303.  
  304.     void UpdateJumping()
  305.     {
  306.         if (!mJumpPressed && mInJump) //see if we released the jump button
  307.         {
  308.             mJumpFramesLeft = 0;
  309.             mInJump = false;
  310.         }
  311.         mJumpPressed = false;
  312.  
  313.         if (mJumpFramesLeft != 0)
  314.             mJumpFramesLeft--;
  315.     }
  316.  
  317.     void ApplyMovementFriction()
  318.     {
  319.         Vector3 velocity = rigidbody.velocity;
  320.  
  321.         //Apply ground friction
  322.         if (mOnGround && mStoppingForce > 0.0f)
  323.         {
  324.             Vector3 velocityInGroundDir = Vector3.Dot(velocity, mGroundDirection) * mGroundDirection; //project velocity on ground direction
  325.             Vector3 newVelocityInGroundDir = velocityInGroundDir * Mathf.Lerp(1.0f, moveFriction, mStoppingForce); //apply ground friction on velocity
  326.             velocity -= (velocityInGroundDir - newVelocityInGroundDir); //apply to actual velocity
  327.         }
  328.  
  329.         //Apply air friction
  330.         velocity *= airFriction;
  331.  
  332.         float absSpeed = Mathf.Abs(velocity.x);
  333.  
  334.         //Apply maximum speed
  335.         float maxSpeed = maxSpeedWalking;
  336.         if (mSprinting)
  337.             maxSpeed = maxSpeedSprinting;
  338.  
  339.         if (absSpeed > maxSpeed)
  340.             velocity.x *= maxSpeed / absSpeed;
  341.  
  342.         //Apply minimum speed
  343.         if (absSpeed < speedToStopAt && mStoppingForce == 1.0f)
  344.             velocity.x = 0;
  345.  
  346.         //Apply final velicty to rigid body
  347.         rigidbody.velocity = velocity;
  348.  
  349.         mStoppingForce = 1.0f; //if no walking is done this frame, the character will start stopping next frame
  350.     }
  351.  
  352.  
  353.     void UpdateGroundInfo()
  354.     {
  355.         //We will trace 2 rays from the front and back of the character both downwards, to see if there is any ground under the character's feet
  356.  
  357.         float epsilon = 0.05f; //the amount the ray will trace below the feet of the character to check if there is ground
  358.         float extraHeight = mCharacterHeight * 0.75f;
  359.         float halfPlayerWidth = mCharacterWidth * 0.49f;
  360.  
  361.         //Origins of the ray
  362.         Vector3 origin1 = transform.position + Vector3.right * halfPlayerWidth + Vector3.up * extraHeight;
  363.         Vector3 origin2 = transform.position + Vector3.left * halfPlayerWidth + Vector3.up * extraHeight;
  364.         Vector3 direction = Vector3.down;
  365.         RaycastHit hit;
  366.  
  367.         //Actual physic traces
  368.         if (Physics.Raycast(origin1, direction, out hit) && (hit.distance < extraHeight + epsilon))
  369.             HitGround(origin1, hit);
  370.         else if (Physics.Raycast(origin2, direction, out hit) && (hit.distance < extraHeight + epsilon))
  371.             HitGround(origin2, hit);
  372.         else
  373.         {
  374.             mOnGround = false; //We didnt hit anything, so we are in the air
  375.             mGroundDirection = Vector3.right;
  376.         }
  377.     }
  378.  
  379.     void HitGround(Vector3 origin, RaycastHit hit)
  380.     {
  381.         //Calculate the angle of the ground we are standing on based on the normal
  382.         mGroundDirection = new Vector3(hit.normal.y, -hit.normal.x, 0);
  383.         float groundAngle = Vector3.Angle(mGroundDirection, new Vector3(mGroundDirection.x, 0, 0));
  384.  
  385.         //Check if we can walk on this angle of ground
  386.         if (groundAngle <= maxGroundWalkingAngle)
  387.         {
  388.             if(!mOnGround)
  389.                 SendAnimMessage("LandedOnGround");
  390.  
  391.             Debug.DrawLine(hit.point+Vector3.up, hit.point, Color.green);
  392.             Debug.DrawLine(hit.point, hit.point + mGroundDirection, Color.magenta);
  393.             mOnGround = true;
  394.             mOnWall = false;
  395.         }
  396.         else
  397.         {
  398.             Debug.DrawLine(hit.point, hit.point + mGroundDirection, Color.grey);
  399.         }
  400.    
  401.         return;
  402.     }
  403.  
  404.  
  405.     void UpdateWallInfo()
  406.     {
  407.         //We will trace 2 rays from the center of the character to the left and right, to see if we are on any wall
  408.  
  409.         float epsilon = 0.05f;
  410.         float halfPlayerWidth = mCharacterWidth * 0.5f;
  411.  
  412.         Vector3 origin = transform.position + Vector3.up * mCharacterHeight * 0.5f;
  413.         RaycastHit hit;
  414.  
  415.         //Raycast going to the right
  416.         if (Physics.Raycast(origin, Vector3.right, out hit))
  417.         {
  418.             if (hit.distance < halfPlayerWidth + epsilon && !mOnGround)
  419.             {
  420.                 //remove collider penetration
  421.                 transform.position += Vector3.left * (halfPlayerWidth - hit.distance);
  422.  
  423.                 HitWall(true);
  424.                 Debug.DrawLine(origin, hit.point, Color.yellow);
  425.                 return;
  426.             }
  427.         }
  428.  
  429.         //Raycast going to the left
  430.         if (Physics.Raycast(origin, Vector3.left, out hit))
  431.         {
  432.             if (hit.distance < halfPlayerWidth + epsilon && !mOnGround)
  433.             {
  434.                 //remove collider penetration
  435.                 transform.position += Vector3.right * (halfPlayerWidth - hit.distance);
  436.  
  437.                 HitWall(false);
  438.                 Debug.DrawLine(origin, hit.point, Color.yellow);
  439.                 return;
  440.             }
  441.         }
  442.  
  443.         //We hit no wall, but we used to be on the wall, this means we just released
  444.         if (mOnWall)
  445.         {
  446.             SendAnimMessage("ReleasedWall");
  447.         }
  448.  
  449.         mWallStickynessLeft = 0;
  450.         mOnWall = false;
  451.     }
  452.  
  453.     void HitWall(bool onRightSide)
  454.     {
  455.         mWallIsOnRightSide = onRightSide;
  456.         mGoingRight = mWallIsOnRightSide;
  457.  
  458.         if (!mOnWall)
  459.         {
  460.             rigidbody.velocity = new Vector3(0, rigidbody.velocity.y, 0); //Remove horizontal speed
  461.             mWallStickynessLeft = wallStickyness;
  462.             mOnWall = true;
  463.             SendAnimMessage("LandedOnWall");
  464.         }
  465.  
  466.         mOnWall = true;
  467.     }
  468.  
  469.     bool CanUnCrouch()
  470.     {
  471.         //We will trace 2 rays from the front and back of the character both upwards, to see if we can uncrouch
  472.         float epsilon = 0.05f; //the amount the ray will trace below the feet of the character to check if there is ground
  473.         float origCharHeight = origColliderSizeY;
  474.         float extraHeight = origCharHeight * 0.75f;
  475.         float halfPlayerWidth = mCharacterWidth * 0.49f;
  476.  
  477.         //Origins of the ray
  478.         Vector3 origin1 = transform.position + Vector3.right * halfPlayerWidth + Vector3.up * (origCharHeight - extraHeight);
  479.         Vector3 origin2 = transform.position + Vector3.left * halfPlayerWidth + Vector3.up * (origCharHeight - extraHeight);
  480.         Vector3 direction = Vector3.up;
  481.         RaycastHit hit;
  482.  
  483.         bool canUncrouch = true;
  484.  
  485.         //Actual physic traces
  486.         if (Physics.Raycast(origin1, direction, out hit) && (hit.distance < extraHeight + epsilon))
  487.             canUncrouch = false;
  488.         else if (Physics.Raycast(origin2, direction, out hit) && (hit.distance < extraHeight + epsilon))
  489.             canUncrouch = false;
  490.  
  491.         return canUncrouch;
  492.     }
  493.  
  494.     //send a message to all other scripts to trigger for example the animations
  495.     void SendAnimMessage(string message)
  496.     {
  497.         SendMessage(message, SendMessageOptions.DontRequireReceiver);
  498.     }
  499.  
  500.     void RecalcBounds()
  501.     {
  502.         mCharacterHeight = collider.bounds.size.y;
  503.         mCharacterWidth = collider.bounds.size.x;
  504.     }
  505.  
  506.     public void SetRespawnPoint(Vector3 spawnPoint)
  507.     {
  508.         mStartPosition = spawnPoint;
  509.     }
  510.    
  511.     //getter functions
  512.     public bool IsWallOnRightSide() { return mWallIsOnRightSide; }
  513.     public bool IsCrouching() { return mCrouching; }
  514.     public bool IsOnWall() { return mOnWall; }
  515.     public bool IsOnGround() { return mOnGround; }
  516.     public bool IsSprinting() { return mSprinting; }
  517. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement