daily pastebin goal
30%
SHARE
TWEET

MovingEntity.cs

mvaganov Jun 14th, 2017 (edited) 504 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using UnityEngine;
  2.  
  3. /// <summary>A custom Unity3D character controller, useful for player characters and AI characters
  4. /// Latest version at: https://pastebin.com/xFUD4tk2
  5. /// The complementary MOvingEntity_CameraInput script is at: https://pastebin.com/pC0Ddjsi </summary>
  6. /// <description>MIT License - TL;DR - This code is free, don't bother me about it!</description>
  7. /// <author email="mvaganov@hotmail.com">Michael Vaganov</author>
  8. public class MovingEntity : MovingEntityBase {
  9.     #region Public API
  10.     /// <summary>true if on the ground</summary>
  11.     [HideInInspector]
  12.     public bool IsStableOnGround { get; protected set; }
  13.     /// <summary>if OnCollision event happens with ground, this is true</summary>
  14.     [HideInInspector]
  15.     public bool IsCollidingWithGround { get; protected set; }
  16.     private bool wasStableOnGroundLastFrame;
  17.     public enum GravityState { none, useGravity, noFalling }
  18.     [Tooltip("if false, disables gravity's effects, and enables flying")]
  19.     public GravityState gravityApplication = GravityState.useGravity;
  20.     /// <summary>the ground-plane normal, used to determine which direction 'forward' is when it comes to movement on the ground</summary>
  21.     public enum HowToDealWithGround {none, withPlaceholder, withParenting}
  22.     [Tooltip("how the player should stay stable if the ground is moving\n"+
  23.         "* none: don't bother trying to be stable\n"+
  24.         "* withPlaceholder: placeholder transform keeps track\n"+
  25.         "* withParenting: player is parented to ground (not safe if ground is scaled)\n")]
  26.     public HowToDealWithGround stickToGround = HowToDealWithGround.withPlaceholder;
  27.     [HideInInspector]
  28.     public Vector3 GroundNormal { get; protected set; }
  29.     [HideInInspector]
  30.     /// <summary>What object is being stood on</summary>
  31.     public GameObject StandingOnObject { get; protected set; }
  32.     /// <summary>the angle of standing, compared to gravity, used to determine if the player can reliably walk on this surface (it might be too slanted)</summary>
  33.     [HideInInspector]
  34.     public float StandAngle { get; protected set; }
  35.     [Tooltip("The maximum angle the player can move forward at while standing on the ground")]
  36.     public float MaxWalkAngle = 45;
  37.     /// <summary>if very far from ground, this value is infinity</summary>
  38.     [HideInInspector]
  39.     public float HeightFromGround { get; protected set; }
  40.     [HideInInspector]
  41.     public Collider CollideBox { get; protected set; }
  42.     /// <summary>expected distance from the center of the collider to the ground. auto-populates based on Box of Capsule collider</summary>
  43.     [HideInInspector]
  44.     public float ExpectedHeightFromGround;
  45.     /// <summary>expected distance from the center of the collider to a horizontal edge. auto-populates based on Box of Capsule collider</summary>
  46.     [HideInInspector]
  47.     public float ExpectedHorizontalRadius;
  48.     /// <summary>how much downward velocity the player is experiencing. non-zero when jumping or falling.</summary>
  49.     [HideInInspector]
  50.     public float VerticalVelocity { get; protected set; }
  51.  
  52.     public Vector3 GetUpOrientation() { return -gravity.dir; }
  53.     public Vector3 GetUpBodyDirection() { return GroundNormal; }
  54.     /// <summary>Used for temporary disabling of controls. Helpful for cutscenes/dialog</summary>
  55.     public void Toggle() { this.enabled = !this.enabled; }
  56.     public void RandomColor() { GetComponent<Renderer>().material.color = Random.ColorHSV(); }
  57.     /// <summary>Calculates correct look vectors given a general forward and an agreeable up</summary>
  58.     public static void CalculatePlanarMoveVectors(Vector3 generalForward, Vector3 upVector, out Vector3 forward, out Vector3 right) {
  59.         right = Vector3.Cross(upVector, generalForward).normalized;
  60.         forward = Vector3.Cross(right, upVector).normalized;
  61.     }
  62.     public float DistanceTo(Vector3 loc) {
  63.         return Vector3.Distance(transform.position, loc) - ExpectedHorizontalRadius;
  64.     }
  65.     public float DistanceTo(Transform mob) {
  66.         float otherRad = mob.transform.localScale.z;
  67.         Collider c = mob.GetComponent<Collider>();
  68.         if(c != null) { otherRad *= c.bounds.extents.z; }
  69.         return Vector3.Distance(transform.position, mob.transform.position) - (ExpectedHorizontalRadius + otherRad);
  70.     }
  71.     public Vector3 GetVelocity() { return rb.velocity; }
  72.     /// <summary>if a null pointer happens while trying to access a rigidbody variable, call this first.</summary>
  73.     public void EnsureRigidBody() {
  74.         if(rb == null) {
  75.             rb = GetComponent<Rigidbody>();
  76.             if(!rb) { rb = gameObject.AddComponent<Rigidbody>(); }
  77.             rb.useGravity = false; rb.freezeRotation = true;
  78.         }
  79.     }
  80.     #endregion // Public API
  81.     #region Core Controller Logic
  82.     /// <summary>used when the player controller needs to stay on a moving platform or vehicle</summary>
  83.     private Transform transformOnPlatform;
  84.     /// <summary>true if player is walking on ground that is too steep.</summary>
  85.     public bool tooSteep { get; protected set; }
  86.     [HideInInspector]
  87.     public bool AutomaticallyFollowPositionOnPlatform = true;
  88.     /// <summary>where the 'leg' raycast comes down from. changes if the leg can't find footing.</summary>
  89.     private Vector3 legOffset;
  90.     /// <summary>cache the data structure with the object, to improve performance</summary>
  91.     private RaycastHit rayDownHit = new RaycastHit();
  92.     /// <summary>useful to call in LateUpdate for camera</summary>
  93.     public void FollowPositionOnPlatform() {
  94.         // if on a platform, update position on the platform based on velocity
  95.         if(transformOnPlatform != null && stickToGround == HowToDealWithGround.withPlaceholder &&
  96.             (IsStableOnGround || wasStableOnGroundLastFrame)) {
  97.             transform.position = transformOnPlatform.position + rb.velocity * Time.deltaTime;
  98.         }
  99.     }
  100.     void StandLogic() {
  101.         // this might be better done in a LateUpdate for a camera, to make smoother motion
  102.         if(AutomaticallyFollowPositionOnPlatform) { FollowPositionOnPlatform(); }
  103.         wasStableOnGroundLastFrame = IsStableOnGround;
  104.         Vector3 hip = (legOffset!=Vector3.zero) ? transform.TransformPoint(legOffset) : transform.position;
  105.         const float epsilon = 0.0625f;
  106.         float lengthOfLeg = ExpectedHeightFromGround+ExpectedHorizontalRadius * 5;
  107.         // use a raycast to check if we're on the ground
  108.         CollideBox.enabled = false;
  109.         // if we're grounded enough (close enough to the ground)
  110.         if(Physics.Raycast(hip, /*gravity.dir*/ -transform.up, out rayDownHit, lengthOfLeg)) {
  111.             // record some important details about the space we're standing at
  112.             GroundNormal = rayDownHit.normal;
  113.             StandAngle = Vector3.Angle(GetUpOrientation(), GroundNormal);
  114.             tooSteep = (StandAngle > MaxWalkAngle);
  115.             HeightFromGround = rayDownHit.distance;
  116.             if(IsCollidingWithGround || HeightFromGround < ExpectedHeightFromGround + epsilon) {
  117.                 IsStableOnGround = true;
  118.                 StandingOnObject = rayDownHit.collider.gameObject;
  119.                 switch(stickToGround){
  120.                 case HowToDealWithGround.withPlaceholder:
  121.                     if (transformOnPlatform == null) {
  122.                         transformOnPlatform = (new GameObject ("<where " + name + " stands>")).transform;
  123.                     }
  124.                     transformOnPlatform.SetParent (StandingOnObject.transform);
  125.                     break;
  126.                 case HowToDealWithGround.withParenting:
  127.                     transform.SetParent (StandingOnObject.transform);
  128.                     break;
  129.                 }
  130.                 legOffset = Vector3.zero;
  131.                 float downwardV = Vector3.Dot(rb.velocity, gravity.dir); // check if we're falling.
  132.                 if(downwardV > 0) {
  133.                     rb.velocity = rb.velocity - downwardV * gravity.dir; // stop falling, we're on the ground.
  134.                 }
  135.                 if(HeightFromGround < ExpectedHeightFromGround - epsilon) {
  136.                     rb.velocity += GetUpOrientation() * epsilon; // if we're not standing tall enough, edge up a little.
  137.                 }
  138.             } else {
  139.                 IsStableOnGround = false;
  140.             }
  141.         } else {
  142.             HeightFromGround = float.PositiveInfinity;
  143.             IsStableOnGround = false;
  144.         }
  145.         if(!IsStableOnGround) { // if we're not grounded enough, mark it
  146.             IsStableOnGround = false;
  147.             StandingOnObject = null;
  148.             tooSteep = false;
  149.             switch(stickToGround){
  150.             case HowToDealWithGround.withPlaceholder:
  151.                 if (transformOnPlatform != null) {
  152.                     transformOnPlatform.SetParent (null);
  153.                 }
  154.                 break;
  155.             }
  156.             // if we couldn't find ground with our leg here, try another location.
  157.             legOffset = new Vector3(
  158.                 Random.Range(-1.0f, 1.0f) * ExpectedHorizontalRadius, 0,
  159.                 Random.Range(-1.0f, 1.0f) * ExpectedHorizontalRadius);
  160.         }
  161.         CollideBox.enabled = true;
  162.     }
  163.     public Vector3 NormalizeDirectionTo(Vector3 direction, Vector3 groundNormal) {
  164.         float amountOfVertical = Vector3.Dot (direction, groundNormal);
  165.         direction -= groundNormal * amountOfVertical;
  166.         if (direction != Vector3.zero) {  direction.Normalize (); }
  167.         return direction;
  168.     }
  169.     protected override void ApplyMove(float acceleration) {
  170.         // validate that the agent should be able to walk at this angle
  171.         float walkAngle = 90 - Vector3.Angle(GetUpOrientation(), rb.velocity);
  172.         if(walkAngle > MaxWalkAngle || StandAngle > MaxWalkAngle) { IsStableOnGround = false; }
  173.         // only apply gravity if the agent can't walk.
  174.         if(!IsStableOnGround) { rb.velocity += gravity.GetGravityPower() * gravity.dir * Time.deltaTime; }
  175.         VerticalVelocity = Vector3.Dot(rb.velocity, gravity.dir); // remember vertical velocity (for jumping!)
  176.         // prevent moving up hill if the hill is too steep (videogame logic!)
  177.         if(tooSteep && IsMovingIntentionally) {
  178.             Vector3 acrossSlope = Vector3.Cross(GroundNormal, -gravity.dir).normalized;
  179.             Vector3 intoSlope = Vector3.Cross(acrossSlope, -gravity.dir).normalized;
  180.             float upHillAmount = Vector3.Dot(MoveDirection, intoSlope);
  181.             if(upHillAmount > 0) {
  182.                 MoveDirection -= intoSlope * upHillAmount;
  183.                 MoveDirection.Normalize ();
  184.             }
  185.         }
  186.         base.ApplyMove(acceleration);
  187.         if(!IsStableOnGround) {
  188.             rb.velocity -= gravity.dir * Vector3.Dot(rb.velocity, gravity.dir); // subtract donward-force from move speed
  189.             rb.velocity += gravity.dir * VerticalVelocity; // re-apply vertical velocity from gravity/jumping
  190.         }
  191.     }
  192.     public override void MoveLogic() {
  193.         if (IsBrakeOn && (IsStableOnGround || gravityApplication != GravityState.useGravity)) {
  194.             ApplyBrakes (Acceleration);
  195.         }
  196.         IsMovingIntentionally = MoveDirection != Vector3.zero;
  197.         if(gravityApplication == GravityState.useGravity) {
  198.             if (IsMovingIntentionally) {
  199.                 // keeps motion aligned to ground, also makes it easy to put on the brakes
  200.                 MoveDirection = NormalizeDirectionTo (MoveDirection, GroundNormal);
  201.                 IsMovingIntentionally = MoveDirection != Vector3.zero;
  202.             }
  203.             ApplyMove (Acceleration);
  204.             jump.FixedUpdate(this);
  205.         } else if(!IsBrakeOn) {
  206.             base.ApplyMove(Acceleration);
  207.         }
  208.         // turn body as needed
  209.         if(AutomaticallyTurnToFaceLookDirection && TurnSpeed != 0) { UpdateFacing (); }
  210.     }
  211.     public override void UpdateFacing() {
  212.         Vector3 correctUp;      // TODO make this a member variable, and only assign here if it's zero? or just set it to ground normal?
  213.         if(!IsCollidingWithGround && !IsStableOnGround && float.IsInfinity(HeightFromGround)) {
  214.             correctUp = GetUpOrientation();
  215.         } else {
  216.             correctUp = (GroundNormal != Vector3.zero && gravityApplication != GravityState.none) ? GroundNormal:transform.up;
  217.         }
  218.         // turn IF needed
  219.         if ((LookDirection != Vector3.zero && LookDirection != transform.forward) || (correctUp != transform.up)) {
  220.             Vector3 correctRight, correctForward;
  221.             correctRight = Vector3.Cross (correctUp, LookDirection);
  222.             if (correctRight == Vector3.zero) {
  223.                 correctRight = Vector3.Cross (correctUp, (LookDirection != transform.forward)?transform.forward:transform.up);
  224.             }
  225.             correctForward = Vector3.Cross (correctRight, correctUp).normalized;
  226.             TurnToFace (correctForward, correctUp);
  227.         }
  228.     }
  229.     protected override void EnforceSpeedLimit() {
  230.         if(gravityApplication == GravityState.useGravity) {
  231.             float actualSpeed = Vector3.Dot(MoveDirection, rb.velocity);
  232.             if(actualSpeed > MoveSpeed) {
  233.                 rb.velocity -= MoveDirection * actualSpeed;
  234.                 rb.velocity += MoveDirection * MoveSpeed;
  235.             }
  236.         } else {
  237.             base.EnforceSpeedLimit();
  238.         }
  239.     }
  240.     #endregion // Core Controller Logic
  241.     #region Steering Behaviors
  242.     protected override Vector3 CalculatePositionForAutomaticMovement(Vector3 position) {
  243.         if (gravityApplication == GravityState.useGravity && IsStableOnGround) {
  244.             Vector3 delta = position - transform.position;
  245.             float notOnGround = Vector3.Dot (GroundNormal, delta);
  246.             delta -= GroundNormal * notOnGround;
  247.             if (delta == Vector3.zero) {
  248.                 return Vector3.zero;
  249.             }
  250.             position = transform.position + delta;
  251.         }
  252.         return position;
  253.     }
  254.     #endregion // Steering Behaviors
  255.     #region Jumping
  256.     public Jumping jump = new Jumping();
  257.  
  258.     [System.Serializable]
  259.     public class Jumping {
  260.         public float minJumpHeight = 0.25f, maxJumpHeight = 1;
  261.         [Tooltip("How long the jump button must be pressed to jump the maximum height")]
  262.         public float fullJumpPressDuration = 0.5f;
  263.         [Tooltip("for double-jumping, put a 2 here. To eliminate jumping, put a 0 here.")]
  264.         public int maxJumps = 1;
  265.         [HideInInspector]
  266.         public float SecondsToPressJump;
  267.         protected float currentJumpVelocity, heightReached, heightReachedTotal, timeHeld, targetHeight;
  268.         protected bool impulseActive, inputHeld, peaked = false;
  269.         [Tooltip("if false, double jumps won't 'restart' a jump, just add jump velocity")]
  270.         private bool jumpStartResetsVerticalMotion = true;
  271.         public int jumpsSoFar { get; protected set; }
  272.         /// <returns>if this instance is trying to jump</returns>
  273.         public bool IsJumping() { return inputHeld; }
  274.         /// <summary>pretends to hold the jump button for the specified duration</summary>
  275.         /// <param name="jumpHeldDuration">Jump held duration.</param>
  276.         public void FixedUpdate(MovingEntity p) {
  277.             if (inputHeld = (SecondsToPressJump > 0)) { SecondsToPressJump -= Time.deltaTime; }
  278.             if(impulseActive && !inputHeld) { impulseActive = false; }
  279.             if(!inputHeld) return;
  280.             // check stable footing for the jump
  281.             if(p.IsStableOnGround) {
  282.                 jumpsSoFar = 0;
  283.                 heightReached = 0;
  284.                 currentJumpVelocity = 0;
  285.                 timeHeld = 0;
  286.             }
  287.             // calculate the jump
  288.             float gForce = p.gravity.GetGravityPower() * p.rb.mass;
  289.             Vector3 jump_force = Vector3.zero, jumpDirection = -p.gravity.dir;
  290.             // if the user wants to jump, and is allowed to jump again
  291.             if(!impulseActive && (jumpsSoFar < maxJumps)) {
  292.                 heightReached = 0;
  293.                 timeHeld = 0;
  294.                 jumpsSoFar++;
  295.                 targetHeight = minJumpHeight * p.rb.mass;
  296.                 float velocityRequiredToJump = Mathf.Sqrt(targetHeight * 2 * gForce);
  297.                 // cancel out current jump/fall forces
  298.                 if(jumpStartResetsVerticalMotion) {
  299.                     float motionInVerticalDirection = Vector3.Dot(jumpDirection, p.rb.velocity);
  300.                     jump_force -= (motionInVerticalDirection * jumpDirection) / Time.deltaTime;
  301.                 }
  302.                 // apply proper jump force
  303.                 currentJumpVelocity = velocityRequiredToJump;
  304.                 peaked = false;
  305.                 jump_force += (jumpDirection * currentJumpVelocity) / Time.deltaTime;
  306.                 impulseActive = true;
  307.             } else
  308.             // if a jump is happening      
  309.             if(currentJumpVelocity > 0) {
  310.                 // handle jump height: the longer you hold jump, the higher you jump
  311.                 if(inputHeld) {
  312.                     timeHeld += Time.deltaTime;
  313.                     if(timeHeld >= fullJumpPressDuration) {
  314.                         targetHeight = maxJumpHeight;
  315.                         timeHeld = fullJumpPressDuration;
  316.                     } else {
  317.                         targetHeight = minJumpHeight + ((maxJumpHeight-minJumpHeight) * timeHeld / fullJumpPressDuration);
  318.                         targetHeight *= p.rb.mass;
  319.                     }
  320.                     if(heightReached < targetHeight) {
  321.                         float requiredJumpVelocity = Mathf.Sqrt((targetHeight - heightReached) * 2 * gForce);
  322.                         float forceNeeded = requiredJumpVelocity - currentJumpVelocity;
  323.                         jump_force += (jumpDirection * forceNeeded) / Time.deltaTime;
  324.                         currentJumpVelocity = requiredJumpVelocity;
  325.                     }
  326.                 }
  327.             } else {
  328.                 impulseActive = false;
  329.             }
  330.             if(currentJumpVelocity > 0) {
  331.                 float moved = currentJumpVelocity * Time.deltaTime;
  332.                 heightReached += moved;
  333.                 heightReachedTotal += moved;
  334.                 currentJumpVelocity -= gForce * Time.deltaTime;
  335.             } else if(!peaked && !p.IsStableOnGround) {
  336.                 peaked = true;
  337.                 impulseActive = false;
  338.             }
  339.             p.rb.AddForce(jump_force);
  340.         }
  341.     }
  342.     #endregion // Jumping
  343.     #region Gravity
  344.     public Gravity gravity = new Gravity();
  345.  
  346.     [System.Serializable]
  347.     public class Gravity {
  348.         [Tooltip("'down' direction for the player, which pulls the player")]
  349.         public Vector3 dir = -Vector3.up;
  350.         [SerializeField]
  351.         public float power = 9.81f;
  352.         public float GetGravityPower() { return power; }
  353.     }
  354.     #endregion // Gravity
  355.     #region MonoBehaviour
  356.     void Start() {
  357.         GroundNormal = Vector3.up;
  358.         EnsureRigidBody();
  359.         CollideBox = GetComponent<Collider>();
  360.         if(CollideBox == null) { CollideBox = gameObject.AddComponent<CapsuleCollider>(); }
  361.         ExpectedHeightFromGround = CollideBox.bounds.extents.y;
  362.         if(CollideBox is CapsuleCollider) {
  363.             CapsuleCollider cc = CollideBox as CapsuleCollider;
  364.             ExpectedHorizontalRadius = cc.radius;
  365.         } else {
  366.             Vector3 ex = CollideBox.bounds.extents;
  367.             ExpectedHorizontalRadius = Mathf.Max(ex.x, ex.z);
  368.         }
  369.     }
  370.     /// <summary>where physics-related changes happen</summary>
  371.     void FixedUpdate() {
  372.         if(gravityApplication != GravityState.none) { StandLogic(); }
  373.         MoveLogic();
  374.         // keep track of where we are in relation to the parent platform object
  375.         if(gravityApplication != GravityState.none && transformOnPlatform && transformOnPlatform.parent != null &&
  376.             stickToGround == HowToDealWithGround.withPlaceholder) {
  377.             transformOnPlatform.position = transform.position;
  378.         }
  379.         UpdateStateIndicators ();
  380.     }
  381.     void OnCollisionStay(Collision c) {
  382.         if(c.gameObject == StandingOnObject) {
  383.             if(!IsStableOnGround) { IsCollidingWithGround = true; } else { GroundNormal = c.contacts[0].normal; }
  384.         }
  385.     }
  386.     void OnCollisionExit(Collision c) {
  387.         if(c.gameObject == StandingOnObject) {
  388.             if(IsStableOnGround || IsCollidingWithGround) {
  389.                 IsCollidingWithGround = false;
  390.                 IsStableOnGround = false;
  391.             }
  392.         }
  393.     }
  394.     #endregion // MonoBehaviour
  395. } // MovingEntity
  396.  
  397. /// a basic Mobile Entity controller (for MOBs, like seeking fireballs, or very basic enemies)
  398. public class MovingEntityBase : MonoBehaviour {
  399.     #region Public API
  400.     /// <summary>if being controlled by player, this value is constanly reset by user input. otherwise, useable as AI controls.</summary>
  401.     [HideInInspector]
  402.     public Vector3 MoveDirection, LookDirection;
  403.     [Tooltip("speed limit")]
  404.     public float MoveSpeed = 5;
  405.     [Tooltip("how quickly the PlayerControl transform rotates to match the intended direction, measured in degrees-per-second")]
  406.     public float TurnSpeed = 180;
  407.     [Tooltip("how quickly the PlayerControl reaches MoveSpeed. If less-than zero, jump straight to move speed.")]
  408.     public float Acceleration = -1;
  409.     /// <summary>the player's RigidBody, which lets Unity do all the physics for us</summary>
  410.     [HideInInspector]
  411.     public Rigidbody rb { get; protected set; }
  412.     /// <summary>cached variables required for a host of calculations in other scripts</summary>
  413.     public float CurrentSpeed { get; protected set; }
  414.     public float BrakeDistance { get; protected set; }
  415.     [HideInInspector]
  416.     public bool IsBrakeOn = false, AutomaticallyTurnToFaceLookDirection = true;
  417.     protected bool brakesOnLastFrame = false;
  418.     public bool IsMovingIntentionally { get; protected set; }
  419.  
  420.     public void Copy(MovingEntityBase toCopy) {
  421.         MoveSpeed = toCopy.MoveSpeed;
  422.         TurnSpeed = toCopy.TurnSpeed;
  423.         Acceleration = toCopy.Acceleration;
  424.     }
  425.     public void UpdateStateIndicators() {
  426.         CurrentSpeed = rb.velocity.magnitude;
  427.         BrakeDistance = (Acceleration > 0)?CalculateBrakeDistance (CurrentSpeed,Acceleration/rb.mass)-(CurrentSpeed*Time.deltaTime):0;
  428.         if (IsBrakeOn) {
  429.             brakesOnLastFrame = true;
  430.             IsBrakeOn = false;
  431.         } else {
  432.             brakesOnLastFrame = false;
  433.         }
  434.     }
  435.     #endregion // Public API
  436.     #regionCore Controller Logic
  437.     public bool IsBraking() { return IsBrakeOn || brakesOnLastFrame; }
  438.     public virtual void MoveLogic() {
  439.         IsMovingIntentionally = MoveDirection != Vector3.zero;
  440.         if (IsBrakeOn) {
  441.             ApplyBrakes (Acceleration);
  442.         } else {
  443.             ApplyMove (Acceleration);
  444.         }
  445.         if (AutomaticallyTurnToFaceLookDirection && transform.forward != LookDirection && TurnSpeed != 0)
  446.         { UpdateFacing (); }    // turn body as needed
  447.     }
  448.     public virtual void UpdateFacing() {
  449.         if((LookDirection != Vector3.zero && transform.forward != LookDirection)) {
  450.             Vector3 r = (LookDirection == transform.right) ? -transform.forward : ((LookDirection == -transform.right) ? transform.forward : transform.right);
  451.             Vector3 up = Vector3.Cross(LookDirection, r);
  452.             TurnToFace (LookDirection, up);
  453.         }
  454.     }
  455.     public void TurnToFace(Vector3 forward, Vector3 up) {
  456.         Quaternion target = Quaternion.LookRotation(forward, up);
  457.         if (TurnSpeed > 0) {
  458.             target = Quaternion.RotateTowards (transform.rotation, target, TurnSpeed * Time.deltaTime);
  459.         }
  460.         transform.rotation = target;
  461.     }
  462.     protected virtual void EnforceSpeedLimit() {
  463.         float actualSpeed = rb.velocity.magnitude;
  464.         if(actualSpeed > MoveSpeed) {
  465.             rb.velocity = rb.velocity.normalized*MoveSpeed;
  466.         }
  467.     }
  468.     public virtual void ApplyBrakes(float deceleration) {
  469.         if (deceleration > 0) {
  470.             float amountToMove = deceleration * Time.deltaTime;
  471.             MoveDirection = -rb.velocity.normalized;
  472.             float actualSpeed = rb.velocity.magnitude;
  473.             if (actualSpeed > Acceleration * amountToMove) {
  474.                 rb.velocity += MoveDirection * amountToMove;
  475.                 return;
  476.             }
  477.         }
  478.         rb.velocity = Vector3.zero;
  479.     }
  480.     /// <summary>Applies AccelerationDirection to the velocity. if brakesOn, slows things down.</summary>
  481.     protected virtual void ApplyMove(float acceleration) {
  482.         if (acceleration > 0) {
  483.             float amountToMove = acceleration * Time.deltaTime;
  484.             rb.velocity += MoveDirection * amountToMove;
  485.             EnforceSpeedLimit ();
  486.         } else {
  487.             rb.velocity = MoveDirection * MoveSpeed;
  488.         }
  489.     }
  490.     public static float CalculateBrakeDistance(float speed, float acceleration) { return (speed * speed) / (2 * acceleration); }
  491.     #endregion // Core Controller Logic
  492.     #region Steering Behaviors
  493.     protected Vector3 SeekMath(Vector3 directionToLookToward) {
  494.         Vector3 desiredVelocity = directionToLookToward * MoveSpeed;
  495.         Vector3 desiredChangeToVelocity = desiredVelocity - rb.velocity;
  496.         float desiredChange = desiredChangeToVelocity.magnitude;
  497.         if (desiredChange < Time.deltaTime * Acceleration) { return directionToLookToward; }
  498.         Vector3 steerDirection = desiredChangeToVelocity.normalized;
  499.         return steerDirection;
  500.     }
  501.     /// <summary>used by the Grounded controller, to filter positions into ground-space (for better tracking on curved surfaces)</summary>
  502.     protected virtual Vector3 CalculatePositionForAutomaticMovement(Vector3 position) { return position; }
  503.     /// <summary>called during a FixedUpdate process to give this agent simple AI movement</summary>
  504.     public void CalculateSeek(Vector3 target, out Vector3 directionToMoveToward, ref Vector3 directionToLookToward) {
  505.         Vector3 delta = target - transform.position;
  506.         if (delta == Vector3.zero) { directionToMoveToward = Vector3.zero; return; }
  507.         float desiredDistance = delta.magnitude;
  508.         directionToLookToward = delta / desiredDistance;
  509.         directionToMoveToward = (Acceleration > 0)?SeekMath (directionToLookToward):directionToLookToward;
  510.     }
  511.     /// <summary>called during a FixedUpdate process to give this agent simple AI movement</summary>
  512.     public void CalculateArrive (Vector3 target, out Vector3 directionToMoveToward, ref Vector3 directionToLookToward) {
  513.         Vector3 delta = target - transform.position;
  514.         if (delta == Vector3.zero) { directionToMoveToward = Vector3.zero; return; }
  515.         float desiredDistance = delta.magnitude;
  516.         directionToLookToward = delta / desiredDistance;
  517.         if (desiredDistance > 1.0f / 1024 && desiredDistance > BrakeDistance) {
  518.             directionToMoveToward = SeekMath (directionToLookToward);
  519.             return;
  520.         }
  521.         directionToMoveToward = -directionToLookToward;
  522.         IsBrakeOn = true;
  523.     }
  524.  
  525.     public enum DirectionMovementIsPossible { none, all, forwardOnly, forwardOrBackward, wasd }
  526.     /// <summary>Filters the direction.</summary>
  527.     /// <returns>The direction.</returns>
  528.     /// <param name="dir">Dir.</param>
  529.     /// <param name="dirs">Dirs.</param>
  530.     /// <param name="minimumMoveAlignment">Minimum move alignment. from 0 to 1, the minimum Dot product for moving</param>
  531.     public Vector3 FilterDirection(Vector3 dir, DirectionMovementIsPossible dirs, float minimumMoveAlignment = 0) {
  532.         if (dir == Vector3.zero || dirs == DirectionMovementIsPossible.none) return Vector3.zero;
  533.         float fdot = Vector3.Dot (dir, transform.forward);
  534.         switch (dirs) {
  535.         case DirectionMovementIsPossible.all: break;
  536.         case DirectionMovementIsPossible.forwardOnly:
  537.             if (fdot > 0 && (minimumMoveAlignment <= 0 || fdot > minimumMoveAlignment))
  538.             { dir = transform.forward; } else { dir = Vector3.zero; } break;
  539.         case DirectionMovementIsPossible.forwardOrBackward:
  540.             if (fdot != 0 && (minimumMoveAlignment <= 0 || Mathf.Abs(fdot) > minimumMoveAlignment))
  541.             { dir = transform.forward * Mathf.Sign(fdot); } else { dir = Vector3.zero; } break;
  542.         case DirectionMovementIsPossible.wasd: {
  543.                 float rdot = Vector3.Dot (dir, transform.right);
  544.                 if (Mathf.Abs (rdot) > Mathf.Abs (fdot)) {
  545.                     if(minimumMoveAlignment <= 0 || Mathf.Abs(rdot) > minimumMoveAlignment)
  546.                     { dir = transform.right * Mathf.Sign (rdot); } else { dir = Vector3.zero; }
  547.                 }
  548.                 else if(minimumMoveAlignment <= 0 || Mathf.Abs(fdot) > minimumMoveAlignment)
  549.                 { dir = transform.forward * Mathf.Sign(fdot); } else { dir = Vector3.zero; }
  550.             } break;
  551.         }
  552.         return dir;
  553.     }
  554.     /// <summary>call this during a FixedUpdate process to give this agent simple AI movement</summary>
  555.     public void Seek(Vector3 target, DirectionMovementIsPossible dirs = DirectionMovementIsPossible.all,
  556.         float minimumMoveAlignment = 1-1.0f/128, bool stopIfAlignmentIsntGoodEnough = true) {
  557.         CalculateSeek (CalculatePositionForAutomaticMovement(target), out MoveDirection, ref LookDirection);
  558.         if (dirs != DirectionMovementIsPossible.all) { MoveDirection = FilterDirection (MoveDirection, dirs, minimumMoveAlignment); }
  559.         if (stopIfAlignmentIsntGoodEnough && MoveDirection == Vector3.zero && Acceleration > 0) { IsBrakeOn = true; }
  560.     }
  561.     /// <summary>call this during a FixedUpdate process to give this agent simple AI movement</summary>
  562.     public void Flee(Vector3 target) {
  563.         CalculateSeek (CalculatePositionForAutomaticMovement(target), out MoveDirection, ref LookDirection);
  564.         MoveDirection *= -1;
  565.     }
  566.     /// <summary>call this during a FixedUpdate process to give this agent simple AI movement</summary>
  567.     public void Arrive(Vector3 target) {
  568.         CalculateArrive (CalculatePositionForAutomaticMovement(target), out MoveDirection, ref LookDirection);
  569.     }
  570.     /// <summary>call this during a FixedUpdate process to give this agent simple AI movement</summary>
  571.     public void RandomWalk(float weight = 0, Vector3 weightedTowardPoint = default(Vector3)) {
  572.         if (weight != 0) {
  573.             Vector3 dir = (CalculatePositionForAutomaticMovement (weightedTowardPoint) - transform.position).normalized;
  574.             dir = (dir * weight) + (Random.onUnitSphere * (1 - weight));
  575.             Vector3 p = CalculatePositionForAutomaticMovement (transform.position + transform.forward + dir);
  576.             Vector3 delta = p - transform.position;
  577.             MoveDirection = LookDirection = delta.normalized;
  578.         } else {
  579.             Vector3 p = CalculatePositionForAutomaticMovement (transform.position + transform.forward + Random.onUnitSphere);
  580.             Vector3 delta = p - transform.position;
  581.             MoveDirection = LookDirection = delta.normalized;
  582.         }
  583.     }
  584.     #endregion // Steering Behaviors
  585.     #region MonoBehaviour
  586.     void Start() {
  587.         rb = GetComponent<Rigidbody>();
  588.         if(!rb) { rb = gameObject.AddComponent<Rigidbody>(); }
  589.         rb.useGravity = false; rb.freezeRotation = true;
  590.     }
  591.     /// <summary>where physics-related changes happen</summary>
  592.     void FixedUpdate() {
  593.         MoveLogic();
  594.         UpdateStateIndicators ();
  595.     }
  596.     #endregion // MonoBehaviour
  597. } // MovingEntityBase
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top