예제 #1
0
        //Actual movement code. Mostly isolated, except transform
        Results MoveCharacter(Results inpRes, Inputs inp, float deltaMultiplier, Vector3 maxSpeed)
        {
            //If controlled outside, return results with the current transform position.
            if (inpRes.flags & Flags.CONTROLLED_OUTSIDE)
            {
                inpRes.speed = myTransform.position - inpRes.position;
                return(new Results(myTransform.position, myTransform.rotation, hitNormal, inp.y, inpRes.speed, inpRes.flags, 0, 0, inpRes.ragdollTime, inp.timestamp));
            }

            //Calculates if ragdoll should be disabled
            if (inpRes.flags & Flags.RAGDOLL)
            {
                inpRes.speed = myTransform.position - inpRes.position;
                if (inpRes.speed.magnitude >= data.ragdollStopVelocity)
                {
                    inpRes.ragdollTime = inp.timestamp;
                }
                if (inp.timestamp - inpRes.ragdollTime >= ragdollTime)
                {
                    inpRes.flags &= ~Flags.RAGDOLL;
                }
                return(new Results(myTransform.position, myTransform.rotation, hitNormal, inp.y, inpRes.speed, inpRes.flags, 0, 0, inpRes.ragdollTime, inp.timestamp));
            }

            //Clamp camera angles
            inp.y = Mathf.Clamp(inp.y, dataInp.camMinY, dataInp.camMaxY);

            if (inp.x > 360f)
            {
                inp.x -= 360f;
            }
            else if (inp.x < 0f)
            {
                inp.x += 360f;
            }

            //Save current position and rotation to restore after the move
            Vector3    pos = myTransform.position;
            Quaternion rot = myTransform.rotation;

            //Set the position and rotation to the last results ones
            myTransform.position = inpRes.position;
            myTransform.rotation = inpRes.rotation;

            Vector3 tempSpeed = myTransform.InverseTransformDirection(inpRes.speed);

            myTransform.rotation = Quaternion.Euler(new Vector3(0, inp.x, 0));

            //Character sliding of surfaces
            if (!(inpRes.flags & Flags.IS_GROUNDED))
            {
                //inpRes.speed.x += (1f - inpRes.groundNormal.y) * inpRes.groundNormal.x * (inpRes.speed.y > 0 ? 0 : -inpRes.speed.y) * (1f - data.slideFriction);
                inpRes.speed.x += (1f - inpRes.groundNormal.y) * inpRes.groundNormal.x * (1f - data.slideFriction);
                //inpRes.speed.z += (1f - inpRes.groundNormal.y) * inpRes.groundNormal.z * (inpRes.speed.y > 0 ? 0 : -inpRes.speed.y) * (1f - data.slideFriction);
                inpRes.speed.z += (1f - inpRes.groundNormal.y) * inpRes.groundNormal.z * (1f - data.slideFriction);
            }

            Vector3 localSpeed  = myTransform.InverseTransformDirection(inpRes.speed);
            Vector3 localSpeed2 = Vector3.Lerp(myTransform.InverseTransformDirection(inpRes.speed), tempSpeed, data.velocityTransferCurve.Evaluate(Mathf.Abs(inpRes.rotation.eulerAngles.y - inp.x) / (deltaMultiplier * data.velocityTransferDivisor)));

            if (!(inpRes.flags & Flags.IS_GROUNDED) && data.strafing)
            {
                AirStrafe(ref inpRes, ref inp, ref deltaMultiplier, ref maxSpeed, ref localSpeed, ref localSpeed2);
            }
            else
            {
                localSpeed = localSpeed2;
            }

            BaseMovement(ref inpRes, ref inp, ref deltaMultiplier, ref maxSpeed, ref localSpeed);

            float tY = myTransform.position.y;

            //Convert the local coordinates to the world ones
            inpRes.speed = transform.TransformDirection(localSpeed);
            hitNormal    = new Vector3(0, 0, 0);

            //Set the speed to the curve values. Allowing to limit the speed
            inpRes.speed.x = data.finalSpeedCurve.Evaluate(inpRes.speed.x);
            inpRes.speed.y = data.finalSpeedCurve.Evaluate(inpRes.speed.y);
            inpRes.speed.z = data.finalSpeedCurve.Evaluate(inpRes.speed.z);

            //Move the controller
            controller.Move(inpRes.speed * deltaMultiplier);
            //This code continues after OnControllerColliderHit gets called (if it does)

            if (Vector3.Angle(Vector3.up, hitNormal) <= data.slopeLimit)
            {
                inpRes.flags |= Flags.IS_GROUNDED;
            }
            else
            {
                inpRes.flags &= ~Flags.IS_GROUNDED;
            }

            //float speed = inpRes.speed.y;
            inpRes.speed = (transform.position - inpRes.position) / deltaMultiplier;

            //if (inpRes.speed.y > 0)
            //	inpRes.speed.y = Mathf.Min (inpRes.speed.y, Mathf.Max(0, speed));
            //else
            //	inpRes.speed.y = Mathf.Max (inpRes.speed.y, Mathf.Min(0, speed));
            //inpRes.speed.y = speed;

            float gpt = 1f;
            float gp  = myTransform.position.y;

            //WIP, broken, Handles hitting ground while spacebar is pressed. It determines how much time was left to move based on at which height the player hit the ground. Some math involved.
            if (data.handleMidTickJump && !(inpRes.flags & Flags.IS_GROUNDED) && tY - gp >= 0 && inp.keys & Keys.JUMP && (controller.isGrounded || Physics.Raycast(myTransform.position + controller.center, Vector3.down, (controller.height / 2) + (controller.skinWidth * 1.5f))))
            {
                float oSpeed = inpRes.speed.y;
                gpt            = (tY - gp) / (-oSpeed);
                inpRes.speed.y = data.speedJump + ((Physics.gravity.y / 2) * Mathf.Abs((1f - gpt) * deltaMultiplier));
                Debug.Log(inpRes.speed.y + " " + gpt);
                controller.Move(myTransform.TransformDirection(0, inpRes.speed.y * deltaMultiplier, 0));
                inpRes.flags |= Flags.IS_GROUNDED;
                Debug.DrawLine(new Vector3(myTransform.position.x, gp, myTransform.position.z), myTransform.position, Color.blue, deltaMultiplier);
                inpRes.flags |= Flags.JUMPED;
            }

            //If snapping is enabled, then do it
            if (data.snapSize > 0f)
            {
                myTransform.position = new Vector3(Mathf.Round(myTransform.position.x * snapInvert) * data.snapSize, Mathf.Round(myTransform.position.y * snapInvert) * data.snapSize, Mathf.Round(myTransform.position.z * snapInvert) * data.snapSize);
            }

            //If grounded set the speed to the gravity
            if (inpRes.flags & Flags.IS_GROUNDED)
            {
                localSpeed.y = Physics.gravity.y * Mathf.Clamp(deltaMultiplier, 1f, 1f);
            }

            if (inpRes.speed.magnitude > data.ragdollStartVelocity)
            {
                inpRes.flags      |= Flags.RAGDOLL;
                inpRes.ragdollTime = inp.timestamp;
            }

            //Generate the return value
            inpRes = new Results(myTransform.position, myTransform.rotation, hitNormal, inp.y, inpRes.speed, inpRes.flags, gp, gpt, inpRes.ragdollTime, inp.timestamp);

            //Set back the position and rotation
            myTransform.position = pos;
            myTransform.rotation = rot;

            return(inpRes);
        }
예제 #2
0
        //The movement part
        public void BaseMovement(ref Results inpRes, ref Inputs inp, ref float deltaMultiplier, ref Vector3 maxSpeed, ref Vector3 localSpeed)
        {
            //Gets the target maximum speed
            if (inp.keys & Keys.CROUCH)
            {
                maxSpeed          = data.maxSpeedCrouch;
                inpRes.flags     |= Flags.CROUCHED;
                controller.height = Mathf.Clamp(controller.height - crouchSwitchMul, data.controllerHeightCrouch, data.controllerHeightNormal);
                controller.center = new Vector3(0, controller.height * data.controllerCentreMultiplier, 0);
            }
            else
            {
                if (inpRes.flags & Flags.CROUCHED)
                {
                    inpRes.flags &= ~Flags.CROUCHED;

                    Collider[] hits;

                    hits = Physics.OverlapCapsule(inpRes.position + new Vector3(0f, data.controllerHeightCrouch, 0f), inpRes.position + new Vector3(0f, data.controllerHeightNormal, 0f), controller.radius);

                    for (int i = 0; i < hits.Length; i++)
                    {
                        if (hits[i].transform.root != myTransform.root)
                        {
                            inpRes.flags |= Flags.CROUCHED;
                            inp.keys     |= Keys.CROUCH;
                            maxSpeed      = data.maxSpeedCrouch;
                            break;
                        }
                    }
                }
                if (!(inpRes.flags & Flags.CROUCHED))
                {
                    controller.height = Mathf.Clamp(controller.height + crouchSwitchMul, data.controllerHeightCrouch, data.controllerHeightNormal);
                    controller.center = new Vector3(0, controller.height * data.controllerCentreMultiplier, 0);
                }
                else
                {
                    controller.height = data.controllerHeightCrouch;
                    controller.center = new Vector3(0, controller.height * data.controllerCentreMultiplier, 0);
                }
            }

            if (!(inp.keys & Keys.JUMP))
            {
                inpRes.flags &= ~Flags.JUMPED;
            }

            if (inpRes.flags & Flags.IS_GROUNDED && inp.keys & Keys.JUMP && !(inpRes.flags & (Flags.CROUCHED | Flags.JUMPED)))
            {
                localSpeed.y = data.speedJump;
                if (!data.allowBunnyhopping)
                {
                    inpRes.flags |= Flags.JUMPED;
                }
            }
            else if (!(inpRes.flags & Flags.IS_GROUNDED))
            {
                localSpeed.y += Physics.gravity.y * deltaMultiplier;
            }
            else
            {
                localSpeed.y = -1f;
            }

            if (inpRes.flags & Flags.IS_GROUNDED)
            {
                if (Mathf.Sign(localSpeed.z * inp.inputs.y) == 1 && inp.inputs.y != 0 && Mathf.Abs(localSpeed.z) <= maxSpeed.z * Mathf.Abs(inp.inputs.y))
                {
                    localSpeed.z = Mathf.Clamp(localSpeed.z + (inp.inputs.y > 0 ? data.accelerationForward : -data.accelerationBack) * deltaMultiplier,
                                               -maxSpeed.z * Mathf.Abs(inp.inputs.y),
                                               maxSpeed.z * Mathf.Abs(inp.inputs.y));
                }
                else if (inp.inputs.y == 0 || Mathf.Abs(localSpeed.z) > maxSpeed.z * Mathf.Abs(inp.inputs.y))
                {
                    localSpeed.z = Mathf.Clamp(localSpeed.z + (data.decceleration * -Mathf.Sign(localSpeed.z)) * deltaMultiplier,
                                               localSpeed.z >= 0 ? 0 : -maxSpeed.z,
                                               localSpeed.z <= 0 ? 0 : maxSpeed.z);
                }
                else
                {
                    localSpeed.z = Mathf.Clamp(localSpeed.z + data.accelerationStop * inp.inputs.y * deltaMultiplier,
                                               localSpeed.z >= 0 ? -data.accelerationBack * deltaMultiplier : -maxSpeed.z,
                                               localSpeed.z <= 0 ? data.accelerationForward * deltaMultiplier : maxSpeed.z);
                }

                if (Mathf.Sign(localSpeed.x * inp.inputs.x) == 1 && inp.inputs.x != 0 && Mathf.Abs(localSpeed.x) <= maxSpeed.x * Mathf.Abs(inp.inputs.x))
                {
                    localSpeed.x = Mathf.Clamp(localSpeed.x + Mathf.Sign(inp.inputs.x) * data.accelerationSides * deltaMultiplier,
                                               -maxSpeed.x * ((Mathf.Sign(localSpeed.x * inp.inputs.x) == 1 && inp.inputs.x != 0) ? Mathf.Abs(inp.inputs.x) : 1f - Mathf.Abs(inp.inputs.x)),
                                               maxSpeed.x * ((Mathf.Sign(localSpeed.x * inp.inputs.x) == 1 && inp.inputs.x != 0) ? Mathf.Abs(inp.inputs.x) : 1f - Mathf.Abs(inp.inputs.x)));
                }
                else if (inp.inputs.x == 0 || Mathf.Abs(localSpeed.x) > maxSpeed.x * Mathf.Abs(inp.inputs.x))
                {
                    localSpeed.x = Mathf.Clamp(localSpeed.x + (data.decceleration * -Mathf.Sign(localSpeed.x)) * deltaMultiplier,
                                               localSpeed.x >= 0 ? 0 : -maxSpeed.x,
                                               localSpeed.x <= 0 ? 0 : maxSpeed.x);
                }
                else
                {
                    localSpeed.x = Mathf.Clamp(localSpeed.x + data.accelerationStop * inp.inputs.x * deltaMultiplier,
                                               localSpeed.x >= 0 ? -data.accelerationBack * deltaMultiplier : -maxSpeed.x,
                                               localSpeed.x <= 0 ? data.accelerationForward * deltaMultiplier : maxSpeed.x);
                }
            }
        }
예제 #3
0
        //Handles strafing in air
        public void AirStrafe(ref Results inpRes, ref Inputs inp, ref float deltaMultiplier, ref Vector3 maxSpeed, ref Vector3 localSpeed, ref Vector3 localSpeed2)
        {
            if (inpRes.flags & Flags.IS_GROUNDED)
            {
                return;
            }

            float tAccel = data.strafeAngleCurve.Evaluate(Mathf.Abs(inpRes.rotation.eulerAngles.y.ClampAngle() - inp.x.ClampAngle()) / deltaMultiplier);
            bool  rDir   = (inpRes.rotation.eulerAngles.y.ClampAngle() - inp.x.ClampAngle()) > 0;

            if (((inp.inputs.x > 0f && !rDir) || (inp.inputs.x < 0f && rDir)) && inp.inputs.y == 0)
            {
                if (localSpeed.z >= 0)
                {
                    localSpeed.z = localSpeed2.z + tAccel * data.strafeToSpeedCurve.Evaluate(Mathf.Abs(localSpeed.z) * strafeToSpeedCurveScaleMul);
                    localSpeed.x = localSpeed2.x;
                    localSpeed.y = localSpeed2.y;
                }
                else
                {
                    localSpeed.z = localSpeed.z + tAccel * data.strafeToSpeedCurve.Evaluate(Mathf.Abs(localSpeed.z) * strafeToSpeedCurveScaleMul);
                }
            }
            else if (((inp.inputs.x < 0f && !rDir) || inp.inputs.x > 0f && rDir) && inp.inputs.y == 0)
            {
                if (localSpeed.z <= 0)
                {
                    localSpeed.z = localSpeed2.z - tAccel * data.strafeToSpeedCurve.Evaluate(Mathf.Abs(localSpeed.z) * strafeToSpeedCurveScaleMul);
                    localSpeed.x = localSpeed2.x;
                    localSpeed.y = localSpeed2.y;
                }
                else
                {
                    localSpeed.z = localSpeed.z - tAccel * data.strafeToSpeedCurve.Evaluate(Mathf.Abs(localSpeed.z) * strafeToSpeedCurveScaleMul);
                }
            }
            else if (((inp.inputs.y > 0f && !rDir) || (inp.inputs.y < 0f && rDir)) && inp.inputs.x == 0)
            {
                if (localSpeed.x <= 0)
                {
                    localSpeed.x = localSpeed2.x - tAccel * data.strafeToSpeedCurve.Evaluate(Mathf.Abs(localSpeed.x) * strafeToSpeedCurveScaleMul);
                    localSpeed.z = localSpeed2.z;
                    localSpeed.y = localSpeed2.y;
                }
                else
                {
                    localSpeed.x = localSpeed.x - tAccel * data.strafeToSpeedCurve.Evaluate(Mathf.Abs(localSpeed.x) * strafeToSpeedCurveScaleMul);
                }
            }
            else if (((inp.inputs.y > 0f && rDir) || (inp.inputs.y < 0f && !rDir)) && inp.inputs.x == 0)
            {
                if (localSpeed.x >= 0)
                {
                    localSpeed.x = localSpeed2.x + tAccel * data.strafeToSpeedCurve.Evaluate(Mathf.Abs(localSpeed.x) * strafeToSpeedCurveScaleMul);
                    localSpeed.z = localSpeed2.z;
                    localSpeed.y = localSpeed2.y;
                }
                else
                {
                    localSpeed.x = localSpeed.x + tAccel * data.strafeToSpeedCurve.Evaluate(Mathf.Abs(localSpeed.x) * strafeToSpeedCurveScaleMul);
                }
            }
        }
예제 #4
0
 public NetworkDataTick(Inputs inp, Results res)
 {
     input  = new Extensions.SInputs(inp);
     result = new Extensions.SResults(res);
 }
예제 #5
0
		public void BaseMovement(ref Results inpRes, ref Inputs inp, ref float deltaMultiplier, ref Vector3 maxSpeed, ref Vector3 localSpeed) {
			if (inp.sprint)
				maxSpeed = data.maxSpeedSprint;
			if (inp.crouch) {
				maxSpeed = data.maxSpeedCrouch;
				if (!inpRes.crouch) {

					inpRes.crouch = true;

				}
				controller.height = Mathf.Clamp (controller.height - crouchSwitchMul, data.controllerHeightCrouch, data.controllerHeightNormal);
				controller.center = new Vector3 (0, controller.height * data.controllerCentreMultiplier, 0);
			} else {
				if (inpRes.crouch) {
					inpRes.crouch = false;

					Collider[] hits;

					hits = Physics.OverlapCapsule (inpRes.position + new Vector3(0f, data.controllerHeightCrouch, 0f), inpRes.position + new Vector3(0f, data.controllerHeightNormal, 0f), controller.radius);

					for (int i = 0; i < hits.Length; i++)
						if (hits [i].transform.root != myTransform.root) {
							inpRes.crouch = true;
							inp.crouch = true;
							maxSpeed = data.maxSpeedCrouch;
							break;
						}
				}
				if (!inpRes.crouch) {
					controller.height = Mathf.Clamp (controller.height + crouchSwitchMul, data.controllerHeightCrouch, data.controllerHeightNormal);
					controller.center = new Vector3 (0, controller.height * data.controllerCentreMultiplier, 0);
				} else {
					controller.height = data.controllerHeightCrouch;
					controller.center = new Vector3(0, controller.height * data.controllerCentreMultiplier, 0);
				}
			}
			inpRes.jumped = false;

			if (inpRes.isGrounded && inp.jump && !inpRes.crouch) {
				localSpeed.y = data.speedJump;
				inpRes.jumped = true;
			} else if (!inpRes.isGrounded)
				localSpeed.y += Physics.gravity.y * deltaMultiplier;
			else
				localSpeed.y = -1f;

			if (inpRes.isGrounded) {
				if (localSpeed.x >= 0f && inp.inputs.x > 0f) {
					localSpeed.x += data.accelerationSides * deltaMultiplier;
					if (localSpeed.x > maxSpeed.x)
						localSpeed.x = maxSpeed.x;
				} else if (localSpeed.x > 0f && (inp.inputs.x < 0f || localSpeed.x > maxSpeed.x)) {
					localSpeed.x -= data.accelerationStop * deltaMultiplier;
					if (localSpeed.x < 0)
						localSpeed.x = 0f;
				} else if (localSpeed.x <= 0f && inp.inputs.x < 0f) {
					localSpeed.x -= data.accelerationSides * deltaMultiplier;
					if (localSpeed.x < -maxSpeed.x)
						localSpeed.x = -maxSpeed.x;
				} else if (localSpeed.x < 0f && (inp.inputs.x > 0f || localSpeed.x < -maxSpeed.x)) {
					localSpeed.x += data.accelerationStop * deltaMultiplier;
					if (localSpeed.x > 0)
						localSpeed.x = 0f;
				} else if (localSpeed.x > 0f) {
					localSpeed.x -= data.decceleration * deltaMultiplier;
					if (localSpeed.x < 0f)
						localSpeed.x = 0f;
				} else if (localSpeed.x < 0f) {
					localSpeed.x += data.decceleration * deltaMultiplier;
					if (localSpeed.x > 0f)
						localSpeed.x = 0f;
				} else
					localSpeed.x = 0;

				if (localSpeed.z >= 0f && inp.inputs.y > 0f) {
					localSpeed.z += data.accelerationSides * deltaMultiplier;
					if (localSpeed.z > maxSpeed.z)
						localSpeed.z = maxSpeed.z;
				} else if (localSpeed.z > 0f && (inp.inputs.y < 0f || localSpeed.z > maxSpeed.z)) {
					localSpeed.z -= data.accelerationStop * deltaMultiplier;
					if (localSpeed.z < 0)
						localSpeed.z = 0f;
				} else if (localSpeed.z <= 0f && inp.inputs.y < 0f) {
					localSpeed.z -= data.accelerationSides * deltaMultiplier;
					if (localSpeed.z < -maxSpeed.z)
						localSpeed.z = -maxSpeed.z;
				} else if (localSpeed.z <= 0f && (inp.inputs.y > 0f || localSpeed.z < -maxSpeed.z)) {
					localSpeed.z += data.accelerationStop * deltaMultiplier;
					if (localSpeed.z > 0)
						localSpeed.z = 0f;
				} else if (localSpeed.z > 0f) {
					localSpeed.z -= data.decceleration * deltaMultiplier;
					if (localSpeed.z < 0f)
						localSpeed.z = 0f;
				} else if (localSpeed.z < 0f) {
					localSpeed.z += data.decceleration * deltaMultiplier;
					if (localSpeed.z > 0f)
						localSpeed.z = 0f;
				} else
					localSpeed.z = 0;
			}
		}
예제 #6
0
		void CmdSendInputs (Inputs inp, Results res) {
		#else
		void CmdSendInputs (Inputs inp) {
		#endif
			#if (SIMULATE)
			#if (CLIENT_TRUST)
			StartCoroutine (SendInputs (inp, res));
			#else
			StartCoroutine (SendInputs (inp));
			#endif
		}
		#if (CLIENT_TRUST)
		IEnumerator SendInputs (Inputs inp, Results res) {
		#else
		IEnumerator SendInputs (Inputs inp) {
		#endif
			yield return new WaitForSeconds (UnityEngine.Random.Range (0.21f, 0.28f));
			#endif

			if (!isLocalPlayer) {

				if (clientInputs.Count > data.clientInputsBuffer)
					clientInputs.RemoveAt (0);

				if (!ClientInputsContainTimestamp (inp.timestamp))
					clientInputs.Add (inp);

				#if (CLIENT_TRUST)
				tempResults = res;
				#endif

				currentTFixedUpdates += sendUpdates;

				if (data.debug && lastTick + 1 != inp.timestamp && lastTick != -1) {
					Debug.Log ("Missing tick " + lastTick + 1);
				}
				lastTick = inp.timestamp;
			}
		}

		[ClientRpc]
		void RpcSendResults (Results res) {

			if (isServer)
				return;
			#if (SIMULATE)
			StartCoroutine (SendResults (res));
		}

		IEnumerator SendResults (Results res) {
			yield return new WaitForSeconds (UnityEngine.Random.Range (0.21f, 0.38f));
			#endif

			if (isLocalPlayer) {

				foreach (Results t in clientResults) {
					if (t.timestamp == res.timestamp)
						Debug_UI.UpdateUI (posEnd, res.position, t.position, currentTick, res.timestamp);
				}

				if (serverResultList.Count > data.serverResultsBuffer)
					serverResultList.RemoveAt (0);

				if (!ServerResultsContainTimestamp (res.timestamp))
					serverResultList.Add (res);

				serverResults = SortServerResultsAndReturnFirst ();

				if (serverResultList.Count >= data.serverResultsBuffer)
					reconciliate = true;

			} else {
				currentTick++;

				if (!isServer) {
					serverResults = res;
					onTickUpdate.Invoke (res);
				}

				if (currentTick > 2) {
					serverResults = res;
					posStart = posEnd;
					rotStart = rotEnd;
					headStartRot = headEndRot;
					headEndRot = res.camX;
					//if (Time.fixedTime - 2f > startTime)
						startTime = Time.fixedTime;
					//else
					//	startTime = Time.fixedTime - ((Time.fixedTime - startTime) / (Time.fixedDeltaTime * _sendUpdates) - 1) * (Time.fixedDeltaTime * _sendUpdates);
					posEnd = posEndO;
					rotEnd = rotEndO;
					groundPointTime = serverResults.groundPointTime;
					posEndG = serverResults.groundPoint;
					posEndO = serverResults.position;
					rotEndO = serverResults.rotation;
				} else {
					startTime = Time.fixedTime;
					serverResults = res;
					posStart = serverResults.position;
					rotStart = serverResults.rotation;
					headStartRot = headEndRot;
					headEndRot = res.camX;
					posEnd = posStart;
					rotEnd = rotStart;
					groundPointTime = serverResults.groundPointTime;
					posEndG = posEndO.y;
					posEndO = posStart;
					rotEndO = rotStart;

				}
			}
		}

		Results SortServerResultsAndReturnFirst () {

			Results tempRes;

			for (int x = 0; x < serverResultList.Count; x++) {
				for (int y = 0; y < serverResultList.Count - 1; y++) {
					if (serverResultList [y].timestamp > serverResultList [y + 1].timestamp) {
						tempRes = serverResultList [y + 1];
						serverResultList [y + 1] = serverResultList [y];
						serverResultList [y] = tempRes;
					}
				}
			}

			if (serverResultList.Count > data.serverResultsBuffer)
				serverResultList.RemoveAt (0);


			return serverResultList [0];
		}

		bool ServerResultsContainTimestamp (int timeStamp) {
			for (int i = 0; i < serverResultList.Count; i++) {
				if (serverResultList [i].timestamp == timeStamp)
					return true;
			}

			return false;
		}

		Inputs SortClientInputsAndReturnFirst () {

			Inputs tempInp;

			for (int x = 0; x < clientInputs.Count; x++) {
				for (int y = 0; y < clientInputs.Count - 1; y++) {
					if (clientInputs [y].timestamp > clientInputs [y + 1].timestamp) {
						tempInp = clientInputs [y + 1];
						clientInputs [y + 1] = clientInputs [y];
						clientInputs [y] = tempInp;
					}
				}
			}

			if (clientInputs.Count > data.clientInputsBuffer)
				clientInputs.RemoveAt (0);


			return clientInputs [0];
		}

		bool ClientInputsContainTimestamp (int timeStamp) {
			for (int i = 0; i < clientInputs.Count; i++) {
				if (clientInputs [i].timestamp == timeStamp)
					return true;
			}

			return false;
		}

		//Function which replays the old inputs if prediction errors occur
		void Reconciliate () {

			for (int i = 0; i < clientResults.Count; i++) {
				if (clientResults [i].timestamp == serverResults.timestamp) {
					clientResults.RemoveRange (0, i);
					for (int o = 0; o < clientInputs.Count; o++) {
						if (clientInputs [o].timestamp == serverResults.timestamp) {
							clientInputs.RemoveRange (0, o);
							break;
						}
					}
					break;
				}
			}

			tempResults = serverResults;

			controller.enabled = true;

			for (int i = 1; i < clientInputs.Count - 1; i++) {
				tempResults = MoveCharacter (tempResults, clientInputs [i], Time.fixedDeltaTime * sendUpdates, data.maxSpeedNormal);
			}

			groundPointTime = tempResults.groundPointTime;
			posEnd = tempResults.position;
			rotEnd = tempResults.rotation;
			posEndG = tempResults.groundPoint;

		}

		//Input gathering
		void Update () {
			if (isLocalPlayer) {
				if (inputsInterface == null)
					throw (new UnityException ("inputsInterface is not set!"));
				
				curInput.inputs.x = inputsInterface.GetMoveX ();
				curInput.inputs.y = inputsInterface.GetMoveY ();

				curInput.x = inputsInterface.GetMouseX ();
				curInput.y = inputsInterface.GetMouseY ();

				curInput.jump = inputsInterface.GetJump ();
				curInput.sprint = inputsInterface.GetSprint ();

				curInput.crouch = inputsInterface.GetCrouch ();
			
			}
		}

		//This is where the ticks happen
		void FixedUpdate () {

			if (data.strafeToSpeedCurveScale != _strafeToSpeedCurveScale) {
				_strafeToSpeedCurveScale = data.strafeToSpeedCurveScale;
				strafeToSpeedCurveScaleMul = 1f / data.strafeToSpeedCurveScale;
			}

			if (isLocalPlayer || isServer) {
				currentFixedUpdates++;
			}

			if (isLocalPlayer && currentFixedUpdates >= sendUpdates) {
				currentTick++;

				if (!isServer) {
					onTickUpdate.Invoke (lastResults);
					clientResults.Add (lastResults);
				}

				if (clientInputs.Count >= data.inputsToStore)
					clientInputs.RemoveAt (0);

				clientInputs.Add (curInput);
				curInput.timestamp = currentTick;

				posStart = myTransform.position;
				rotStart = myTransform.rotation;
				startTime = Time.fixedTime;

				if (reconciliate) {
					Reconciliate ();
					lastResults = tempResults;
					reconciliate = false;
				}
					
				controller.enabled = true;
				lastResults = MoveCharacter (lastResults, clientInputs [clientInputs.Count - 1], Time.fixedDeltaTime * _sendUpdates, data.maxSpeedNormal);

				#if (CLIENT_TRUST)
				CmdSendInputs (clientInputs [clientInputs.Count - 1], lastResults);
				#else
				CmdSendInputs (clientInputs [clientInputs.Count - 1]);
				#endif
				if (data.debug)
					onTickUpdateDebug.Invoke(clientInputs [clientInputs.Count - 1], lastResults);

				controller.enabled = false;
				posEnd = lastResults.position;
				groundPointTime = lastResults.groundPointTime;
				posEndG = lastResults.groundPoint;
				rotEnd = lastResults.rotation;
			}

			if (isServer && currentFixedUpdates >= sendUpdates && (currentTFixedUpdates >= sendUpdates || isLocalPlayer)) {

				if (isLocalPlayer) {
					onTickUpdate.Invoke (lastResults);
					RpcSendResults (lastResults);
				}

				if (!isLocalPlayer && clientInputs.Count > 0) {
					currentFixedUpdates -= sendUpdates;
					currentTFixedUpdates -= sendUpdates;
					//if (clientInputs.Count == 0)
					//	clientInputs.Add (curInputServer);
					//clientInputs[clientInputs.Count - 1] = curInputServer;
					curInput = SortClientInputsAndReturnFirst ();

					posStart = myTransform.position;
					rotStart = myTransform.rotation;
					startTime = Time.fixedTime;
					controller.enabled = true;
					serverResults = MoveCharacter (serverResults, curInput, Time.fixedDeltaTime * _sendUpdates, data.maxSpeedNormal);
					#if (CLIENT_TRUST)
					if (serverResults.timestamp == tempResults.timestamp && Vector3.SqrMagnitude(serverResults.position-tempResults.position) <= data.clientPositionToleration * data.clientPositionToleration && Vector3.SqrMagnitude(serverResults.speed-tempResults.speed) <= data.clientSpeedToleration * data.clientSpeedToleration && ((serverResults.isGrounded == tempResults.isGrounded) || !data.clientGroundedMatch) && ((serverResults.crouch == tempResults.crouch) || !data.clientCrouchMatch))
						serverResults = tempResults;
					#endif
					groundPointTime = serverResults.groundPointTime;
					posEnd = serverResults.position;
					rotEnd = serverResults.rotation;
					posEndG = serverResults.groundPoint;
					controller.enabled = false;

					onTickUpdate.Invoke (serverResults);
					if (data.debug)
						onTickUpdateDebug.Invoke(curInput, serverResults);
					RpcSendResults (serverResults);
				}

			}

			if (isLocalPlayer && currentFixedUpdates >= sendUpdates)
				currentFixedUpdates = 0;
		}

		//This is where all the interpolation happens
		void LateUpdate () {
			if (data.movementType == MoveType.UpdateOnceAndLerp) {
				if (isLocalPlayer || isServer || (Time.time - startTime) / (Time.fixedDeltaTime * _sendUpdates) <= 1f) {
					interpPos = Vector3.Lerp (posStart, posEnd, (Time.time - startTime) / (Time.fixedDeltaTime * _sendUpdates));
					//if ((Time.time - startTime) / (Time.fixedDeltaTime * _sendUpdates) <= groundPointTime)
					//	interpPos.y = Mathf.Lerp (posStart.y, posEndG, (Time.time - startTime) / (Time.fixedDeltaTime * _sendUpdates * groundPointTime));
					//else
					//	interpPos.y = Mathf.Lerp (posStart.y, posEndG, (Time.time - startTime + (groundPointTime * Time.fixedDeltaTime * _sendUpdates)) / (Time.fixedDeltaTime * _sendUpdates * (1f - groundPointTime)));

					myTransform.rotation = Quaternion.Lerp (rotStart, rotEnd, (Time.time - startTime) / (Time.fixedDeltaTime * _sendUpdates));
					if (isLocalPlayer)
						myTransform.rotation = Quaternion.Euler (myTransform.rotation.eulerAngles.x, curInput.x, myTransform.rotation.eulerAngles.z);
					myTransform.position = interpPos;
				} else {
					myTransform.position = Vector3.Lerp (posEnd, posEndO, (Time.time - startTime) / (Time.fixedDeltaTime * _sendUpdates) - 1f);
					myTransform.rotation = Quaternion.Lerp (rotEnd, rotEndO, (Time.time - startTime) / (Time.fixedDeltaTime * _sendUpdates) - 1f);
				}
			} else {
				myTransform.position = posEnd;
				myTransform.rotation = rotEnd;
			}
		}

		//Data not to be messed with. Needs to be outside the function due to OnControllerColliderHit
		Vector3 hitNormal;

		//Actual movement code. Mostly isolated, except transform
		Results MoveCharacter (Results inpRes, Inputs inp, float deltaMultiplier, Vector3 maxSpeed) {

			inp.y = Mathf.Clamp (curInput.y, dataInp.camMinY, dataInp.camMaxY);

			if (inp.x > 360f)
				inp.x -= 360f;
			else if (inp.x < 0f)
				inp.x += 360f;

			Vector3 pos = myTransform.position;
			Quaternion rot = myTransform.rotation;

			myTransform.position = inpRes.position;
			myTransform.rotation = inpRes.rotation;

			Vector3 tempSpeed = myTransform.InverseTransformDirection (inpRes.speed);

			myTransform.rotation = Quaternion.Euler (new Vector3 (0, inp.x, 0));

			//Character sliding of surfaces
			if (!inpRes.isGrounded) {
				inpRes.speed.x += (1f - inpRes.groundNormal.y) * inpRes.groundNormal.x * (inpRes.speed.y > 0 ? 0 : -inpRes.speed.y) * (1f - data.slideFriction);
				inpRes.speed.x += (1f - inpRes.groundNormal.y) * inpRes.groundNormal.x * (inpRes.speed.y < 0 ? 0 : inpRes.speed.y) * (1f - data.slideFriction);
				inpRes.speed.z += (1f - inpRes.groundNormal.y) * inpRes.groundNormal.z * (inpRes.speed.y > 0 ? 0 : -inpRes.speed.y) * (1f - data.slideFriction);
				inpRes.speed.z += (1f - inpRes.groundNormal.y) * inpRes.groundNormal.z * (inpRes.speed.y < 0 ? 0 : -inpRes.speed.y) * (1f - data.slideFriction);
			}

			Vector3 localSpeed = myTransform.InverseTransformDirection (inpRes.speed);
			Vector3 localSpeed2 = Vector3.Lerp (myTransform.InverseTransformDirection (inpRes.speed), tempSpeed, data.velocityTransferCurve.Evaluate (Mathf.Abs (inpRes.rotation.eulerAngles.y - inp.x) / (deltaMultiplier * data.velocityTransferDivisor)));

			if (!inpRes.isGrounded && data.strafing)
				AirStrafe (ref inpRes, ref inp, ref deltaMultiplier, ref maxSpeed, ref localSpeed, ref localSpeed2);
			else
				localSpeed = localSpeed2;

			BaseMovement (ref inpRes, ref inp, ref deltaMultiplier, ref maxSpeed, ref localSpeed);

			float tY = myTransform.position.y;

			inpRes.speed = transform.TransformDirection (localSpeed);
			hitNormal = new Vector3 (0, 0, 0);

			inpRes.speed.x = data.finalSpeedCurve.Evaluate (inpRes.speed.x);
			inpRes.speed.y = data.finalSpeedCurve.Evaluate (inpRes.speed.y);
			inpRes.speed.z = data.finalSpeedCurve.Evaluate (inpRes.speed.z);

			controller.Move (inpRes.speed * deltaMultiplier);
			//This code continues after OnControllerColliderHit gets called (if it does)

			if (Vector3.Angle (Vector3.up, hitNormal) <= data.slopeLimit)
				inpRes.isGrounded = true;
			else
				inpRes.isGrounded = false;

			//float speed = inpRes.speed.y;
			inpRes.speed = (transform.position - inpRes.position) / deltaMultiplier;
			//inpRes.speed.y = speed;

			float gpt = 1f;
			float gp = myTransform.position.y;

			//WIP, broken, Handles hitting ground while spacebar is pressed. It determines how much time was left to move based on at which height the player hit the ground. Some math involved.
			if (data.handleMidTickJump && !inpRes.isGrounded && tY - gp >= 0 && inp.jump && (controller.isGrounded || Physics.Raycast (myTransform.position + controller.center, Vector3.down, (controller.height / 2) + (controller.skinWidth * 1.5f)))) {
				float oSpeed = inpRes.speed.y;
				gpt = (tY - gp) / (-oSpeed);
				inpRes.speed.y = data.speedJump + ((Physics.gravity.y / 2) * Mathf.Abs((1f - gpt) * deltaMultiplier));
				Debug.Log (inpRes.speed.y + " " + gpt);
				controller.Move (myTransform.TransformDirection (0, inpRes.speed.y * deltaMultiplier, 0));
				inpRes.isGrounded = true;
				Debug.DrawLine (new Vector3( myTransform.position.x, gp, myTransform.position.z), myTransform.position, Color.blue, deltaMultiplier);
				inpRes.jumped = true;
			}

			if (data.snapSize > 0f)
				myTransform.position = new Vector3 (Mathf.Round (myTransform.position.x * snapInvert) * data.snapSize, Mathf.Round (myTransform.position.y * snapInvert) * data.snapSize, Mathf.Round (myTransform.position.z * snapInvert) * data.snapSize);


			if (inpRes.isGrounded)
				localSpeed.y = Physics.gravity.y * Mathf.Clamp(deltaMultiplier, 1f, 1f);

			inpRes = new Results (myTransform.position, myTransform.rotation, hitNormal, inp.y, inpRes.speed, inpRes.isGrounded, inpRes.jumped, inpRes.crouch, gp, gpt, inp.timestamp);

			myTransform.position = pos;
			myTransform.rotation = rot;

			return inpRes;
		}

		//The part which determines if the controller was hit or not
		void OnControllerColliderHit (ControllerColliderHit hit) {
			hitNormal = hit.normal;
		}