/// <summary> /// Determines if we'll treat input as if we're strafing /// </summary> protected void SimulateStrafeInput() { // Direction we need to travel in Vector3 lDirection = mWaypointVector; lDirection.y = lDirection.y - _PathHeight; lDirection.Normalize(); // Determine our view Vector3 lVerticalDirection = Vector3.Project(lDirection, _Transform.up); Vector3 lLateralDirection = (lDirection - lVerticalDirection).normalized; mInputFromAvatarAngle = Vector3Ext.SignedAngle(_Transform.forward, lLateralDirection); // Determine our movement if (mTargetDistance > _StopDistance) { // Calculate our own slowing float lRelativeMoveSpeed = 1f; if (mIsInSlowDistance && _SlowFactor > 0f) { float lSlowPercent = (mTargetDistance - _StopDistance) / (_SlowDistance - _StopDistance); lRelativeMoveSpeed = ((1f - _SlowFactor) * lSlowPercent) + _SlowFactor; } // TRT 4/5/2016: Force the slow distance as an absolute value if (mIsInSlowDistance && _SlowFactor > 0f) { lRelativeMoveSpeed = _SlowFactor; } lLateralDirection = _Transform.InverseTransformDirection(lLateralDirection); mMovementX = lLateralDirection.x * lRelativeMoveSpeed; mMovementY = lLateralDirection.z * lRelativeMoveSpeed; } // Grab extra input information if we can if (mMotionController._CameraTransform == null) { mInputFromCameraAngle = mInputFromAvatarAngle; } else { Vector3 lInputForward = new Vector3(mMovementX, 0f, mMovementY); // We do the inverse tilt so we calculate the rotation in "natural up" space vs. "actor up" space. Quaternion lInvTilt = QuaternionExt.FromToRotation(_Transform.up, Vector3.up); // Camera forward in "natural up" Vector3 lCameraForward = lInvTilt * mMotionController._CameraTransform.forward; // Create a quaternion that gets us from our world-forward to our camera direction. Quaternion lToCamera = Quaternion.LookRotation(lCameraForward, lInvTilt * _Transform.up); // Transform joystick from world space to camera space. Now the input is relative // to how the camera is facing. Vector3 lMoveDirection = lToCamera * lInputForward; mInputFromCameraAngle = NumberHelper.GetHorizontalAngle(lCameraForward, lMoveDirection); } }
/// <summary> /// Calculate how much to move an rotate by /// </summary> /// <param name="rMove"></param> /// <param name="rRotate"></param> protected virtual void CalculateMove(Vector3 rWaypoint, ref Vector3 rMove, ref Quaternion rRotate) { float lDeltaTime = TimeManager.SmoothedDeltaTime; // Direction we need to travel in Vector3 lDirection = rWaypoint - transform.position; lDirection.y = lDirection.y - _PathHeight; lDirection.Normalize(); // Determine our rotation Vector3 lVerticalDirection = Vector3.Project(lDirection, transform.up); Vector3 lLateralDirection = lDirection - lVerticalDirection; float lYawAngle = Vector3Ext.SignedAngle(transform.forward, lLateralDirection); if (_RotationSpeed == 0f) { rRotate = Quaternion.AngleAxis(lYawAngle, transform.up); } else { rRotate = Quaternion.AngleAxis(Mathf.Sign(lYawAngle) * Mathf.Min(Mathf.Abs(lYawAngle), _RotationSpeed * lDeltaTime), transform.up); } // Determine the movement if (_UseNavMeshPosition) { rMove = mNavMeshAgent.nextPosition - transform.position; } // In this case, we'll calculate movement ourselves else { // Grab the base movement speed float lMoveSpeed = mRootMotionMovement.magnitude / lDeltaTime; if (lMoveSpeed == 0f) { lMoveSpeed = _MovementSpeed; } // Calculate our own slowing float lRelativeMoveSpeed = 1f; if (mIsInSlowDistance && _SlowFactor > 0f) { float lSlowPercent = (mTargetDistance - _StopDistance) / (_SlowDistance - _StopDistance); lRelativeMoveSpeed = ((1f - _SlowFactor) * lSlowPercent) + _SlowFactor; } // TRT 4/5/2016: Force the slow distance as an absolute value if (mIsInSlowDistance && _SlowFactor > 0f) { lMoveSpeed = _SlowFactor; lRelativeMoveSpeed = 1f; } // Set the final velocity based on the future rotation Quaternion lFutureRotation = transform.rotation * rRotate; rMove = lFutureRotation.Forward() * (lMoveSpeed * lRelativeMoveSpeed * lDeltaTime); } }
/// <summary> /// Allow the joint to render it's own GUI. This GUI is used /// for displaying and manipulating the joint itself. /// </summary> /// <returns>Reports if the object's value was changed</returns> public override bool OnInspectorManipulatorGUI(IKBoneModifier rModifier) { #if UNITY_EDITOR // Determine if the swing is changing if (mBone != null) { Vector3 lSwing = rModifier.Swing.eulerAngles; Vector3 lNewSwing = InspectorHelper.Vector3Fields("Swing", "Euler angles to swing the bone.", lSwing, true, true, false); if (lNewSwing != lSwing) { // Grab the amount that was just rotated by based on the current rotation. // We do this so the change is relative to the current swing rotation Vector3 lDeltaRotations = lNewSwing - lSwing; rModifier.Swing = rModifier.Swing * Quaternion.Euler(lDeltaRotations); rModifier.IsDirty = true; } // Determine if the twist is changing //float lTwist = mBone.Twist.eulerAngles.y; float lTwist = Vector3Ext.SignedAngle(Vector3.up, rModifier.Twist * Vector3.up, Vector3.forward); float lNewTwist = EditorGUILayout.FloatField("Twist", lTwist); if (_AllowTwist && lNewTwist != lTwist) { rModifier.Twist = Quaternion.AngleAxis(lNewTwist, Vector3.forward); rModifier.IsDirty = true; } // Reset the values if needed if (GUILayout.Button("reset rotation", EditorStyles.miniButton)) { rModifier.Swing = Quaternion.identity; rModifier.Twist = (_AllowTwist ? Quaternion.identity : mBone._Twist); rModifier.IsDirty = true; mBone._Transform.localRotation = mBone._BindRotation; } if (rModifier.IsDirty) { // Before we go to far, see if we are within the joint limits. If not, // we need to go back to a good position. bool lIsInLimits = ApplyLimits(ref rModifier.Swing, ref rModifier.Twist); if (lIsInLimits || QuaternionExt.IsEqual(rModifier.Swing, Quaternion.identity)) { mLastSwing = rModifier.Swing; mLastTwist = rModifier.Twist; } else { rModifier.Swing = mLastSwing; rModifier.Twist = mLastTwist; } } } #endif return(rModifier.IsDirty); }
// ************************************** EDITOR SUPPORT ************************************** /// <summary> /// Allow the constraint to render it's own GUI /// </summary> /// <returns>Reports if the object's value was changed</returns> public override bool OnInspectorConstraintGUI(bool rIsSelected) { bool lIsDirty = false; #if UNITY_EDITOR GUILayout.Space(5); // Determine if the swing is changing if (mBone != null) { Vector3 lSwing = _Swing.eulerAngles; Vector3 lNewSwing = InspectorHelper.Vector3Fields("Swing", "Euler angles to swing the bone.", lSwing, true, true, false); if (lNewSwing != lSwing) { lIsDirty = true; // Grab the amount that was just rotated by based on the current rotation. // We do this so the change is relative to the current swing rotation Vector3 lDeltaRotations = lNewSwing - lSwing; _Swing = _Swing * Quaternion.Euler(lDeltaRotations); } // Determine if the twist is changing float lTwist = Vector3Ext.SignedAngle(Vector3.up, _Twist * Vector3.up, Vector3.forward); float lNewTwist = EditorGUILayout.FloatField("Twist", lTwist); if (lNewTwist != lTwist) { lIsDirty = true; _Twist = Quaternion.AngleAxis(lNewTwist, Vector3.forward); } // Reset the values if needed if (GUILayout.Button("reset rotation", EditorStyles.miniButton)) { _Swing = Quaternion.identity; _Twist = Quaternion.identity; lIsDirty = true; } if (lIsDirty) { mBone.SetLocalRotation(_Swing, _Twist, 1f); } } #endif return(lIsDirty); }
/// <summary> /// This function renders out the handles that allow us to edit the twist limits. It /// isn't actually meant to change the twist itself /// </summary> /// <param name="rBone"></param> /// <param name="rMinAngle"></param> /// <param name="rMaxAngle"></param> /// <returns></returns> public static bool JointTwistLimitsHandle(BoneControllerBone rBone, ref float rMinAngle, ref float rMaxAngle) { bool lIsDirty = false; #if UNITY_EDITOR Vector3 lWorldPosition = rBone._Transform.position; Quaternion lWorldSwingRotation = rBone.WorldBindRotation * rBone._Swing; Color lGUIColor = GUI.color; Color lHandleColor = Handles.color; float lHandleScale = 0.2f; if (rBone.Skeleton.EditorAutoScaleHandles) { lHandleScale = HandleUtility.GetHandleSize(rBone.Transform.position) * HandlesHelper.HandleScale; } // Render the border of the angles float lTwistAngle = Vector3Ext.SignedAngle(Vector3.up, rBone._Twist * Vector3.up, Vector3.forward); Quaternion lActualRotation = Quaternion.AngleAxis(lTwistAngle, Vector3.forward); Quaternion lMinRotation = Quaternion.AngleAxis(rMinAngle, Vector3.forward); Quaternion lMaxRotation = Quaternion.AngleAxis(rMaxAngle, Vector3.forward); Vector3 lMinOffset = lWorldSwingRotation * lMinRotation * Vector3.up * lHandleScale; Vector3 lMaxOffset = lWorldSwingRotation * lMaxRotation * Vector3.up * lHandleScale; Handles.color = new Color(0.1f, 0.1f, 0.6f, 1f); Handles.DrawLine(lWorldPosition, lWorldPosition + lMinOffset); Handles.DrawLine(lWorldPosition, lWorldPosition + lMaxOffset); //Handles.DrawLine(lWorldPosition, lWorldPosition + (lWorldSwingRotation * rBone.Twist * Vector3.up * (lHandleScale * 1.1f))); // Render the solid of the angles Handles.color = new Color(0.25f, 0.60f, 0.95f, 0.1f); Handles.DrawSolidArc(lWorldPosition, rBone.Transform.rotation * rBone._BoneForward, lWorldSwingRotation * lMinRotation * Vector3.up, Mathf.Abs(rMinAngle) + rMaxAngle, lHandleScale); // Render text GUI.color = new Color(0.05f, 0.05f, 0.5f, 1f); Handles.Label(lWorldPosition + lMinOffset, "min:\r\n" + rMinAngle.ToString("0.00")); Handles.Label(lWorldPosition + lMaxOffset, "max:\r\n" + rMaxAngle.ToString("0.00")); Handles.Label(lWorldPosition + (lWorldSwingRotation * lActualRotation * Vector3.up * (lHandleScale * 1.3f)), " " + lTwistAngle.ToString("0.00")); // Reset GUI.color = lGUIColor; Handles.color = lHandleColor; #endif return lIsDirty; }
/// <summary> /// This function renders out the handles that allow us to edit the twist limits. It /// isn't actually meant to change the twist itself /// </summary> /// <param name="rBone"></param> /// <param name="rMinAngle"></param> /// <param name="rMaxAngle"></param> /// <returns></returns> public static bool JointSwingAxisLimitsHandle(BoneControllerBone rBone, Vector3 rAxis, float rMinAngle, float rMaxAngle) { bool lIsDirty = false; #if UNITY_EDITOR Vector3 lWorldPosition = rBone._Transform.position; Quaternion lWorldSwing = rBone.WorldBindRotation * rBone.Swing; Color lGUIColor = GUI.color; Color lHandleColor = Handles.color; float lHandleScale = 0.2f; if (rBone.Skeleton.EditorAutoScaleHandles) { lHandleScale = HandleUtility.GetHandleSize(rBone.Transform.position) * HandlesHelper.HandleScale; } // Render the border of the angles Quaternion lMinRotation = Quaternion.AngleAxis(rMinAngle, rAxis); Quaternion lMaxRotation = Quaternion.AngleAxis(rMaxAngle, rAxis); // We don't use the world swing since we want the rotation based on the bind position Vector3 lMinOffset = rBone.WorldBindRotation * lMinRotation * Vector3.forward * lHandleScale; Vector3 lMaxOffset = rBone.WorldBindRotation * lMaxRotation * Vector3.forward * lHandleScale; Handles.color = new Color(0.94118f, 0.39608f, 0.13333f, 1f); Handles.DrawLine(lWorldPosition, lWorldPosition + lMinOffset); Handles.DrawLine(lWorldPosition, lWorldPosition + lMaxOffset); // Render the solid of the angles Handles.color = new Color(0.94118f, 0.39608f, 0.13333f, 0.1f); Handles.DrawSolidArc(lWorldPosition, rBone.WorldBindRotation * rAxis, (rBone.WorldBindRotation * lMinRotation * Vector3.forward), Mathf.Abs(rMinAngle) + rMaxAngle, lHandleScale); // Draw text GUI.color = new Color(0.72549f, 0.30588f, 0.10588f, 1f); Handles.Label(lWorldPosition + lMinOffset, "min:\r\n" + rMinAngle.ToString("0.00")); Handles.Label(lWorldPosition + lMaxOffset, "max:\r\n" + rMaxAngle.ToString("0.00")); Vector3 lDirectionAxis = Vector3.Cross(rAxis, rBone._BindRotation * rBone._BoneForward); float lSwingAngle = Vector3Ext.SignedAngle(lDirectionAxis, rBone.Swing * lDirectionAxis, rAxis); Handles.Label(lWorldPosition + (lWorldSwing * (Vector3.forward * (lHandleScale * 1.3f))), " " + lSwingAngle.ToString("0.00")); // Reset GUI.color = lGUIColor; Handles.color = lHandleColor; #endif return lIsDirty; }
/// <summary> /// Allow the joint to render it's own GUI. This GUI is used /// for displaying and manipulating the joint itself. /// </summary> /// <returns>Reports if the object's value was changed</returns> public override bool OnInspectorManipulatorGUI(IKBoneModifier rModifier) { #if UNITY_EDITOR // Determine if the swing is changing if (mBone != null) { // Determine if the swing is changing Vector3 lSwing = rModifier.Swing.eulerAngles; Vector3 lNewSwing = InspectorHelper.Vector3Fields("Swing", "Euler angles to swing the bone.", lSwing, true, true, false); //lBoneForward.x == 0f, lBoneForward.y == 0f, lBoneForward.z == 0f); if (lNewSwing != lSwing) { // Grab the amount that was just rotated by based on the current rotation. // We do this so the change is relative to the current swing rotation Vector3 lDeltaRotations = lNewSwing - lSwing; rModifier.Swing = rModifier.Swing * Quaternion.Euler(lDeltaRotations); rModifier.IsDirty = true; } float lTwist = Vector3Ext.SignedAngle(Vector3.up, rModifier.Twist * Vector3.up, Vector3.forward); float lNewTwist = EditorGUILayout.FloatField("Twist", lTwist); if (lNewTwist != lTwist) { rModifier.Twist = Quaternion.AngleAxis(lNewTwist, Vector3.forward); rModifier.IsDirty = true; } // Reset the values if needed if (GUILayout.Button("reset rotation", EditorStyles.miniButton)) { rModifier.Swing = Quaternion.identity; rModifier.Twist = Quaternion.identity; rModifier.IsDirty = true; mBone._Transform.localRotation = mBone._BindRotation; } if (rModifier.IsDirty) { ApplyLimits(ref rModifier.Swing, ref rModifier.Twist); } } #endif return(rModifier.IsDirty); }
/// <summary> /// Apply any rotational limits to the local rotation so it /// meets the constraints of this bone type /// </summary> /// <param name="rBone">Bone being processed</param> /// <param name="rRotation">Target local rotation of the bone to be modified</param> public override bool ApplyLimits(ref Quaternion rSwing, ref Quaternion rTwist) { // We may need to get rid of the twisting associated with the swing. if (_PreventSwingTwisting) { // Create a longitudinal axis based on the initial bone // direction and the current rotation. This is the "local" direction // of the bone relative to the bind position. Vector3 lTargetBoneForward = rSwing * Vector3.forward; // If we are preserving the the automatic twisting, we can remove it // by grabing the simple rotation from the start position to the new position rSwing = Quaternion.FromToRotation(Vector3.forward, lTargetBoneForward); } // Only process the twist if it's allowed if (_AllowTwist) { // Test if we should limit the twist if (_LimitTwist && (_MinTwistAngle > -180 || _MaxTwistAngle < 180) && !rTwist.IsIdentity()) { float lTwistAngle = Vector3Ext.SignedAngle(Vector3.up, rTwist * Vector3.up, Vector3.forward); // Force the angle if it's exceeeded. if (lTwistAngle > _MaxTwistAngle) { rTwist = Quaternion.AngleAxis(_MaxTwistAngle, Vector3.forward); } else if (lTwistAngle < _MinTwistAngle) { rTwist = Quaternion.AngleAxis(_MinTwistAngle, Vector3.forward); } else { rTwist = Quaternion.AngleAxis(lTwistAngle, Vector3.forward); } } } else { rTwist = Quaternion.identity; } return(true); }
/// <summary> /// Calculate how much to move an rotate by /// </summary> /// <param name="rMove"></param> /// <param name="rRotate"></param> protected virtual void CalculateMove(Vector3 rWaypoint, ref Vector3 rMove, ref Quaternion rRotate) { float lDeltaTime = TimeManager.SmoothedDeltaTime; // Direction we need to travel in Vector3 lDirection = rWaypoint - transform.position; lDirection.Normalize(); // Determine our rotation Vector3 lVerticalDirection = Vector3.Project(lDirection, transform.up); Vector3 lLateralDirection = lDirection - lVerticalDirection; float lYawAngle = Vector3Ext.SignedAngle(transform.forward, lLateralDirection); if (_RotationSpeed == 0f) { rRotate = Quaternion.AngleAxis(lYawAngle, transform.up); } else { rRotate = Quaternion.AngleAxis(Mathf.Sign(lYawAngle) * Mathf.Min(Mathf.Abs(lYawAngle), _RotationSpeed * lDeltaTime), transform.up); } // Determine the movement float lMoveSpeed = _FastSpeed; if (mIsInSlowDistance && _SlowSpeed > 0f) { lMoveSpeed = _SlowSpeed; } // Set the final velocity based on the future rotation Quaternion lFutureRotation = transform.rotation * rRotate; rMove = lFutureRotation.Forward() * (lMoveSpeed * lDeltaTime); }
/// <summary> /// Determines how close the bone is from reaching its twist limit. Values /// are from -1 (at min limit) to 0 (at bind angle) to 1 (at max limit). /// </summary> public override float GetTwistStress(Quaternion rLocalTwist) { if (!_AllowTwist) { return(0f); } if (!_LimitTwist) { return(0f); } // Grab the current twist angle float lTwistAngle = Vector3Ext.SignedAngle(Vector3.up, rLocalTwist * Vector3.up, Vector3.forward); // Based on the angle (and limits), determine the stress if (lTwistAngle == 0f) { return(0f); } else if (lTwistAngle < 0f) { if (lTwistAngle <= _MinTwistAngle) { return(1f); } return(1f - ((_MinTwistAngle - lTwistAngle) / _MinTwistAngle)); } else { if (lTwistAngle >= _MaxTwistAngle) { return(1f); } return(1f - ((_MaxTwistAngle - lTwistAngle) / _MaxTwistAngle)); } }
/// <summary> /// Apply any rotational limits to the local rotation so it /// meets the constraints of this bone type /// </summary> /// <param name="rBone">Bone being processed</param> /// <param name="rRotation">Target local rotation of the bone to be modified</param> public override bool ApplyLimits(ref Quaternion rSwing, ref Quaternion rTwist) { //Vector3 lBoneForward = Vector3.forward; // mBone.BoneForward; // First thing we need to do is build our reach points based on the angles if (BoundaryPoints == null || BoundaryPoints.Count == 0) { ClearBoundaryPoints(); } // Track whether we're in bounds bool lIsInLimits = (!_LimitSwing || rSwing.IsIdentity()) && (!_LimitTwist || rTwist.IsIdentity()); // Test if we should limit the swing if (_LimitSwing && !rSwing.IsIdentity()) { // Create a longitudinal axis based on the initial bone // direction and the current rotation. This is the "local" direction // of the bone relative to the bind position. Vector3 lTargetBoneForward = rSwing * Vector3.forward; // Determine which reach cone our bone axis is in. We shouldn't // get a value less than 0. If so, the reach cones could be bad. // For now, we'll simply move on. int lCurrentSliceIndex = GetReachConeIndex(lTargetBoneForward); if (lCurrentSliceIndex >= 0) { // Test if we're actually valid inside the reach cone. If so, // we can move on. float lDot = Vector3.Dot(mReachCones[lCurrentSliceIndex].BoundaryPlane, lTargetBoneForward); if (lDot >= 0) { lIsInLimits = true; //mLastReachSliceIndex = lCurrentSliceIndex; // If we are preserving the the automatic twisting, we can remove it // by grabing the simple rotation from the start position to the new position if (_PreventSwingTwisting) { rSwing = Quaternion.FromToRotation(Vector3.forward, lTargetBoneForward); } } // If we're not in the reach cone, we need to pull the swing // back so that we are. else { Quaternion lTwist = Quaternion.identity; Quaternion lSwing = Quaternion.identity; // We may need to store the twisting associated with the swing. So grab it first if (!_PreventSwingTwisting) { rSwing.DecomposeSwingTwist(Vector3.forward, ref lSwing, ref lTwist); } // Find the max swing rotation Vector3 lCurrentBoneForward = Vector3.forward; // Determine the closest intersection along the direction // of old-L to new-L. This becomes the rotation we care about Vector3 lNewTargetBoneForward = GetReachConeExit(lCurrentBoneForward, lTargetBoneForward); rSwing = Quaternion.FromToRotation(lCurrentBoneForward, lNewTargetBoneForward); // If we are aren't preventing the twist, put the twist back if (!_PreventSwingTwisting) { rSwing = rSwing * lTwist; } } } } // Only process the twist if it's allowed if (_AllowTwist) { // Test if we should limit the twist if (_LimitTwist && (_MinTwistAngle > -180 || _MaxTwistAngle < 180) && !rTwist.IsIdentity()) { float lTwistAngle = Vector3Ext.SignedAngle(Vector3.up, rTwist * Vector3.up, Vector3.forward); // Force the angle if it's exceeeded. if (lTwistAngle > _MaxTwistAngle) { rTwist = Quaternion.AngleAxis(_MaxTwistAngle, Vector3.forward); } else if (lTwistAngle < _MinTwistAngle) { rTwist = Quaternion.AngleAxis(_MinTwistAngle, Vector3.forward); } else { rTwist = Quaternion.AngleAxis(lTwistAngle, Vector3.forward); } } } else { rTwist = Quaternion.identity; } return(lIsInLimits); }
/// <summary> /// Called every frame so the driver can process input and /// update the actor controller. /// </summary> protected virtual void Update() { // Ensure we have everything we need if (mActorController == null) { return; } if (mInputSource == null || !mInputSource.IsEnabled) { return; } // Initialize some variables Vector3 lMovement = Vector3.zero; Quaternion lRotation = Quaternion.identity; // ----------------------------------------------------------------- // INPUT // ----------------------------------------------------------------- // This is the horizontal movement of the mouse or Xbox controller's right stick //float lYaw = mInputSource.ViewX; // This is the WASD buttons or Xbox controller's left stick Vector3 lInput = new Vector3(mInputSource.MovementX, 0f, mInputSource.MovementY); // ----------------------------------------------------------------- // ATTACH TO WALL // ----------------------------------------------------------------- if (mIsInToWall) { Vector3 lToWallHit = mToWallPoint - transform.position; float lToWallHitNormal = Vector3.Angle(mActorController._Transform.up, mToWallNormal); // Move to the target and ensure we orient ourselves to the wall if (lToWallHit.magnitude > 0.03f || lToWallHitNormal > 0.5f) { mActorController.SetTargetGroundNormal(mToWallNormal); lMovement = lToWallHit.normalized * Mathf.Min(MovementSpeed * Time.deltaTime, lToWallHit.magnitude); mActorController.Move(lMovement); } // Once we're there, clean up else { mIsInToWall = false; mToWallPoint = Vector3.zero; mToWallNormal = Vector3.zero; mActorController.MaxSlopeAngle = mSavedMaxSlopeAngle; mActorController.OrientToGroundSpeed = mSavedOrientToGroundSpeed; mActorController.SetTargetGroundNormal(Vector3.zero); // Disable gravity temporarily mActorController.IsGravityEnabled = true; mActorController.FixGroundPenetration = true; } } else { // ----------------------------------------------------------------- // ROTATE // ----------------------------------------------------------------- // Set the target based on the input. This works because we're looking down // the world's z-axis. if (lInput.x < 0f) { mTargetForward = Vector3.left; } else if (lInput.x > 0f) { mTargetForward = Vector3.right; } // If we have a target forward start rotating towards it and ignore input if (mTargetForward.sqrMagnitude > 0f) { // Determine how much we need to rotate to get to the target float lTargetAngle = Vector3Ext.SignedAngle(mActorController.Yaw.Forward(), mTargetForward); // If there is no difference, we can turn off the target if (lTargetAngle == 0f) { mTargetForward = Vector3.zero; } else { // Grab the actual rotation angle based on our speed. However, make sure we don't overshoot the // angle. So, we do this logic to truncate it if we're only a tiny bit off. float lRotationAngle = Mathf.Sign(lTargetAngle) * Mathf.Min(RotationSpeed * Time.deltaTime, Mathf.Abs(lTargetAngle)); // Since the rotate function deals with the actor's yaw, we just to a vector3.up (it's // relative to the actor regardless of his orientation/tilt lRotation = Quaternion.AngleAxis(lRotationAngle, Vector3.up); // Rotate. mActorController.Rotate(lRotation); } } // ----------------------------------------------------------------- // MOVE // ----------------------------------------------------------------- // We get the tilt so we can add this up/down direction to the camera input. This helps // characters to not run off ramps since they are moving how they are facing (ie down a ramp) // vs. simply forward (off the ramp) Quaternion lTilt = QuaternionExt.FromToRotation(Vector3.up, mActorController._Transform.up); // Move based on WASD we add the tilt lMovement = lTilt * lInput * MovementSpeed * Time.deltaTime; mActorController.Move(lMovement); // ----------------------------------------------------------------- // JUMP // ----------------------------------------------------------------- // Only jump if the button is pressed and we're on the ground if (mInputSource.IsJustPressed("Jump")) { if (mActorController.State.IsGrounded) { mActorController.AddImpulse(mActorController._Transform.up * JumpForce); } } // ----------------------------------------------------------------- // TEST FOR WALL // ----------------------------------------------------------------- RaycastHit lWallHitInfo; if (TestForWallCollision(lMovement, out lWallHitInfo) || TestForWallAt90DegreeDrop(lMovement, out lWallHitInfo)) { // Save the tilt values mIsInToWall = true; mToWallPoint = lWallHitInfo.point; mToWallNormal = lWallHitInfo.normal; // Save and reset some AC values that will help us to tilt mSavedOrientToGroundSpeed = mActorController.OrientToGroundSpeed; mActorController.OrientToGroundSpeed = 0.75f; mSavedMaxSlopeAngle = mActorController.MaxSlopeAngle; mActorController.MaxSlopeAngle = 0f; // Disable gravity temporarily mActorController.IsGravityEnabled = false; mActorController.FixGroundPenetration = false; } } }
/// <summary> /// Converts the nav mesh agent data into psuedo-input that the motion controller /// will use to drive animations. /// </summary> protected void SimulateInput() { float lDeltaTime = TimeManager.SmoothedDeltaTime; // Direction we need to travel in Vector3 lDirection = mWaypointVector; lDirection.y = lDirection.y - _PathHeight; lDirection.Normalize(); // Determine our view Vector3 lVerticalDirection = Vector3.Project(lDirection, _Transform.up); Vector3 lLateralDirection = lDirection - lVerticalDirection; mInputFromAvatarAngle = Vector3Ext.SignedAngle(_Transform.forward, lLateralDirection); // Determine how much we simulate the view x. We temper it to make it smooth float lYawAngleAbs = Mathf.Min(Mathf.Abs(mInputFromAvatarAngle * lDeltaTime), mViewSpeedPer60FPSTick * lDeltaTime); if (lYawAngleAbs < EPSILON) { lYawAngleAbs = 0f; } mViewX = Mathf.Sign(mInputFromAvatarAngle) * lYawAngleAbs; // Determine our movement if (mTargetDistance > _StopDistance) { // Calculate our own slowing float lRelativeMoveSpeed = _NormalizedSpeed; if (mIsInSlowDistance && _SlowFactor > 0f) { float lSlowPercent = (mTargetDistance - _StopDistance) / (_SlowDistance - _StopDistance); lRelativeMoveSpeed = ((1f - _SlowFactor) * lSlowPercent) + _SlowFactor; } // TRT 4/5/2016: Force the slow distance as an absolute value if (mIsInSlowDistance && _SlowFactor > 0f) { lRelativeMoveSpeed = _SlowFactor; } mMovementY = 1f * lRelativeMoveSpeed; mMotionController.TargetNormalizedSpeed = lRelativeMoveSpeed; } // Grab extra input information if we can if (mMotionController._CameraTransform == null) { mInputFromCameraAngle = mInputFromAvatarAngle; } else { Vector3 lInputForward = new Vector3(mMovementX, 0f, mMovementY); // We do the inverse tilt so we calculate the rotation in "natural up" space vs. "actor up" space. Quaternion lInvTilt = QuaternionExt.FromToRotation(_Transform.up, Vector3.up); // Camera forward in "natural up" Vector3 lCameraForward = lInvTilt * mMotionController._CameraTransform.forward; // Create a quaternion that gets us from our world-forward to our camera direction. Quaternion lToCamera = Quaternion.LookRotation(lCameraForward, lInvTilt * _Transform.up); // Transform joystick from world space to camera space. Now the input is relative // to how the camera is facing. Vector3 lMoveDirection = lToCamera * lInputForward; mInputFromCameraAngle = NumberHelper.GetHorizontalAngle(lCameraForward, lMoveDirection); } }
/// <summary> /// Called every frame so the driver can process input and /// update the actor controller. /// </summary> protected virtual void Update() { // Ensure we have everything we need if (mActorController == null) { return; } if (mInputSource == null || !mInputSource.IsEnabled) { return; } // Initialize some variables Vector3 lMovement = Vector3.zero; Quaternion lRotation = Quaternion.identity; // ----------------------------------------------------------------- // INPUT // ----------------------------------------------------------------- // This is the horizontal movement of the mouse or Xbox controller's right stick //float lYaw = mInputSource.ViewX; // This is the WASD buttons or Xbox controller's left stick Vector3 lInput = new Vector3(mInputSource.MovementX, 0f, mInputSource.MovementY); // ----------------------------------------------------------------- // ROTATE // ----------------------------------------------------------------- // Set the target based on the input. This works because we're looking down // the world's z-axis. if (lInput.x < 0f) { mTargetForward = Vector3.left; } else if (lInput.x > 0f) { mTargetForward = Vector3.right; } // If we have a target forward start rotating towards it and ignore input if (mTargetForward.sqrMagnitude > 0f) { // Determine how much we need to rotate to get to the target float lTargetAngle = Vector3Ext.SignedAngle(mActorController.Yaw.Forward(), mTargetForward); // If there is no difference, we can turn off the target if (lTargetAngle == 0f) { mTargetForward = Vector3.zero; } else { // Grab the actual rotation angle based on our speed. However, make sure we don't overshoot the // angle. So, we do this logic to truncate it if we're only a tiny bit off. float lRotationAngle = Mathf.Sign(lTargetAngle) * Mathf.Min(RotationSpeed * Time.deltaTime, Mathf.Abs(lTargetAngle)); // Since the rotate function deals with the actor's yaw, we just to a vector3.up (it's // relative to the actor regardless of his orientation/tilt lRotation = Quaternion.AngleAxis(lRotationAngle, Vector3.up); // Rotate. mActorController.Rotate(lRotation); } } // ----------------------------------------------------------------- // MOVE // ----------------------------------------------------------------- // We get the tilt so we can add this up/down direction to the camera input. This helps // characters to not run off ramps since they are moving how they are facing (ie down a ramp) // vs. simply forward (off the ramp) Quaternion lTilt = QuaternionExt.FromToRotation(Vector3.up, mActorController._Transform.up); // Move based on WASD we add the tilt lMovement = lTilt * lInput * MovementSpeed * Time.deltaTime; mActorController.Move(lMovement); // ----------------------------------------------------------------- // JUMP // ----------------------------------------------------------------- // Only jump if the button is pressed and we're on the ground if (mInputSource.IsJustPressed("Jump")) { if (mActorController.State.IsGrounded) { mActorController.AddImpulse(mActorController._Transform.up * JumpForce); } } }