Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // comment the line below if missing Lines.cs (found at http://pastebin.com/8m69iTut )
- #define SHOW_AXIS_MATH
- using UnityEngine;
- using System.Collections;
- // TODO camera zoom with mouse wheel AxisCamera
- // TODO gravity source
- /// <summary>
- /// used for calculating and organizing 3D axis math
- /// </summary>
- public class Axis {
- public Vector3 forward, up, position;
- private Vector3 right;
- public void SetIdentity() {
- up = Vector3.up;
- forward = Vector3.forward;
- right = Vector3.right;
- position = Vector3.zero;
- }
- public bool Equals(Transform t) {
- return up == t.up && forward == t.forward && position == t.position;
- }
- public Axis(Axis other) {
- up = other.up;
- right = other.right;
- forward = other.forward;
- position = other.position;
- }
- public Axis() { }
- public Vector3 GetRight() {
- return right;
- //return Vector3.Cross(up, forward).normalized;
- }
- #if SHOW_AXIS_MATH
- public LineRenderer DrawSimple(ref GameObject lineObj, Color c, float size) {
- const float epsilon = 0.001f;
- Vector3 u = up * size, f = forward * size;
- Vector3[] points = {
- position + u,
- position + u * epsilon,
- position + f * epsilon,
- position + f * (1 - epsilon),
- position + f,
- Vector3.zero };
- points[points.Length - 1] = Vector3.Lerp(points[3], points[0], 0.125f);
- LineRenderer lr = Lines.Make(ref lineObj, c, points, points.Length, .1f, .1f);
- return lr;
- }
- private Vector3[] thumbtack_points_base = null;
- public LineRenderer DrawThumbtack(ref GameObject lineObj, Color c, float size, float lineWidth) {
- const float epsilon = 0.001f;
- if(thumbtack_points_base == null) {
- Vector3 pstn = Vector3.zero;
- Vector3 fwrd = Vector3.forward * size;
- Vector3 rght = Vector3.right * size;
- Vector3 up__ = Vector3.up;
- float startAngle = (360.0f / 4) - (360.0f / 32);
- Vector3 v = Quaternion.AngleAxis(startAngle, up__) * fwrd;
- Lines.MakeArc(ref thumbtack_points_base, 32, up__, v, 360, pstn);
- Vector3 tip = pstn + fwrd * Mathf.Sqrt(2);
- thumbtack_points_base[0] = thumbtack_points_base[thumbtack_points_base.Length - 1];
- int m = (32 * 5 / 8) + 1;
- thumbtack_points_base[m++] = thumbtack_points_base[m] + (tip - thumbtack_points_base[m]) * (1 - epsilon);
- thumbtack_points_base[m++] = tip;
- int n = (32 * 7 / 8) + 1;
- while(n < 32) {
- thumbtack_points_base[m++] = thumbtack_points_base[n++];
- }
- Vector3 side = pstn + rght;
- thumbtack_points_base[m++] = thumbtack_points_base[m] + (side - thumbtack_points_base[m]) * (1 - epsilon);
- thumbtack_points_base[m++] = pstn + rght;
- thumbtack_points_base[m++] = pstn + rght * epsilon;
- thumbtack_points_base[m++] = pstn;
- thumbtack_points_base[m++] = pstn + up__ * size * (1 - epsilon);
- thumbtack_points_base[m++] = pstn + up__ * size;
- }
- LineRenderer lr = Lines.Make(ref lineObj, c, thumbtack_points_base, thumbtack_points_base.Length, lineWidth, lineWidth);
- lr.useWorldSpace = false;
- return lr;
- }
- #endif
- public void SetFrom(Transform t) {
- position = t.position;
- up = t.up;
- forward = t.forward;
- right = t.right;
- }
- public void SetFrom(Axis t) {
- position = t.position;
- up = t.up;
- forward = t.forward;
- right = t.right;
- }
- public void WriteInto(Transform t) {
- t.position = position;
- t.rotation = Quaternion.LookRotation(forward, up);
- }
- public void Turn(float t) {
- Quaternion turn = Quaternion.AngleAxis(t, up);
- forward = turn * forward;
- right = turn * right;
- }
- public void Pitch(float p) {
- Quaternion turn = Quaternion.AngleAxis(p, right);
- forward = turn * forward;
- up = turn * up;
- }
- public Quaternion GetUpright(Vector3 nextUp) {
- Vector3 r = Vector3.Cross(forward, nextUp);
- Vector3 fwd = Vector3.Cross(nextUp, r);
- return Quaternion.LookRotation(fwd, nextUp);
- }
- public Quaternion DeltaTo(Quaternion rotation) {
- Quaternion q = Quaternion.LookRotation(this.forward, this.up);
- return rotation * Quaternion.Inverse(q);
- }
- public Quaternion UprightDelta(Vector3 nextUp) {
- return DeltaTo(GetUpright(nextUp));
- }
- public void Rotate(Quaternion rotation) {
- forward = rotation * forward;
- up = rotation * up;
- right = rotation * right;
- }
- public void SetRotation(Quaternion rotation) {
- forward = rotation * Vector3.forward;
- up = rotation * Vector3.up;
- right = rotation * Vector3.right;
- }
- public void SetUpright(Vector3 up) {
- SetRotation(GetUpright(up));
- }
- }
- [System.Serializable]
- public struct TupleMinMax {
- public float min, max;
- public TupleMinMax(float min, float max) { this.min = min; this.max = max; }
- public float Difference(float value) {
- if(value > max) { return max - value; }
- if(value < min) { return min - value; }
- return 0;
- }
- public float Lerp(float t) { return (max - min) * t + min; }
- public float PercentageOf(float value) { return (value - min) / (max - min); }
- }
- public class AxisAgent : MonoBehaviour {
- /// <summary>how the body is oriented, which decides how key input creates movement impulses</summary>
- private Axis body = new Axis();
- /// <summary>how the face is oriented, which decides where the agent's head transform is looking (if there is one)</summary>
- private Axis face = new Axis();
- /// <summary>cached rigidbody of this agent</summary>
- private Rigidbody rb;
- [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."),
- ContextMenuItem("Setup Standard Axis Agent", "SetStandardAgent")]
- public Transform head;
- public Camera playerCamera;
- #if SHOW_AXIS_MATH
- GameObject line_body, line_head, line_stand, line_gravity, line_velocity, line_move;
- #endif
- void SetStandardAgent() {
- if(head == null) {
- head = gameObject.transform.FindChild("head");
- if(head == null) {
- head = new GameObject("head").transform;
- head.position = transform.position + transform.up * 0.875f;
- head.transform.SetParent(transform);
- head.localRotation = Quaternion.identity;
- }
- }
- if(playerCamera == null) {
- Transform t = head.FindChild("camera");
- playerCamera = (t != null) ? t.GetComponent<Camera>() : null;
- if(playerCamera == null) {
- playerCamera = new GameObject("camera").AddComponent<Camera>();
- playerCamera.transform.SetParent(head);
- playerCamera.transform.localPosition = new Vector3(0, 5, -10);
- playerCamera.transform.localRotation = Quaternion.Euler(25, 0, 0);
- }
- }
- Collider col = GetComponent<Collider>();
- if(col == null) {
- CapsuleCollider cap = gameObject.AddComponent<CapsuleCollider>();
- cap.height = 2;
- cap.radius = 0.5f;
- }
- rb = GetComponent<Rigidbody>();
- if(rb == null) {
- rb = gameObject.AddComponent<Rigidbody>();
- }
- }
- [System.Serializable]
- public struct Gravity {
- [SerializeField, Tooltip("How strong the gravity force is")]
- private float force;
- [SerializeField, Tooltip("The direction of the gravity force")]
- private Vector3 direction;
- private Vector3 calculatedForce;
- public Gravity(float force, Vector3 dir) {
- this.force = force; this.direction = dir; this.calculatedForce = force*dir; }
- public Vector3 GetDirection() { return direction; }
- public void SetDirection(Vector3 dir) { direction = dir; Refresh(); }
- public float GetForce() { return force; }
- public void SetForce(float force) { force = this.force; Refresh(); }
- public Vector3 Force() { return calculatedForce; }
- public void Negate() { calculatedForce = Vector3.zero; }
- public void Refresh() { calculatedForce = force * direction; }
- public Vector3 FixedUpdate(Rigidbody rb) { Vector3 v; rb.AddForce(v = Force()); return v; }
- }
- public Gravity gravity = new Gravity(20, Vector3.down);
- [System.Serializable]
- /// <summary>a system opposing gravity that allows an agent to hover very slightly over the ground, and move without friction</summary>
- public class Stand {
- [SerializeField, Tooltip("How far from the center (in the gravity direction) the standing volume goes")]
- private float distance = 1;
- [SerializeField, Tooltip("The radius of the standing volume")]
- private float radius = .375f;
- [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.")]
- public Transform marker;
- /// <summary>the actual spot being stood on</summary>
- private Vector3 groundContact;
- /// <summary>how the ground is sloped in 3D space</summary>
- private Vector3 surfaceNormal = Vector3.up;
- /// <summary>a 1D value describing how the ground is sloped. used to limit standing force</summary>
- private float surfaceAngle;
- /// <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>
- private float standRadiusEpsilon = 1 / 128.0f;
- /// <summary>used as fast adjustment to position using the physics system (without directly setting transform.position)</summary></summary>
- private Vector3 oneTimeForce;
- /// true if an adjustment to position needs to be made</summary>
- private bool usingOneTimeForce = false;
- /// <summary>true if standing volume is on the ground</summary>
- private bool isOnGround = true;
- [SerializeField, Tooltip("If true, will automatically match motion of transform being stood on")]
- private bool becomeParentedToPlatform = true;
- /// <summary>the actual transform being stood on</summary>
- Transform stoodOn = null;
- /// <summary>the velocity of the transform being stood on</summary>
- private Vector3 stoodOnVelocity;
- /// <summary>axis (position and rotation) of the transform being stood on</summary>
- Axis parentTransform = new Axis();
- /// <summary>the physics calculation done last frame to keep up with the stood-on transform</summary>
- Vector3 lastPlatformMove;
- public bool IsOnGround() { return isOnGround; }
- public void SetOnGround(bool onGround) { isOnGround = onGround; }
- public float GetSurfaceAngle() { return surfaceAngle; }
- public Vector3 GetSurfaceNormal() { return surfaceNormal; }
- public Vector3 GetGroundContact() { return groundContact; }
- /// <param name="a"></param>
- /// <returns>force to apply to a Rigidbody, using AddForce</returns>
- public Vector3 Force(AxisAgent a, ref Gravity g) {
- Vector3 force = Vector3.zero;
- if(usingOneTimeForce) {
- usingOneTimeForce = false;
- force -= oneTimeForce;
- }
- // if non-rigid-body parent has moved
- if(becomeParentedToPlatform && stoodOn != null && !parentTransform.Equals(stoodOn)) {
- a.rb.AddForce(-lastPlatformMove);
- stoodOnVelocity = stoodOn.position - parentTransform.position;
- lastPlatformMove = stoodOnVelocity / Time.deltaTime;
- parentTransform.SetFrom(stoodOn);
- a.rb.AddForce(lastPlatformMove);
- a.transform.position = a.transform.position + stoodOnVelocity;
- }
- Vector3 standingDirection = -g.GetDirection();
- Ray ray = new Ray(a.transform.position, -standingDirection);
- RaycastHit hitInfo = new RaycastHit();
- a.GetComponent<Collider>().enabled = false;
- if(!a.jumping.IsJumping() && Physics.SphereCast(ray, radius, out hitInfo, distance) && hitInfo.distance <= distance) {
- groundContact = hitInfo.point;
- surfaceNormal = hitInfo.normal;
- surfaceAngle = Vector3.Angle(standingDirection, surfaceNormal);
- if(Vector3.Dot(a.body.forward, surfaceNormal) > 0) {
- surfaceAngle *= -1;
- }
- Vector3 centerOfFoot = a.transform.position - standingDirection * distance;
- if(marker != null) {
- marker.position = centerOfFoot;
- }
- float dist = Vector3.Distance(groundContact, centerOfFoot);
- float groundOverlap = radius - dist;
- if(groundOverlap > 0) {
- // instead of setting the position, set the force needed to clip out, then remove that force next frame.
- usingOneTimeForce = true;
- oneTimeForce = (surfaceNormal * (groundOverlap - standRadiusEpsilon)) / (Time.deltaTime*Time.deltaTime);
- force += oneTimeForce;
- }
- float downForce = Vector2.Dot(-standingDirection, a.rb.velocity);
- if(!isOnGround && downForce > 0) {
- force += standingDirection * downForce / Time.deltaTime;
- }
- // reset double-jump variable if on the ground
- a.jumping.ResetJumpCount();
- isOnGround = true;
- g.Negate();
- if(becomeParentedToPlatform) {
- if(stoodOn != hitInfo.collider.transform) {
- stoodOn = hitInfo.collider.transform;
- parentTransform.SetFrom(stoodOn);
- }
- }
- } else {
- isOnGround = false;
- g.Refresh();
- if(becomeParentedToPlatform) {
- //if(stoodOn != null) print("no parent");
- stoodOn = null;
- parentTransform.SetIdentity();
- }
- }
- a.GetComponent<Collider>().enabled = true;
- return force;
- }
- public Vector3 FixedUpdate(Rigidbody rb, AxisAgent a, ref Gravity gravity) {
- Vector3 standForce = Force(a, ref gravity);
- rb.AddForce(standForce);
- return standForce;
- }
- }
- public Stand stand;
- [System.Serializable]
- public class Jump {
- public TupleMinMax height = new TupleMinMax(1, 5);
- public float fullJumpPressDuration = 0.5f;
- public int maxJumps = 2;
- /// how long the user has been holding jump
- float timeJumpHeld;
- /// how the jump is currently doing
- float currentJumpVelocity;
- float targetHeight;
- float heightReached;
- Vector3 jumpStart;
- bool jumpPeaked = false;
- /// whether or not the user is currently jumping
- bool jumpImpulseActive = false;
- int jumpsSoFar = 0;
- public enum JumpDirection { opposingGravity, opposingGroundSurface }
- GameObject jumpShow;
- float heightReachedTotal;
- public bool IsRequestingJump(AxisAgent a) { return !jumpImpulseActive && (a.input.jump || a.input.jumpHeld); }
- public bool IsJumping() { return currentJumpVelocity > 0; }
- public void ResetJumpCount() { jumpsSoFar = 0; }
- public Vector3 Force(AxisAgent a, ref Gravity g, ref Movement movement) {
- Vector3 force = Vector3.zero;
- Vector3 jumpDirection = -g.GetDirection();//onlyJumpAgainstGravity ? -g.GetDirection() : a.stand.GetSurfaceNormal();//
- // if the user wants to jump, and is allowed to jump again
- if(IsRequestingJump(a) && (jumpsSoFar < maxJumps)) {
- if(jumpsSoFar == 0) {
- jumpStart = a.transform.position;
- heightReachedTotal = 0;
- }
- heightReached = 0;
- timeJumpHeld = 0;
- jumpsSoFar++;
- targetHeight = height.min;
- float velocityRequiredToJump = Mathf.Sqrt(targetHeight * 2 * g.GetForce());
- currentJumpVelocity = velocityRequiredToJump;
- jumpPeaked = false;
- force = (jumpDirection * currentJumpVelocity) / Time.deltaTime;
- jumpImpulseActive = true;
- a.stand.SetOnGround(false);
- } else
- // if a jump is happening
- if(currentJumpVelocity > 0) {
- // handle jump height: the longer you hold jump, the higher you jump
- if(a.input.jumpHeld) {
- timeJumpHeld += Time.deltaTime;
- if(timeJumpHeld >= fullJumpPressDuration) {
- targetHeight = height.max;
- timeJumpHeld = fullJumpPressDuration;
- } else {
- targetHeight = height.Lerp(timeJumpHeld / fullJumpPressDuration);
- }
- if(heightReached < targetHeight) {
- float requiredJumpVelocity = Mathf.Sqrt((targetHeight - heightReached) * 2 * g.GetForce());
- float forceNeeded = requiredJumpVelocity - currentJumpVelocity;
- force += (jumpDirection * forceNeeded) / Time.deltaTime;
- currentJumpVelocity = requiredJumpVelocity;
- }
- } else {
- jumpImpulseActive = false;
- }
- }
- if(currentJumpVelocity > 0) {
- float moved = currentJumpVelocity * Time.deltaTime;
- heightReached += moved;
- heightReachedTotal += moved;
- #if SHOW_AXIS_MATH
- Lines.Make(ref jumpShow, Color.black, jumpStart, a.transform.position, .5f, 0f);
- #endif
- currentJumpVelocity -= g.GetForce() * Time.deltaTime;
- } else if(!jumpPeaked && !a.stand.IsOnGround()) {
- jumpPeaked = true;
- jumpImpulseActive = false;
- }
- return force;
- }
- public Vector3 FixedUpdate(Rigidbody rb, AxisAgent a, ref Gravity gravity, ref Movement movement) {
- Vector3 f = Force(a, ref gravity, ref movement);
- rb.AddForce(f);
- return f;
- }
- }
- public Jump jumping;
- [System.Serializable]
- public class Input {
- public enum Source { none, mouseAndKeyboard }
- [Tooltip("Where to get input from")]
- public Source source = Source.mouseAndKeyboard;
- /// [Header("Controller Variables, set automatically by reading controller input")]
- /// variable:+/-, forward:forward/backward, strafe:right/left, turn:right/left, pitch:down/up
- [HideInInspector] public float forward, strafe, turn, pitch;
- /// [Tooltip("button inputs")]
- [HideInInspector] public bool jump, jumpHeld, fire1, fire1Held, fire2, fire2Held, gravityToggle, fullStop, invertPitch, runHeld;
- public void ReleaseAllInput() {
- forward = strafe = turn = pitch = 0; //mouseWheel = 0;
- jump = jumpHeld = fire1 = fire1Held = fire2 = fire2Held = runHeld = false;
- }
- [System.Serializable]
- public class Sensitivity {
- [Tooltip("x mouse sensitivity")]
- public float horizontal = 10F;
- [Tooltip("y mouse sensitivity")]
- public float vertical = 5F;
- }
- public Sensitivity sensitivity;
- public void LoadInputFromController() {
- switch(source) {
- case Source.none: break;
- case Source.mouseAndKeyboard:
- forward = UnityEngine.Input.GetAxis("Vertical");
- strafe = UnityEngine.Input.GetAxis("Horizontal");
- turn = UnityEngine.Input.GetAxis("Mouse X") * sensitivity.horizontal;
- pitch = -UnityEngine.Input.GetAxis("Mouse Y") * sensitivity.vertical;
- jump = UnityEngine.Input.GetButtonDown("Jump");
- jumpHeld = UnityEngine.Input.GetButton("Jump");
- fire1 = UnityEngine.Input.GetButtonDown("Fire1");
- fire1Held = UnityEngine.Input.GetButton("Fire1");
- fire2 = UnityEngine.Input.GetButtonDown("Fire2");
- fire2Held = UnityEngine.Input.GetButton("Fire2");
- runHeld = UnityEngine.Input.GetKey(KeyCode.LeftShift)
- ||UnityEngine.Input.GetKey(KeyCode.RightShift);
- break;
- default:
- throw new System.Exception("unsupported controller " + source);
- }
- if(invertPitch) pitch *= -1;
- }
- }
- public Input input;
- [System.Serializable]
- public class Movement {
- ///<summary>current movement force</summary>
- private Vector3 force;
- ///<summary>velocity vector if viewed directly from above</summary>
- private Vector3 groundVelocity;
- ///<summary>groundVelocity.magnitude</summary>
- private Vector3 groundDir;
- ///<summary>the direction according to the input</summary>
- private Vector3 inputDir;
- ///<summary>how much rise/fall is going on</summary>
- private float verticalVelocity;
- ///<summary>velocity absent rise/fall</summary>
- private float groundSpeed;
- ///<summary>max speed based on input, taking directional speed limits into account</summary>
- private float maxInputSpeed;
- ///<summary>how much faster the agent is going than the current speed limit</summary>
- private float overSpeed;
- public Vector3 defaultUpDirection = Vector3.up;
- Axis movementAxis;
- ///<summary>how far the 3rd person camera can move back based on obstacles in the way</summary>
- float distanceLimitedTo;
- ///<summary>where the 3rd person camera is</summary>
- float actualDistance;
- #if SHOW_AXIS_MATH
- GameObject line_movementAxis;
- #endif
- public Vector3 CalculatedSurfaceUpDirection(AxisAgent a) {
- return a.stand.IsOnGround() ? a.stand.GetSurfaceNormal() : defaultUpDirection;
- }
- public Vector3 Force(AxisAgent a, Settings s) {
- if(movementAxis == null) movementAxis = new Axis();
- movementAxis.SetFrom(a.body);
- Vector3 upDirection = (Mathf.Abs(a.stand.GetSurfaceAngle()) < s.maxIncline) ? CalculatedSurfaceUpDirection(a) : defaultUpDirection;//
- if(a.body.up != upDirection) {
- movementAxis.SetUpright(upDirection);
- }
- #if SHOW_AXIS_MATH
- movementAxis.DrawThumbtack(ref line_movementAxis, Color.blue, 2, .0625f);
- movementAxis.WriteInto(line_movementAxis.transform);
- #endif
- force = Vector3.zero;
- verticalVelocity = Vector3.Dot(defaultUpDirection, a.rb.velocity);
- groundVelocity = a.rb.velocity - (defaultUpDirection * verticalVelocity);
- groundSpeed = groundVelocity.magnitude;
- groundDir = groundVelocity / groundSpeed;
- inputDir = Vector3.zero;
- // input impulse
- if(a.input.forward != 0 || a.input.strafe != 0) {
- maxInputSpeed = s.GetSpeedBasedOnInput(a.input, a.stand.GetSurfaceAngle());
- inputDir = movementAxis.forward * a.input.forward + movementAxis.GetRight() * a.input.strafe;
- inputDir.Normalize();
- float accelSpeedInCurrentDirection = Vector3.Dot(inputDir, groundVelocity);
- force = inputDir * ((accelSpeedInCurrentDirection > 0) ? s.acceleration : s.deceleration);
- if(groundSpeed > maxInputSpeed) {
- overSpeed = Vector3.Dot(groundDir, force);
- overSpeed += groundSpeed - maxInputSpeed;
- force += groundDir * -overSpeed;
- force = force.normalized * s.acceleration;
- }
- } else {
- // stop logic
- if(s.stopWhenNoInputGiven && groundSpeed != 0) {
- // figure out how much force is needed to stop
- float stopNeededToBringToZero = groundSpeed / Time.deltaTime;
- // if that is too much force, go with the max possible.
- if(stopNeededToBringToZero > s.deceleration) {
- force += -groundDir * s.deceleration;
- } else {
- force += -groundDir * stopNeededToBringToZero;
- }
- }
- }
- return force;
- }
- [System.Serializable]
- public class Settings {
- [Tooltip("What to use this movement profile. For example, there could be different movement settings for: walk, run, swim, climb, parkour, jump, fall, fly, hover")]
- public string name = "standard";
- [Tooltip("Maximum speed when pressing forward/backward")]
- public float speedForward = 15, speedBackward = 5;
- [Tooltip("Maximum speed when pressing strafe right/left")]
- public float speedRight = 10, speedLeft = 10;
- [Tooltip("Maximum acceleration when going forward/backward or strafing right/left")]
- public float acceleration = 20;
- [Tooltip("Maximum acceleration when bringing velocity toward zero")]
- public float deceleration = 30;
- [Tooltip("If true, movement calculations will cause a stop if inputs are released")]
- public bool stopWhenNoInputGiven = true;
- [Tooltip("the agent will not be able to move around on surfaces with an incline greater than this")]
- public float maxIncline = 50;
- [Tooltip("Percentage of speed possible as the current surface angle approaches the max incline")]
- public AnimationCurve inclineSpeedLoss = AnimationCurve.Linear(0,0,1,1);
- public float GetInclineSpeedAdjustment(float surfaceAngle) {
- return (surfaceAngle > 0) ? (1-inclineSpeedLoss.Evaluate(surfaceAngle / maxIncline)) :1;
- }
- [System.Serializable]
- public class HeadTurning {
- public TupleMinMax pitchLimit = new TupleMinMax(-140, 110);
- float currentVerticalAngle;
- // TODO allow the head transform to turn independently of the body, and make the body match if the turnLimit is reached or movement starts
- // public TupleMinMax turnLimit = new TupleMinMax(-50, 50);
- // public bool turnBodyBeyondMaximum = true;
- // public bool pitchBodyBeyondMaximum = false;
- public void Update(AxisAgent a) {
- if(a.input.pitch != 0) {
- a.face.SetFrom(a.head);
- a.face.Pitch(a.input.pitch);
- currentVerticalAngle = Vector3.Angle(a.body.up, a.face.up);
- if(Vector3.Dot(a.face.forward, a.body.up) > 0) {
- currentVerticalAngle *= -1;
- }
- float difference = pitchLimit.Difference(currentVerticalAngle);
- if(difference != 0) {
- a.face.Pitch(difference);
- currentVerticalAngle += difference;
- }
- a.face.WriteInto(a.head);
- thirdPersonCamera.CameraAngleUpdate(a.playerCamera, pitchLimit.PercentageOf(currentVerticalAngle));
- }
- float wheel = UnityEngine.Input.GetAxis("Mouse ScrollWheel");
- if(wheel != 0) {
- thirdPersonCamera.Move(a.playerCamera.transform, wheel);
- }
- }
- [System.Serializable]
- public class ThirdPersonCameraController {
- [Tooltip("How far the user wants the camera to be")]
- public float thirdPersonDistance = 10;
- [Tooltip("How quickly to move the camera to it's spot (lower is faster)"), Range(1, 128)]
- public float collisionSpeed = 8;
- public AnimationCurve viewAngle3rdPerson = DefaultCameraLookCurve();
- public static AnimationCurve DefaultCameraLookCurve() {
- AnimationCurve curve = new AnimationCurve();
- curve.AddKey(0, 0);
- curve.AddKey(.5f, .25f);
- curve.AddKey(.75f, .25f);
- curve.AddKey(1, 1);
- return curve;
- }
- public void Move(Transform camera, float change) {
- change *= 5;
- thirdPersonDistance -= change;
- if(thirdPersonDistance < change) {
- camera.position = camera.parent.position;
- thirdPersonDistance = 0;
- }
- }
- public void CameraAngleUpdate(Camera camera, float vertical) {
- vertical = viewAngle3rdPerson.Evaluate(vertical);
- float fieldOfView = camera.fieldOfView;
- float viewAngle = fieldOfView * vertical;
- camera.transform.localRotation = Quaternion.Euler(viewAngle - 30, 0, 0);
- }
- }
- public ThirdPersonCameraController thirdPersonCamera = new ThirdPersonCameraController();
- }
- public HeadTurning headTurning = new HeadTurning();
- [System.Serializable]
- public class RotationControl {
- [Tooltip("Be precise when turning with the mouse (no lag, or continued turning)")]
- public bool stopControlledAngularVelocity = true;
- [Tooltip("stop the spinning!")]
- public bool stopUncontrolledAngularVelocity = true;
- [Tooltip("set the player standing correctly if angle is perterbed")]
- public bool automaticallySetRightsideUp = true;
- [Tooltip("Force used to stop uncontrolled angular rotation")]
- public Vector3 angularVelocityCounterForce = new Vector3(15, 90, 15);
- [Tooltip("Force used to set player upright")]
- public float rightsideUpForce = 20;
- }
- public RotationControl rotationControl = new RotationControl();
- public Settings(float forward, float backward, float strafeRight, float strafeLeft, float accel, float decel) {
- this.speedForward = forward;
- this.speedBackward = backward;
- this.speedRight = strafeRight;
- this.speedLeft = strafeLeft;
- this.acceleration = accel;
- this.deceleration = decel;
- }
- public float GetSpeedBasedOnInput(Input i, float angle) {
- float result;
- if(i.forward == 0) {
- if(i.strafe == 0) result = 0;
- else if(i.strafe < 0) result = speedLeft;
- else result = speedRight;
- } else if(i.strafe == 0) {
- if(i.forward > 0) result = speedForward;
- else result = speedBackward;
- } else {
- float sum, f, s;
- if(i.forward > 0) {
- f = i.forward;
- if(i.strafe > 0) { s = i.strafe; sum = f + s; f /= sum; s /= sum; result = (f * speedForward) + (s * speedRight); }
- else { s = -i.strafe; sum = f + s; f /= sum; s /= sum; result = (f * speedForward) + (s * speedLeft); }
- } else {
- f = -i.forward;
- if(i.strafe > 0) { s = i.strafe; sum = f + s; f /= sum; s /= sum; result = (f * speedBackward) + (s * speedRight); }
- else { s = -i.strafe; sum = f + s; f /= sum; s /= sum; result = (f * speedBackward) + (s * speedLeft); }
- }
- }
- return result * GetInclineSpeedAdjustment(angle);
- }
- }
- public Settings currentSettings = new Settings(15, 5, 10, 10, 20, 60);
- private Vector3 angularJerk, standUpJerk;
- #if SHOW_AXIS_MATH
- /// visuals to help debug rotation forces
- GameObject rot_axis, rot_fwd, rot_up, rot_target;
- #endif
- public void FixedUpdateRotation(AxisAgent a, Input inputSource, Transform transform, Axis body, Rigidbody rb) {
- bool balancingRotation = false;
- // if the agent is controlling angular jerks, to emulate precise rotation, and there was an angular jerk last frame
- if(currentSettings.rotationControl.stopControlledAngularVelocity && angularJerk != Vector3.zero) {
- // find out how much that angular jerk has depreciated
- Vector3 adjusted = angularJerk - (angularJerk * rb.angularDrag * Time.deltaTime);
- // remove the jerk from the current angular rotation
- rb.AddTorque(-adjusted, ForceMode.Acceleration);
- // mark that there is no more angular jerk (it has been undone)
- angularJerk = Vector3.zero;
- // mark thata balancing rotation occured, which will prevent further balancing rotations
- balancingRotation = true;
- }
- if(currentSettings.rotationControl.automaticallySetRightsideUp && body.up != defaultUpDirection) {
- Quaternion uprightRotation = body.GetUpright(defaultUpDirection);
- Quaternion rotationToRightSelf = body.DeltaTo(uprightRotation);//body.UprightDelta(-gravity.GetDirection());
- Vector3 euler = rotationToRightSelf.eulerAngles;
- bool change = false;
- if(euler.x > 180) { euler.x -= 360; change = true; }
- if(euler.y > 180) { euler.y -= 360; change = true; }
- if(euler.z > 180) { euler.z -= 360; change = true; }
- const float epsilon = 1 / 128f;
- if(Mathf.Abs(euler.x) < epsilon) euler.x = 0;
- if(Mathf.Abs(euler.y) < epsilon) euler.y = 0;
- if(Mathf.Abs(euler.z) < epsilon) euler.z = 0;
- if(change) {
- //print(Time.time+":["+euler.x + " " + euler.y + " " + euler.z+"]");
- rotationToRightSelf = Quaternion.Euler(euler);
- }
- if(euler != Vector3.zero) {
- Vector3 rotAxis = Vector3.zero;
- float rotAngle;
- rotationToRightSelf.ToAngleAxis(out rotAngle, out rotAxis);
- #if SHOW_AXIS_MATH
- Lines.Make(ref rot_axis, Color.magenta, transform.position + rotAxis, transform.position - rotAxis, .1f, .1f);
- Lines.MakeArc(ref rot_fwd, Color.magenta, transform.position, rotAxis, transform.forward, rotAngle, 8, .1f, 0);
- Lines.MakeArc(ref rot_up, Color.magenta, transform.position, rotAxis, transform.up, rotAngle, 8, .1f, 0);
- Axis next = new Axis(body);
- next.Rotate(rotationToRightSelf);
- next.DrawSimple(ref rot_target, Color.black, 1);
- #endif
- float f = currentSettings.rotationControl.rightsideUpForce;
- euler.x = Mathf.Clamp(euler.x, -f, f);
- euler.y = Mathf.Clamp(euler.y, -f, f);
- euler.z = Mathf.Clamp(euler.z, -f, f);
- //print(Time.time + ": " + euler.x + " " + euler.y + " " + euler.z);
- if(standUpJerk != euler) {
- // divide by 2 because... it makes it work better. TODO more controlled right-side-up angular forces!
- rb.AddTorque(-standUpJerk/2, ForceMode.Acceleration);
- standUpJerk = euler;
- rb.AddTorque(euler, ForceMode.Acceleration);
- }
- }
- }
- // if the player wants to turn (horizontal motion)
- if(inputSource.turn != 0) {
- // turn horizontally
- angularJerk.y += inputSource.turn / Time.deltaTime;
- rb.AddTorque(angularJerk, ForceMode.Acceleration);
- } else {
- // if no turn is going on, and no balancing rotation is going on, and this agent should be stopping uncontrolled rotations
- if(currentSettings.rotationControl.stopUncontrolledAngularVelocity && !balancingRotation) {
- // if there is rotation
- Vector3 angle = rb.angularVelocity;
- if(angle != Vector3.zero) {
- // calculate a counter-force to stop rotation
- Vector3 counterForce = currentSettings.rotationControl.angularVelocityCounterForce;// Vector3.zero;
- counterForce.x = Mathf.Clamp(angle.x, -counterForce.x, counterForce.x);
- counterForce.y = Mathf.Clamp(angle.y, -counterForce.y, counterForce.y);
- counterForce.z = Mathf.Clamp(angle.z, -counterForce.z, counterForce.z);
- rb.AddTorque(-counterForce / Time.deltaTime, ForceMode.Acceleration);
- }
- }
- }
- }
- public Vector3 Force(AxisAgent a) {
- return Force(a, currentSettings);
- }
- public Vector3 FixedUpdate(Rigidbody rb, AxisAgent a) {
- Vector3 f = Force(a);
- rb.AddForce(f);
- return f;
- }
- public void MoveCameraBack(Transform camera) {
- float thirdPersonDistance = currentSettings.headTurning.thirdPersonCamera.thirdPersonDistance;
- if(thirdPersonDistance == 0) return;
- RaycastHit rh = new RaycastHit();
- Vector3 dir = -camera.parent.forward;
- float nextPosition = actualDistance;
- if(!Physics.Raycast(camera.parent.position, dir, out rh, thirdPersonDistance)) {
- distanceLimitedTo = thirdPersonDistance;
- } else {
- distanceLimitedTo = rh.distance;
- }
- if(actualDistance != distanceLimitedTo) {
- float delta = (distanceLimitedTo - actualDistance) / currentSettings.headTurning.thirdPersonCamera.collisionSpeed;
- nextPosition = (Mathf.Abs(delta) > 1 / 128.0f) ? (actualDistance + delta) : distanceLimitedTo;
- }
- if(nextPosition != actualDistance) {
- actualDistance = nextPosition;
- camera.localPosition = -Vector3.forward * actualDistance;
- }
- }
- public void Update(AxisAgent a) {
- currentSettings.headTurning.Update(a);
- MoveCameraBack(a.playerCamera.transform);
- }
- }
- [Tooltip("Variables that set how this agent moves, and keep track of orientation")]
- public Movement movement;
- public void SetMovementSettings(Movement.Settings moveSettings) {
- movement.currentSettings = moveSettings;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////
- /// uses Rigidbody.AddForce() to modify the rigidbody's behavior.
- void FixedUpdate () {
- // update axis
- body.SetFrom(transform);
- face.SetFrom(head);
- // update components. keep track of forces locally so visuals can be updated
- Vector3 moveForce = movement.FixedUpdate(rb, this);
- Vector3 gravityForce = gravity.FixedUpdate(rb);
- Vector3 jumpingForce = jumping.FixedUpdate(rb, this, ref gravity, ref movement);
- Vector3 standForce = stand.FixedUpdate(rb, this, ref gravity);
- // TODO RotationMovement
- movement.FixedUpdateRotation(this, input, transform, body, rb);
- #if SHOW_AXIS_MATH
- DrawAxisOnTransform(ref line_body, Color.cyan, body, transform, .75f);
- DrawAxisOnTransform(ref line_head, Color.yellow, face, head, .5f);
- Vector3 p = transform.position;
- Vector3 s = stand.GetGroundContact();
- Vector3 v = transform.position + rb.velocity;
- Lines.Make(ref line_gravity, Color.magenta, p, p + gravityForce, .2f, 0);
- Lines.Make(ref line_stand, Color.green, s, s + standForce, .4f, 0);
- Lines.Make(ref line_velocity, Color.blue, p, v, .1f, .1f);
- Lines.Make(ref line_move, Color.red, v, v + moveForce, .1f, .1f);
- #endif
- }
- #if SHOW_AXIS_MATH
- void DrawAxisOnTransform(ref GameObject line_, Color c, Axis a, Transform t, float size) {
- a.DrawThumbtack(ref line_, c, size, .1f);
- line_.transform.SetParent(t);
- line_.transform.localPosition = Vector3.zero;
- line_.transform.localRotation = Quaternion.identity;
- }
- #endif
- struct MoveSettingsManager {
- Movement.Settings defaultMoveSetting;
- [Tooltip("walk, run, swim, climb, parkour, jump, fall, fly, hover")]
- public Movement.Settings[] movementSettings;
- // TODO walk button, run button, climb button, parkour minimum speed, hover button, fly minimum speed, swim toggle
- }
- void Start() {
- rb = GetComponent<Rigidbody>();
- rb.useGravity = false;
- rb.angularDrag = 0;
- rb.maxAngularVelocity = 360;
- }
- void Update() {
- input.LoadInputFromController();
- movement.Update(this);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement