/// <summary> /// We smooth the input so that we don't start and stop immediately in the blend tree. That can create pops. /// </summary> protected void SmoothInput() { MotionState lState = mMotionController.State; // Convert the input to radial so we deal with keyboard and gamepad input the same. float lInputMax = (IsRunActive ? 1f : 0.5f); float lInputX = Mathf.Clamp(lState.InputX, -lInputMax, lInputMax); float lInputY = Mathf.Clamp(lState.InputY, -lInputMax, lInputMax); float lInputMagnitude = Mathf.Clamp(lState.InputMagnitudeTrend.Value, 0f, lInputMax); InputManagerHelper.ConvertToRadialInput(ref lInputX, ref lInputY, ref lInputMagnitude); // Smooth the input mInputX.Add(lInputX); mInputY.Add(lInputY); mInputMagnitude.Add(lInputMagnitude); // Modify the input values to add some lag mMotionController.State.InputX = mInputX.Average; mMotionController.State.InputY = mInputY.Average; mMotionController.State.InputMagnitudeTrend.Replace(mInputMagnitude.Average); }
/// <summary> /// Updates the motion over time. This is called by the controller /// every update cycle so animations and stages can be updated. /// </summary> /// <param name="rDeltaTime">Time since the last frame (or fixed update call)</param> /// <param name="rUpdateIndex">Index of the update to help manage dynamic/fixed updates. [0: Invalid update, >=1: Valid update]</param> public override void Update(float rDeltaTime, int rUpdateIndex) { mRotation = Quaternion.identity; // Grab the state info MotionState lState = mMotionController.State; // Convert the input to radial so we deal with keyboard and gamepad input the same. float lInputX = lState.InputX; float lInputY = lState.InputY; float lInputMag = lState.InputMagnitudeTrend.Value; InputManagerHelper.ConvertToRadialInput(ref lInputX, ref lInputY, ref lInputMag); //, (IsRunActive ? 1f : 0.5f)); // Ensure we support the stop delay. This way, we can get out // of the blend tree with a nice transition if (lState.InputMagnitudeTrend.Value < 0.4f) { // Only set the timer if it's not set yet if (mStopTime == 0f) { mStopInput.x = mInputX.Average; mStopInput.y = mInputY.Average; mStopTime = Time.time + _StopDelay; mInputX.Clear(mStopInput.x); mInputY.Clear(mStopInput.y); mInputMagnitude.Clear(Mathf.Sqrt((mInputX.Value * mInputX.Value) + (mInputY.Value * mInputY.Value))); } } // Clear the timer else { mStopTime = 0f; } // When we're processing normally, update all the input values if (mStopTime == 0f) { mInputX.Add(lInputX); mInputY.Add(lInputY); mInputMagnitude.Add(lInputMag); } // If we've reached our stop time, it's time to stop else if (Time.time > mStopTime) { // Determine how we'll stop based on the direction if (!(mMotionLayer._AnimatorStateID == STATE_IdlePoseOut)) { mMotionController.SetAnimatorMotionPhase(mMotionLayer._AnimatorLayerIndex, PHASE_STOP, 0, true); } // If we're already stopping, we can clear our movement info. We don't want // to clear the movement before the transition our our blend tree will drop to idle else { mStopTime = 0f; mInputX.Clear(); mInputY.Clear(); mInputMagnitude.Clear(); lState.AnimatorStates[mMotionLayer._AnimatorLayerIndex].MotionPhase = 0; lState.AnimatorStates[mMotionLayer._AnimatorLayerIndex].MotionParameter = 0; } } // Modify the input values to add some lag lState.InputX = mInputX.Average; lState.InputY = mInputY.Average; lState.InputMagnitudeTrend.Replace(mInputMagnitude.Average); // Finally, set the state value mMotionController.State = lState; // If we're not dealing with an ootii camera rig, we need to rotate to the camera here if (_RotateWithCamera && !(mMotionController.CameraRig is BaseCameraRig)) { OnCameraUpdated(rDeltaTime, rUpdateIndex, null); } if (!_RotateWithCamera && _RotateWithInput) { RotateUsingInput(rDeltaTime, ref mRotation); } // Allow the base class to render debug info base.Update(rDeltaTime, rUpdateIndex); }
/// <summary> /// Updates the motion over time. This is called by the controller /// every update cycle so animations and stages can be updated. /// </summary> /// <param name="rDeltaTime">Time since the last frame (or fixed update call)</param> /// <param name="rUpdateIndex">Index of the update to help manage dynamic/fixed updates. [0: Invalid update, >=1: Valid update]</param> public override void Update(float rDeltaTime, int rUpdateIndex) { mMovement = Vector3.zero; mRotation = Quaternion.identity; mNoInputElapsed = mNoInputElapsed + rDeltaTime; // If we're at the surface and grounded, we can't be going fast bool lAllowRunning = (!mSwimmerInfo.IsInShallowWater(mActorController.GroundingLayers)) && IsRunning; // Grab the state info MotionState lState = mMotionController.State; // Convert the input to radial so we deal with keyboard and gamepad input the same. float lInputX = lState.InputX; float lInputY = lState.InputY; float lInputMagnitude = lState.InputMagnitudeTrend.Value; InputManagerHelper.ConvertToRadialInput(ref lInputX, ref lInputY, ref lInputMagnitude, (lAllowRunning ? 1f : 0.5f)); // If input stops, we're going to keep our current value // and have the motion come to a smooth stop as needed if (lInputMagnitude > 0f) { mInputX.Add(lInputX); mInputY.Add(lInputY); mInputMagnitude.Add(lInputMagnitude); mNoInputElapsed = 0f; } // Use the averaged values for input lState.InputX = mInputX.Average; lState.InputY = mInputY.Average; lState.InputMagnitudeTrend.Replace(mInputMagnitude.Average); mMotionController.State = lState; // Determine how we'll stop based on the direction if (mMotionLayer._AnimatorStateID == STATE_SwimTree && mNoInputElapsed > 0.15f) { mMotionController.SetAnimatorMotionPhase(mMotionLayer._AnimatorLayerIndex, PHASE_STOP_SWIM, 0, true); } // We do this so we can re-enter the movement if needed if (mMotionLayer._AnimatorTransitionID == TRANS_SwimTree_TreadIdlePose) { mMotionController.SetAnimatorMotionPhase(mMotionLayer._AnimatorLayerIndex, 0, 0, true); } // Do a surface check to see if we're exiting the water float lWaterMovement = mSwimmerInfo.WaterSurface.position.y - mSwimmerInfo.WaterSurfaceLastPosition.y; if (mSwimmerInfo.TestExitWater(lWaterMovement)) { mMotionController.SetAnimatorMotionPhase(mMotionLayer._AnimatorLayerIndex, PHASE_STOP_IDLE, 0, true); return; } // If we're at the surface, we may need to move with the surface if (mSwimmerInfo.IsAtWaterSurface(0.5f)) { mMovement.y = mMovement.y + lWaterMovement; mSwimmerInfo.CreateRipples(mMotionController._Transform.position); } else { mSwimmerInfo.CreateUnderwaterEffect(mMotionController.Animator, HumanBodyBones.Head); } mSwimmerInfo.WaterSurfaceLastPosition = mSwimmerInfo.WaterSurface.position; // Move based on the buoyancy mMovement = mMovement + mSwimmerInfo.GetBuoyancy(rDeltaTime); // Move vertically float lAngle = 0f; if (mMotionController._InputSource != null) { float lUpSpeed = mMotionController._InputSource.GetValue(_UpAlias) * _VerticalSpeed; if (lUpSpeed != 0f) { lAngle = _MaxPitch; } if (mSwimmerInfo.IsAtWaterSurface(0f)) { lUpSpeed = 0f; } float lDownSpeed = mMotionController._InputSource.GetValue(_DownAlias) * -_VerticalSpeed; if (lDownSpeed != 0f) { lAngle = -_MaxPitch; } if (mActorController.IsGrounded) { lDownSpeed = 0f; } mMovement.y = mMovement.y + ((lUpSpeed + lDownSpeed) * rDeltaTime); } // If we're not using the keys, we may use the mouse if (_DiveWithCamera && lAngle == 0f) { float lUpSpeed = 0f; float lDownSpeed = 0f; float lAdjustedMaxPitch = _MaxPitch - 5f; float lCameraAngle = NumberHelper.GetHorizontalAngle(mMotionController._CameraTransform.forward, mMotionController._Transform.forward, mMotionController._CameraTransform.right); if (lCameraAngle > 5f) { lCameraAngle = lCameraAngle - 5f; lUpSpeed = (Mathf.Min(lCameraAngle, lAdjustedMaxPitch) / lAdjustedMaxPitch) * _VerticalSpeed; if (mSwimmerInfo.IsAtWaterSurface(0f)) { lUpSpeed = 0f; lCameraAngle = 0f; } } else if (lCameraAngle < -5f) { lCameraAngle = lCameraAngle + 5f; lDownSpeed = (Mathf.Max(lCameraAngle, -lAdjustedMaxPitch) / -lAdjustedMaxPitch) * -_VerticalSpeed; if (mActorController.IsGrounded) { lDownSpeed = 0f; lCameraAngle = 0f; } } if (lCameraAngle != 0f) { lAngle = Mathf.Clamp(lAngle + lCameraAngle, -_MaxPitch, _MaxPitch); mMovement.y = mMovement.y + ((lUpSpeed + lDownSpeed) * rDeltaTime); } } //Utilities.Debug.Log.ScreenWrite("Angle:" + lAngle.ToString("f3"), 14); // Rotate the body if we can RotateBody(-lAngle); // If we're not dealing with an ootii camera rig, we need to rotate to the camera here if (_RotateWithCamera && !(mMotionController.CameraRig is BaseCameraRig)) { OnCameraUpdated(rDeltaTime, rUpdateIndex, null); } // Rotate as needed if (!_RotateWithCamera && _RotateWithInput) { RotateUsingInput(rDeltaTime, ref mRotation); } }