 # AxisAgent.cs

May 27th, 2015
443
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
1. // comment the line below if missing Lines.cs (found at http://pastebin.com/8m69iTut )
2. #define SHOW_AXIS_MATH
3. using UnityEngine;
4. using System.Collections;
5.
6. // TODO camera zoom with mouse wheel AxisCamera
7. // TODO gravity source
8.
9. /// <summary>
10. /// used for calculating and organizing 3D axis math
11. /// </summary>
12. public class Axis {
13.     public Vector3 forward, up, position;
14.     private Vector3 right;
15.
16.     public void SetIdentity() {
17.         up = Vector3.up;
18.         forward = Vector3.forward;
19.         right = Vector3.right;
20.         position = Vector3.zero;
21.     }
22.
23.     public bool Equals(Transform t) {
24.         return up == t.up && forward == t.forward && position == t.position;
25.     }
26.
27.     public Axis(Axis other) {
28.         up = other.up;
29.         right = other.right;
30.         forward = other.forward;
31.         position = other.position;
32.     }
33.     public Axis() { }
34.
35.     public Vector3 GetRight() {
36.         return right;
37.         //return Vector3.Cross(up, forward).normalized;
38.     }
39. #if SHOW_AXIS_MATH
40.     public LineRenderer DrawSimple(ref GameObject lineObj, Color c, float size) {
41.         const float epsilon = 0.001f;
42.         Vector3 u = up * size, f = forward * size;
43.         Vector3[] points = {
44.             position + u,
45.             position + u * epsilon,
46.             position + f * epsilon,
47.             position + f * (1 - epsilon),
48.             position + f,
49.             Vector3.zero };
50.         points[points.Length - 1] = Vector3.Lerp(points, points, 0.125f);
51.         LineRenderer lr = Lines.Make(ref lineObj, c, points, points.Length, .1f, .1f);
52.         return lr;
53.     }
54.     private Vector3[] thumbtack_points_base = null;
55.     public LineRenderer DrawThumbtack(ref GameObject lineObj, Color c, float size, float lineWidth) {
56.         const float epsilon = 0.001f;
57.         if(thumbtack_points_base == null) {
58.             Vector3 pstn = Vector3.zero;
59.             Vector3 fwrd = Vector3.forward * size;
60.             Vector3 rght = Vector3.right * size;
61.             Vector3 up__ = Vector3.up;
62.             float startAngle = (360.0f / 4) - (360.0f / 32);
63.             Vector3 v = Quaternion.AngleAxis(startAngle, up__) * fwrd;
64.             Lines.MakeArc(ref thumbtack_points_base, 32, up__, v, 360, pstn);
65.             Vector3 tip = pstn + fwrd * Mathf.Sqrt(2);
66.             thumbtack_points_base = thumbtack_points_base[thumbtack_points_base.Length - 1];
67.             int m = (32 * 5 / 8) + 1;
68.             thumbtack_points_base[m++] = thumbtack_points_base[m] + (tip - thumbtack_points_base[m]) * (1 - epsilon);
69.             thumbtack_points_base[m++] = tip;
70.             int n = (32 * 7 / 8) + 1;
71.             while(n < 32) {
72.                 thumbtack_points_base[m++] = thumbtack_points_base[n++];
73.             }
74.             Vector3 side = pstn + rght;
75.             thumbtack_points_base[m++] = thumbtack_points_base[m] + (side - thumbtack_points_base[m]) * (1 - epsilon);
76.             thumbtack_points_base[m++] = pstn + rght;
77.             thumbtack_points_base[m++] = pstn + rght * epsilon;
78.             thumbtack_points_base[m++] = pstn;
79.             thumbtack_points_base[m++] = pstn + up__ * size * (1 - epsilon);
80.             thumbtack_points_base[m++] = pstn + up__ * size;
81.         }
82.         LineRenderer lr = Lines.Make(ref lineObj, c, thumbtack_points_base, thumbtack_points_base.Length, lineWidth, lineWidth);
83.         lr.useWorldSpace = false;
84.         return lr;
85.     }
86. #endif
87.     public void SetFrom(Transform t) {
88.         position = t.position;
89.         up = t.up;
90.         forward = t.forward;
91.         right = t.right;
92.     }
93.
94.     public void SetFrom(Axis t) {
95.         position = t.position;
96.         up = t.up;
97.         forward = t.forward;
98.         right = t.right;
99.     }
100.
101.     public void WriteInto(Transform t) {
102.         t.position = position;
103.         t.rotation = Quaternion.LookRotation(forward, up);
104.     }
105.
106.     public void Turn(float t) {
107.         Quaternion turn = Quaternion.AngleAxis(t, up);
108.         forward = turn * forward;
109.         right = turn * right;
110.     }
111.
112.     public void Pitch(float p) {
113.         Quaternion turn = Quaternion.AngleAxis(p, right);
114.         forward = turn * forward;
115.         up = turn * up;
116.     }
117.
118.     public Quaternion GetUpright(Vector3 nextUp) {
119.         Vector3 r = Vector3.Cross(forward, nextUp);
120.         Vector3 fwd = Vector3.Cross(nextUp, r);
121.         return Quaternion.LookRotation(fwd, nextUp);
122.     }
123.
124.     public Quaternion DeltaTo(Quaternion rotation) {
125.         Quaternion q = Quaternion.LookRotation(this.forward, this.up);
126.         return rotation * Quaternion.Inverse(q);
127.     }
128.
129.     public Quaternion UprightDelta(Vector3 nextUp) {
130.         return DeltaTo(GetUpright(nextUp));
131.     }
132.
133.     public void Rotate(Quaternion rotation) {
134.         forward = rotation * forward;
135.         up = rotation * up;
136.         right = rotation * right;
137.     }
138.
139.     public void SetRotation(Quaternion rotation) {
140.         forward = rotation * Vector3.forward;
141.         up = rotation * Vector3.up;
142.         right = rotation * Vector3.right;
143.     }
144.
145.     public void SetUpright(Vector3 up) {
146.         SetRotation(GetUpright(up));
147.     }
148. }
149.
150. [System.Serializable]
151. public struct TupleMinMax {
152.     public float min, max;
153.     public TupleMinMax(float min, float max) { this.min = min; this.max = max; }
154.     public float Difference(float value) {
155.         if(value > max) { return max - value; }
156.         if(value < min) { return min - value; }
157.         return 0;
158.     }
159.     public float Lerp(float t) { return (max - min) * t + min; }
160.     public float PercentageOf(float value) { return (value - min) / (max - min); }
161. }
162.
163. public class AxisAgent : MonoBehaviour {
164.     /// <summary>how the body is oriented, which decides how key input creates movement impulses</summary>
165.     private Axis body = new Axis();
166.     /// <summary>how the face is oriented, which decides where the agent's head transform is looking (if there is one)</summary>
167.     private Axis face = new Axis();
168.     /// <summary>cached rigidbody of this agent</summary>
169.     private Rigidbody rb;
170.
171.     [Tooltip("What is moved with mouse-look, and determines the direction being aimed at.\n\nRight-click to setup a controllable agent from an otherwise Empty object."),
172.     ContextMenuItem("Setup Standard Axis Agent", "SetStandardAgent")]
174.     public Camera playerCamera;
175. #if SHOW_AXIS_MATH
176.     GameObject line_body, line_head, line_stand, line_gravity, line_velocity, line_move;
177. #endif
178.
179.     void SetStandardAgent() {
184.                 head.position = transform.position + transform.up * 0.875f;
187.             }
188.         }
189.         if(playerCamera == null) {
191.             playerCamera = (t != null) ? t.GetComponent<Camera>() : null;
192.             if(playerCamera == null) {
195.                 playerCamera.transform.localPosition = new Vector3(0, 5, -10);
196.                 playerCamera.transform.localRotation = Quaternion.Euler(25, 0, 0);
197.             }
198.         }
199.         Collider col = GetComponent<Collider>();
200.         if(col == null) {
202.             cap.height = 2;
204.         }
205.         rb = GetComponent<Rigidbody>();
206.         if(rb == null) {
208.         }
209.     }
210.
211.     [System.Serializable]
212.     public struct Gravity {
213.         [SerializeField, Tooltip("How strong the gravity force is")]
214.         private float force;
215.         [SerializeField, Tooltip("The direction of the gravity force")]
216.         private Vector3 direction;
217.         private Vector3 calculatedForce;
218.
219.         public Gravity(float force, Vector3 dir) {
220.             this.force = force; this.direction = dir; this.calculatedForce = force*dir; }
221.         public Vector3 GetDirection() { return direction; }
222.         public void SetDirection(Vector3 dir) { direction = dir; Refresh(); }
223.         public float GetForce() { return force; }
224.         public void SetForce(float force) { force = this.force; Refresh(); }
225.         public Vector3 Force() { return calculatedForce; }
226.         public void Negate() { calculatedForce = Vector3.zero; }
227.         public void Refresh() { calculatedForce = force * direction; }
228.
229.         public Vector3 FixedUpdate(Rigidbody rb) { Vector3 v; rb.AddForce(v = Force()); return v; }
230.     }
231.     public Gravity gravity = new Gravity(20, Vector3.down);
232.
233.     [System.Serializable]
234.     /// <summary>a system opposing gravity that allows an agent to hover very slightly over the ground, and move without friction</summary>
235.     public class Stand {
236.         [SerializeField, Tooltip("How far from the center (in the gravity direction) the standing volume goes")]
237.         private float distance = 1;
238.         [SerializeField, Tooltip("The radius of the standing volume")]
239.         private float radius = .375f;
240.         [Tooltip("If non-null, this transform's position will be set to the location where the standing is centered.\n\nSet to a sphere with the radius above to visualize the standing volume.")]
241.         public Transform marker;
242.
243.         /// <summary>the actual spot being stood on</summary>
244.         private Vector3 groundContact;
245.         /// <summary>how the ground is sloped in 3D space</summary>
246.         private Vector3 surfaceNormal = Vector3.up;
247.         /// <summary>a 1D value describing how the ground is sloped. used to limit standing force</summary>
248.         private float surfaceAngle;
249.         /// <summary>used to keep the standing volume from pushing fully out of the ground. keeping the standing volume slightly overlapping helps verify that the agent is still standing.</summary>
250.         private float standRadiusEpsilon = 1 / 128.0f;
251.         /// <summary>used as fast adjustment to position using the physics system (without directly setting transform.position)</summary></summary>
252.         private Vector3 oneTimeForce;
253.         /// true if an adjustment to position needs to be made</summary>
254.         private bool usingOneTimeForce = false;
255.         /// <summary>true if standing volume is on the ground</summary>
256.         private bool isOnGround = true;
257.         [SerializeField, Tooltip("If true, will automatically match motion of transform being stood on")]
258.         private bool becomeParentedToPlatform = true;
259.         /// <summary>the actual transform being stood on</summary>
260.         Transform stoodOn = null;
261.         /// <summary>the velocity of the transform being stood on</summary>
262.         private Vector3 stoodOnVelocity;
263.         /// <summary>axis (position and rotation) of the transform being stood on</summary>
264.         Axis parentTransform = new Axis();
265.         /// <summary>the physics calculation done last frame to keep up with the stood-on transform</summary>
266.         Vector3 lastPlatformMove;
267.
268.         public bool IsOnGround() { return isOnGround; }
269.         public void SetOnGround(bool onGround) { isOnGround = onGround; }
270.         public float GetSurfaceAngle() { return surfaceAngle; }
271.         public Vector3 GetSurfaceNormal() { return surfaceNormal; }
272.         public Vector3 GetGroundContact() { return groundContact; }
273.
274.         /// <param name="a"></param>
275.         /// <returns>force to apply to a Rigidbody, using AddForce</returns>
276.         public Vector3 Force(AxisAgent a, ref Gravity g) {
277.             Vector3 force = Vector3.zero;
278.             if(usingOneTimeForce) {
279.                 usingOneTimeForce = false;
280.                 force -= oneTimeForce;
281.             }
282.             // if non-rigid-body parent has moved
283.             if(becomeParentedToPlatform && stoodOn != null && !parentTransform.Equals(stoodOn)) {
285.                 stoodOnVelocity = stoodOn.position - parentTransform.position;
286.                 lastPlatformMove = stoodOnVelocity / Time.deltaTime;
287.                 parentTransform.SetFrom(stoodOn);
289.                 a.transform.position = a.transform.position + stoodOnVelocity;
290.             }
291.             Vector3 standingDirection = -g.GetDirection();
292.             Ray ray = new Ray(a.transform.position, -standingDirection);
293.             RaycastHit hitInfo = new RaycastHit();
294.             a.GetComponent<Collider>().enabled = false;
295.             if(!a.jumping.IsJumping() && Physics.SphereCast(ray, radius, out hitInfo, distance) && hitInfo.distance <= distance) {
296.                 groundContact = hitInfo.point;
297.                 surfaceNormal = hitInfo.normal;
298.                 surfaceAngle = Vector3.Angle(standingDirection, surfaceNormal);
299.                 if(Vector3.Dot(a.body.forward, surfaceNormal) > 0) {
300.                     surfaceAngle *= -1;
301.                 }
302.                 Vector3 centerOfFoot = a.transform.position - standingDirection * distance;
303.                 if(marker != null) {
304.                     marker.position = centerOfFoot;
305.                 }
306.                 float dist = Vector3.Distance(groundContact, centerOfFoot);
307.                 float groundOverlap = radius - dist;
308.                 if(groundOverlap > 0) {
309.                     // instead of setting the position, set the force needed to clip out, then remove that force next frame.
310.                     usingOneTimeForce = true;
311.                     oneTimeForce = (surfaceNormal * (groundOverlap - standRadiusEpsilon)) / (Time.deltaTime*Time.deltaTime);
312.                     force += oneTimeForce;
313.                 }
314.                 float downForce = Vector2.Dot(-standingDirection, a.rb.velocity);
315.                 if(!isOnGround && downForce > 0) {
316.                     force += standingDirection * downForce / Time.deltaTime;
317.                 }
318.                 // reset double-jump variable if on the ground
319.                 a.jumping.ResetJumpCount();
320.                 isOnGround = true;
321.                 g.Negate();
322.                 if(becomeParentedToPlatform) {
323.                     if(stoodOn != hitInfo.collider.transform) {
324.                         stoodOn = hitInfo.collider.transform;
325.                         parentTransform.SetFrom(stoodOn);
326.                     }
327.                 }
328.             } else {
329.                 isOnGround = false;
330.                 g.Refresh();
331.                 if(becomeParentedToPlatform) {
332.                     //if(stoodOn != null) print("no parent");
333.                     stoodOn = null;
334.                     parentTransform.SetIdentity();
335.                 }
336.             }
337.             a.GetComponent<Collider>().enabled = true;
338.             return force;
339.         }
340.         public Vector3 FixedUpdate(Rigidbody rb, AxisAgent a, ref Gravity gravity) {
341.             Vector3 standForce = Force(a, ref gravity);
343.             return standForce;
344.         }
345.     }
346.     public Stand stand;
347.
348.     [System.Serializable]
349.     public class Jump {
350.         public TupleMinMax height = new TupleMinMax(1, 5);
351.         public float fullJumpPressDuration = 0.5f;
352.         public int maxJumps = 2;
353.         public bool jumpStartResetsVerticalMotion = true;
354.
355.         /// how long the user has been holding jump
356.         float timeJumpHeld;
357.         /// how the jump is currently doing
358.         float currentJumpVelocity;
359.         float targetHeight;
360.         float heightReached;
361. #if SHOW_AXIS_MATH
362.         Vector3 jumpStart;
363.         float heightReachedTotal;
364. #endif
365.         bool jumpPeaked = false;
366.         /// whether or not the user is currently jumping
367.         bool jumpImpulseActive = false;
368.         int jumpsSoFar = 0;
369.
370.         public enum JumpDirection { opposingGravity, opposingGroundSurface }
371.         GameObject jumpShow;
372.         public bool IsRequestingJump(AxisAgent a) { return !jumpImpulseActive && (a.input.jump || a.input.jumpHeld); }
373.         public bool IsJumping() { return currentJumpVelocity > 0; }
374.         public void ResetJumpCount() { jumpsSoFar = 0; }
375.
376.         public Vector3 Force(AxisAgent a, ref Gravity g, ref Movement movement) {
377.             Vector3 force = Vector3.zero;
378.             Vector3 jumpDirection = -g.GetDirection();//onlyJumpAgainstGravity ? -g.GetDirection() : a.stand.GetSurfaceNormal();//
379.             // if the user wants to jump, and is allowed to jump again
380.             if(IsRequestingJump(a) && (jumpsSoFar < maxJumps)) {
381. #if SHOW_AXIS_MATH
382.                 if(jumpsSoFar == 0) {
383.                     jumpStart = a.transform.position;
384.                     heightReachedTotal = 0;
385.                 }
386. #endif
387.                 heightReached = 0;
388.                 timeJumpHeld = 0;
389.                 jumpsSoFar++;
390.                 targetHeight = height.min;
391.                 float velocityRequiredToJump = Mathf.Sqrt(targetHeight * 2 * g.GetForce());
392.                 // cancel out current jump/fall forces
393.                 if(jumpStartResetsVerticalMotion) {
394.                     float motionInVerticalDirection = Vector3.Dot(jumpDirection, a.rb.velocity);
395.                     force -= (motionInVerticalDirection * jumpDirection) / Time.deltaTime;
396.                 }
397.                 // apply proper jump force
398.                 currentJumpVelocity = velocityRequiredToJump;
399.                 jumpPeaked = false;
400.                 force += (jumpDirection * currentJumpVelocity) / Time.deltaTime;
401.                 jumpImpulseActive = true;
402.                 a.stand.SetOnGround(false);
403.             } else
404.             // if a jump is happening
405.             if(currentJumpVelocity > 0) {
406.                 // handle jump height: the longer you hold jump, the higher you jump
407.                 if(a.input.jumpHeld) {
408.                     timeJumpHeld += Time.deltaTime;
409.                     if(timeJumpHeld >= fullJumpPressDuration) {
410.                         targetHeight = height.max;
411.                         timeJumpHeld = fullJumpPressDuration;
412.                     } else {
413.                         targetHeight = height.Lerp(timeJumpHeld / fullJumpPressDuration);
414.                     }
415.                     if(heightReached < targetHeight) {
416.                         float requiredJumpVelocity = Mathf.Sqrt((targetHeight - heightReached) * 2 * g.GetForce());
417.                         float forceNeeded = requiredJumpVelocity - currentJumpVelocity;
418.                         force += (jumpDirection * forceNeeded) / Time.deltaTime;
419.                         currentJumpVelocity = requiredJumpVelocity;
420.                     }
421.                 } else {
422.                     jumpImpulseActive = false;
423.                 }
424.             }
425.             if(currentJumpVelocity > 0) {
426.                 float moved = currentJumpVelocity * Time.deltaTime;
427.                 heightReached += moved;
428. #if SHOW_AXIS_MATH
429.                 heightReachedTotal += moved;
430.                 Lines.Make(ref jumpShow, Color.black, jumpStart, a.transform.position, .5f, 0f);
431. #endif
432.                 currentJumpVelocity -= g.GetForce() * Time.deltaTime;
433.             } else if(!jumpPeaked && !a.stand.IsOnGround()) {
434.                 jumpPeaked = true;
435.                 jumpImpulseActive = false;
436.             }
437.             return force;
438.         }
439.
440.         public Vector3 FixedUpdate(Rigidbody rb, AxisAgent a, ref Gravity gravity, ref Movement movement) {
441.             Vector3 f = Force(a, ref gravity, ref movement);
443.             return f;
444.         }
445.     }
446.     public Jump jumping;
447.
448.     [System.Serializable]
449.     public class Input {
450.         public enum Source { none, mouseAndKeyboard }
451.         [Tooltip("Where to get input from")]
452.         public Source source = Source.mouseAndKeyboard;
454.         /// variable:+/-, forward:forward/backward, strafe:right/left, turn:right/left, pitch:down/up
455.         [HideInInspector] public float forward, strafe, turn, pitch;
456.         /// [Tooltip("button inputs")]
457.         [HideInInspector] public bool jump, jumpHeld, fire1, fire1Held, fire2, fire2Held, gravityToggle, fullStop, invertPitch, runHeld;
458.
459.         public void ReleaseAllInput() {
460.             forward = strafe = turn = pitch = 0; //mouseWheel = 0;
461.             jump = jumpHeld = fire1 = fire1Held = fire2 = fire2Held = runHeld = false;
462.         }
463.         [System.Serializable]
464.         public class Sensitivity {
465.             [Tooltip("x mouse sensitivity")]
466.             public float horizontal = 10F;
467.             [Tooltip("y mouse sensitivity")]
468.             public float vertical = 5F;
469.         }
470.         public Sensitivity sensitivity;
471.
473.             switch(source) {
474.             case Source.none: break;
475.             case Source.mouseAndKeyboard:
476.                 forward = UnityEngine.Input.GetAxis("Vertical");
477.                 strafe = UnityEngine.Input.GetAxis("Horizontal");
478.                 turn = UnityEngine.Input.GetAxis("Mouse X") * sensitivity.horizontal;
479.                 pitch = -UnityEngine.Input.GetAxis("Mouse Y") * sensitivity.vertical;
480.                 jump = UnityEngine.Input.GetButtonDown("Jump");
481.                 jumpHeld = UnityEngine.Input.GetButton("Jump");
482.                 fire1 = UnityEngine.Input.GetButtonDown("Fire1");
483.                 fire1Held = UnityEngine.Input.GetButton("Fire1");
484.                 fire2 = UnityEngine.Input.GetButtonDown("Fire2");
485.                 fire2Held = UnityEngine.Input.GetButton("Fire2");
486.                 runHeld = UnityEngine.Input.GetKey(KeyCode.LeftShift)
487.                         ||UnityEngine.Input.GetKey(KeyCode.RightShift);
488.                 break;
489.             default:
490.                 throw new System.Exception("unsupported controller " + source);
491.             }
492.             if(invertPitch) pitch *= -1;
493.         }
494.     }
495.     public Input input;
496.
497.     [System.Serializable]
498.     public class Movement {
499.         ///<summary>current movement force</summary>
500.         private Vector3 force;
501.         ///<summary>velocity vector if viewed directly from above</summary>
502.         private Vector3 groundVelocity;
503.         ///<summary>groundVelocity.magnitude</summary>
504.         private Vector3 groundDir;
505.         ///<summary>the direction according to the input</summary>
506.         private Vector3 inputDir;
507.         ///<summary>how much rise/fall is going on</summary>
508.         private float verticalVelocity;
509.         ///<summary>velocity absent rise/fall</summary>
510.         private float groundSpeed;
511.         ///<summary>max speed based on input, taking directional speed limits into account</summary>
512.         private float maxInputSpeed;
513.         ///<summary>how much faster the agent is going than the current speed limit</summary>
514.         private float overSpeed;
515.
516.         public Vector3 defaultUpDirection = Vector3.up;
517.
518.         Axis movementAxis;
519.
520.         ///<summary>how far the 3rd person camera can move back based on obstacles in the way</summary>
521.         float distanceLimitedTo;
522.         ///<summary>where the 3rd person camera is</summary>
523.         float actualDistance;
524.
525. #if SHOW_AXIS_MATH
526.         GameObject line_movementAxis;
527. #endif
528.         public Vector3 CalculatedSurfaceUpDirection(AxisAgent a) {
529.             return a.stand.IsOnGround() ? a.stand.GetSurfaceNormal() : defaultUpDirection;
530.         }
531.
532.         public Vector3 Force(AxisAgent a, Settings s) {
533.             if(movementAxis == null) movementAxis = new Axis();
534.             movementAxis.SetFrom(a.body);
535.             Vector3 upDirection = (Mathf.Abs(a.stand.GetSurfaceAngle()) < s.maxIncline) ? CalculatedSurfaceUpDirection(a) : defaultUpDirection;//
536.             if(a.body.up != upDirection) {
537.                 movementAxis.SetUpright(upDirection);
538.             }
539. #if SHOW_AXIS_MATH
540.             movementAxis.DrawThumbtack(ref line_movementAxis, Color.blue, 2, .0625f);
541.             movementAxis.WriteInto(line_movementAxis.transform);
542. #endif
543.             force = Vector3.zero;
544.             verticalVelocity = Vector3.Dot(defaultUpDirection, a.rb.velocity);
545.             groundVelocity = a.rb.velocity - (defaultUpDirection * verticalVelocity);
546.             groundSpeed = groundVelocity.magnitude;
547.             groundDir = groundVelocity / groundSpeed;
548.             inputDir = Vector3.zero;
549.             // input impulse
550.             if(a.input.forward != 0 || a.input.strafe != 0) {
551.
552.                 maxInputSpeed = s.GetSpeedBasedOnInput(a.input, a.stand.GetSurfaceAngle());
553.                 inputDir = movementAxis.forward * a.input.forward + movementAxis.GetRight() * a.input.strafe;
554.                 inputDir.Normalize();
555.                 float accelSpeedInCurrentDirection = Vector3.Dot(inputDir, groundVelocity);
556.                 force = inputDir * ((accelSpeedInCurrentDirection > 0) ? s.acceleration : s.deceleration);
557.                 if(groundSpeed > maxInputSpeed) {
558.                     overSpeed = Vector3.Dot(groundDir, force);
559.                     overSpeed += groundSpeed - maxInputSpeed;
560.                     force += groundDir * -overSpeed;
561.                     force = force.normalized * s.acceleration;
562.                 }
563.             } else {
564.                 // stop logic
565.                 if(s.stopWhenNoInputGiven && groundSpeed != 0) {
566.                     // figure out how much force is needed to stop
567.                     float stopNeededToBringToZero = groundSpeed / Time.deltaTime;
568.                     // if that is too much force, go with the max possible.
569.                     if(stopNeededToBringToZero > s.deceleration) {
570.                         force += -groundDir * s.deceleration;
571.                     } else {
572.                         force += -groundDir * stopNeededToBringToZero;
573.                     }
574.                 }
575.             }
576.             return force;
577.         }
578.
579.         [System.Serializable]
580.         public class Settings {
581.             [Tooltip("What to use this movement profile. For example, there could be different movement settings for: walk, run, crawl, swim, climb, parkour, jump, fall, fly, hover")]
582.             public string name = "standard";
583.             [Tooltip("Maximum speed when pressing forward/backward")]
584.             public float speedForward = 15, speedBackward = 5;
585.             [Tooltip("Maximum speed when pressing strafe right/left")]
586.             public float speedRight = 10, speedLeft = 10;
587.             [Tooltip("Maximum acceleration when going forward/backward or strafing right/left")]
588.             public float acceleration = 20;
589.             [Tooltip("Maximum acceleration when bringing velocity toward zero")]
590.             public float deceleration = 30;
591.             [Tooltip("If true, movement calculations will cause a stop if inputs are released")]
592.             public bool stopWhenNoInputGiven = true;
593.             [Tooltip("the agent will not be able to move around on surfaces with an incline greater than this")]
594.             public float maxIncline = 50;
595.             [Tooltip("Percentage of speed possible as the current surface angle approaches the max incline")]
596.             public AnimationCurve inclineSpeedLoss = AnimationCurve.Linear(0,0,1,1);
597.
598.             /// TODO implement these for hover, climb, and parkour movement types
599.             [Tooltip("Acts against gravity force")]
600.             public float antiGravityForce = 0;
601.             [Tooltip("Causes gravity to match player up direction, the stronger force the more un-aligned with gravity it will work")]
602.             public float gravityRealignForce = 0;
603.
604.             public float GetInclineSpeedAdjustment(float surfaceAngle) {
605.                 return (surfaceAngle > 0) ? (1-inclineSpeedLoss.Evaluate(surfaceAngle / maxIncline)) :1;
606.             }
607.
608.             [System.Serializable]
610.                 public TupleMinMax pitchLimit = new TupleMinMax(-140, 110);
611.                 float currentVerticalAngle;
612.                 // TODO allow the head transform to turn independently of the body, and make the body match if the turnLimit is reached or movement starts
613. //              public TupleMinMax turnLimit = new TupleMinMax(-50, 50);
614. //              public bool turnBodyBeyondMaximum = true;
615. //              public bool pitchBodyBeyondMaximum = false;
616.
617.                 public void Update(AxisAgent a) {
618.                     if(a.input.pitch != 0) {
620.                         a.face.Pitch(a.input.pitch);
621.                         currentVerticalAngle = Vector3.Angle(a.body.up, a.face.up);
622.                         if(Vector3.Dot(a.face.forward, a.body.up) > 0) {
623.                             currentVerticalAngle *= -1;
624.                         }
625.                         float difference = pitchLimit.Difference(currentVerticalAngle);
626.                         if(difference != 0) {
627.                             a.face.Pitch(difference);
628.                             currentVerticalAngle += difference;
629.                         }
631.                         thirdPersonCamera.CameraAngleUpdate(a.playerCamera, pitchLimit.PercentageOf(currentVerticalAngle));
632.                     }
633.                     float wheel = UnityEngine.Input.GetAxis("Mouse ScrollWheel");
634.                     if(wheel != 0) {
635.                         thirdPersonCamera.Move(a.playerCamera.transform, wheel);
636.                     }
637.                 }
638.
639.                 [System.Serializable]
640.                 public class ThirdPersonCameraController {
641.                     [Tooltip("How far the user wants the camera to be")]
642.                     public float thirdPersonDistance = 10;
643.                     [Tooltip("How quickly to move the camera to it's spot (lower is faster)"), Range(1, 128)]
644.                     public float collisionSpeed = 8;
645.                     public AnimationCurve viewAngle3rdPerson = DefaultCameraLookCurve();
646.
647.                     public static AnimationCurve DefaultCameraLookCurve() {
648.                         AnimationCurve curve = new AnimationCurve();
653.                         return curve;
654.                     }
655.
656.                     public void Move(Transform camera, float change) {
657.                         change *= 5;
658.                         thirdPersonDistance -= change;
659.                         if(thirdPersonDistance < change) {
660.                             camera.position = camera.parent.position;
661.                             thirdPersonDistance = 0;
662.                         }
663.                     }
664.
665.                     public void CameraAngleUpdate(Camera camera, float vertical) {
666.                         vertical = viewAngle3rdPerson.Evaluate(vertical);
667.                         float fieldOfView = camera.fieldOfView;
668.                         float viewAngle = fieldOfView * vertical;
669.                         camera.transform.localRotation = Quaternion.Euler(viewAngle - 30, 0, 0);
670.                     }
671.                 }
672.                 public ThirdPersonCameraController thirdPersonCamera = new ThirdPersonCameraController();
673.             }
675.
676.             [System.Serializable]
677.             public class RotationControl {
678.                 [Tooltip("Be precise when turning with the mouse (no lag, or continued turning)")]
679.                 public bool stopControlledAngularVelocity = true;
680.                 [Tooltip("stop the spinning!")]
681.                 public bool stopUncontrolledAngularVelocity = true;
682.                 [Tooltip("set the player standing correctly if angle is perterbed")]
683.                 public bool automaticallySetRightsideUp = true;
684.                 [Tooltip("Force used to stop uncontrolled angular rotation")]
685.                 public Vector3 angularVelocityCounterForce = new Vector3(15, 90, 15);
686.                 [Tooltip("Force used to set player upright")]
687.                 public float rightsideUpForce = 20;
688.             }
689.             public RotationControl rotationControl = new RotationControl();
690.
691.             public Settings(float forward, float backward, float strafeRight, float strafeLeft, float accel, float decel) {
692.                 this.speedForward = forward;
693.                 this.speedBackward = backward;
694.                 this.speedRight = strafeRight;
695.                 this.speedLeft = strafeLeft;
696.                 this.acceleration = accel;
697.                 this.deceleration = decel;
698.             }
699.
700.             public float GetSpeedBasedOnInput(Input i, float angle) {
701.                 float result;
702.                 if(i.forward == 0) {
703.                     if(i.strafe == 0) result = 0;
704.                     else if(i.strafe < 0) result = speedLeft;
705.                     else result = speedRight;
706.                 } else if(i.strafe == 0) {
707.                     if(i.forward > 0) result = speedForward;
708.                     else result = speedBackward;
709.                 } else {
710.                     float sum, f, s;
711.                     if(i.forward > 0) {
712.                         f = i.forward;
713.                         if(i.strafe > 0) { s = i.strafe; sum = f + s; f /= sum; s /= sum; result = (f * speedForward) + (s * speedRight); }
714.                         else             { s = -i.strafe; sum = f + s; f /= sum; s /= sum; result = (f * speedForward) + (s * speedLeft); }
715.                     } else {
716.                         f = -i.forward;
717.                         if(i.strafe > 0) { s = i.strafe; sum = f + s; f /= sum; s /= sum; result = (f * speedBackward) + (s * speedRight); }
718.                         else             { s = -i.strafe; sum = f + s; f /= sum; s /= sum; result = (f * speedBackward) + (s * speedLeft); }
719.                     }
720.                 }
722.             }
723.         }
724.
725.         public Settings currentSettings = new Settings(15, 5, 10, 10, 20, 60);
726.
727.         private Vector3 angularJerk, standUpJerk;
728.
729. #if SHOW_AXIS_MATH
730.         /// visuals to help debug rotation forces
731.         GameObject rot_axis, rot_fwd, rot_up, rot_target;
732. #endif
733.         public void FixedUpdateRotation(AxisAgent a, Input inputSource, Transform transform, Axis body, Rigidbody rb) {
734.             bool balancingRotation = false;
735.             // if the agent is controlling angular jerks, to emulate precise rotation, and there was an angular jerk last frame
736.             if(currentSettings.rotationControl.stopControlledAngularVelocity && angularJerk != Vector3.zero) {
737.                 // find out how much that angular jerk has depreciated
738.                 Vector3 adjusted = angularJerk - (angularJerk * rb.angularDrag * Time.deltaTime);
739.                 // remove the jerk from the current angular rotation
741.                 // mark that there is no more angular jerk (it has been undone)
742.                 angularJerk = Vector3.zero;
743.                 // mark thata balancing rotation occured, which will prevent further balancing rotations
744.                 balancingRotation = true;
745.             }
746.             if(currentSettings.rotationControl.automaticallySetRightsideUp && body.up != defaultUpDirection) {
747.                 Quaternion uprightRotation = body.GetUpright(defaultUpDirection);
748.                 Quaternion rotationToRightSelf = body.DeltaTo(uprightRotation);//body.UprightDelta(-gravity.GetDirection());
749.                 Vector3 euler = rotationToRightSelf.eulerAngles;
750.                 bool change = false;
751.                 if(euler.x > 180) { euler.x -= 360; change = true; }
752.                 if(euler.y > 180) { euler.y -= 360; change = true; }
753.                 if(euler.z > 180) { euler.z -= 360; change = true; }
754.                 const float epsilon = 1 / 128f;
755.                 if(Mathf.Abs(euler.x) < epsilon) euler.x = 0;
756.                 if(Mathf.Abs(euler.y) < epsilon) euler.y = 0;
757.                 if(Mathf.Abs(euler.z) < epsilon) euler.z = 0;
758.                 if(change) {
759.                     //print(Time.time+":["+euler.x + " " + euler.y + " " + euler.z+"]");
760.                     rotationToRightSelf = Quaternion.Euler(euler);
761.                 }
762.                 if(euler != Vector3.zero) {
763.                     Vector3 rotAxis = Vector3.zero;
764.                     float rotAngle;
765.                     rotationToRightSelf.ToAngleAxis(out rotAngle, out rotAxis);
766. #if SHOW_AXIS_MATH
767.                     Lines.Make(ref rot_axis, Color.magenta, transform.position + rotAxis, transform.position - rotAxis, .1f, .1f);
768.                     Lines.MakeArc(ref rot_fwd, Color.magenta, transform.position, rotAxis, transform.forward, rotAngle, 8, .1f, 0);
769.                     Lines.MakeArc(ref rot_up, Color.magenta, transform.position, rotAxis, transform.up, rotAngle, 8, .1f, 0);
770.                     Axis next = new Axis(body);
771.                     next.Rotate(rotationToRightSelf);
772.                     next.DrawSimple(ref rot_target, Color.black, 1);
773. #endif
774.                     float f = currentSettings.rotationControl.rightsideUpForce;
775.                     euler.x = Mathf.Clamp(euler.x, -f, f);
776.                     euler.y = Mathf.Clamp(euler.y, -f, f);
777.                     euler.z = Mathf.Clamp(euler.z, -f, f);
778.                     //print(Time.time + ": " + euler.x + " " + euler.y + " " + euler.z);
779.                     if(standUpJerk != euler) {
780.                         // divide by 2 because... it makes it work better. TODO more controlled right-side-up angular forces!
782.                         standUpJerk = euler;
784.                     }
785.                 }
786.             }
787.             // if the player wants to turn (horizontal motion)
788.             if(inputSource.turn != 0) {
789.                 // turn horizontally
790.                 angularJerk.y += inputSource.turn / Time.deltaTime;
792.             } else {
793.                 // if no turn is going on, and no balancing rotation is going on, and this agent should be stopping uncontrolled rotations
794.                 if(currentSettings.rotationControl.stopUncontrolledAngularVelocity && !balancingRotation) {
795.                     // if there is rotation
796.                     Vector3 angle = rb.angularVelocity;
797.                     if(angle != Vector3.zero) {
798.                         // calculate a counter-force to stop rotation
799.                         Vector3 counterForce = currentSettings.rotationControl.angularVelocityCounterForce;// Vector3.zero;
800.                         counterForce.x = Mathf.Clamp(angle.x, -counterForce.x, counterForce.x);
801.                         counterForce.y = Mathf.Clamp(angle.y, -counterForce.y, counterForce.y);
802.                         counterForce.z = Mathf.Clamp(angle.z, -counterForce.z, counterForce.z);
804.                     }
805.                 }
806.             }
807.         }
808.
809.         public Vector3 Force(AxisAgent a) {
810.             return Force(a, currentSettings);
811.         }
812.
813.         public Vector3 FixedUpdate(Rigidbody rb, AxisAgent a) {
814.             Vector3 f = Force(a);
816.             return f;
817.         }
818.
819.         public void MoveCameraBack(Transform camera) {
821.             if(thirdPersonDistance == 0) return;
822.             RaycastHit rh = new RaycastHit();
823.             Vector3 dir = -camera.parent.forward;
824.             float nextPosition = actualDistance;
825.             if(!Physics.Raycast(camera.parent.position, dir, out rh, thirdPersonDistance)) {
826.                 distanceLimitedTo = thirdPersonDistance;
827.             } else {
828.                 distanceLimitedTo = rh.distance;
829.             }
830.             if(actualDistance != distanceLimitedTo) {
831.                 float delta = (distanceLimitedTo - actualDistance) / currentSettings.headTurning.thirdPersonCamera.collisionSpeed;
832.                 nextPosition = (Mathf.Abs(delta) > 1 / 128.0f) ? (actualDistance + delta) : distanceLimitedTo;
833.             }
834.             if(nextPosition != actualDistance) {
835.                 actualDistance = nextPosition;
836.                 camera.localPosition = -Vector3.forward * actualDistance;
837.             }
838.         }
839.
840.         public void Update(AxisAgent a) {
842.             MoveCameraBack(a.playerCamera.transform);
843.         }
844.     }
845.
846.     [Tooltip("Variables that set how this agent moves, and keep track of orientation")]
847.     public Movement movement;
848.
849.     public void SetMovementSettings(Movement.Settings moveSettings) {
850.         movement.currentSettings = moveSettings;
851.     }
852.
853.     ///////////////////////////////////////////////////////////////////////////////////////////////
854.
855.     /// uses Rigidbody.AddForce() to modify the rigidbody's behavior.
856.     void FixedUpdate () {
857.         // update axis
858.         body.SetFrom(transform);
860.         // update components. keep track of forces locally so visuals can be updated
861.         Vector3 moveForce = movement.FixedUpdate(rb, this);
862.         Vector3 gravityForce = gravity.FixedUpdate(rb);
863.         Vector3 jumpingForce = jumping.FixedUpdate(rb, this, ref gravity, ref movement);
864.         Vector3 standForce = stand.FixedUpdate(rb, this, ref gravity);
865.         // TODO RotationMovement
866.         movement.FixedUpdateRotation(this, input, transform, body, rb);
867. #if SHOW_AXIS_MATH
868.         DrawAxisOnTransform(ref line_body, Color.cyan, body, transform, .75f);
870.         Vector3 p = transform.position;
871.         Vector3 s = stand.GetGroundContact();
872.         Vector3 v = transform.position + rb.velocity;
873.         Lines.Make(ref line_gravity, Color.magenta, p, p + gravityForce, .2f, 0);
874.         Lines.Make(ref line_stand, Color.green, s, s + standForce, .4f, 0);
875.         Lines.Make(ref line_velocity, Color.blue, p, v, .1f, .1f);
876.         Lines.Make(ref line_move, Color.red, v, v + moveForce, .1f, .1f);
877. #endif
878.     }
879.
880. #if SHOW_AXIS_MATH
881.     void DrawAxisOnTransform(ref GameObject line_, Color c, Axis a, Transform t, float size) {
882.         a.DrawThumbtack(ref line_, c, size, .1f);
883.         line_.transform.SetParent(t);
884.         line_.transform.localPosition = Vector3.zero;
885.         line_.transform.localRotation = Quaternion.identity;
886.     }
887. #endif
888.
889.     struct MoveSettingsManager {
890.         Movement.Settings defaultMoveSetting;
891.         [Tooltip("walk, run, crawl, swim, climb, parkour, jump, fall, fly, hover")]
892.         public Movement.Settings[] movementSettings;
893.         // TODO run button, crawl button, fly minimum speed float, swim boolean toggle
894.         // walk is standard movement, just slow. motion axis aligns with ground, if in the air, it aligns with player-up
895.         //     if crawl and run pressed while standing)
896.         // crawl crouches the player, and is very slow movement (with a smaller height profile)
897.         //     crawl button. jump stands player up
898.         // run is standard movement, but fast.
899.         //     automatically parkour if on non-ground surface
900.         // climb aligns gravity to be against a surface, allowing scaling of walls (and ceiling too if gravity alignment force is strong enough)
901.         //     if crawl and run pressed while against non-walkable surface. jump ends climb, and goes into jump (fall)
902.         // parkour causes player-up to shift when running on surfaces, and aligns gravity if able, allowing wall running (and ceiling running if gravity alignment force is strong enough)
903.         //     if running at surface that is too steep to be ground. release run to drop into standard movement. crawl to climb.
904.         // fall activates when the player is jumping or falling (non air-movement, but still in the air)
905.         //     jump again for double jump. if no more double jumps possible, go into hover.
906.         // hover is an air-travel, feels like no-clip movement in a FPS game. jump is up, crawl is down. uses antigravity force to balance out gravity. re-orient to up after movement and turning stops.
907.         //     activate if pressing jump during fall at the end of last double-jump. run to drop into fall, or switch into fly if speed is sufficient
908.         // fly is a fast air-travel, like a flight-sim, where left and right roll, forward and back pitch. player-up is not bound to gravity. must be in motion. free look. slowing down drops player into hover
909.         //     crawl to drop into fall, falling to minimum speed drops into fall
910.         // swim is triggered when entering a swim zone (water). dramatically reduces gravity, and makes the motion axis align with the look axis, allowing motion in any direction
911.         //     climb works. leave swim zone to end swim. jump to go into hover
912.         //     (water areas have a move-speed modifier and direction of flow)
913.     }
914.
915.     void Start() {
916.         rb = GetComponent<Rigidbody>();
917.         rb.useGravity = false;
918.         rb.angularDrag = 0;
919.         rb.maxAngularVelocity = 360;
920.     }
921.
922.     void Update() {