/// <summary> /// When we want to rotate based on the camera direction, we need to tweak the actor /// rotation AFTER we process the camera. Otherwise, we can get small stutters during camera rotation. /// /// This is the only way to keep them totally in sync. It also means we can't run any of our AC processing /// as the AC already ran. So, we do minimal work here /// </summary> /// <param name="rDeltaTime"></param> /// <param name="rUpdateCount"></param> /// <param name="rCamera"></param> private void OnCameraUpdated(float rDeltaTime, int rUpdateIndex, BaseCameraRig rCamera) { if (mMotionController._CameraTransform == null) { return; } float lToCameraAngle = Vector3Ext.HorizontalAngleTo(mMotionController._Transform.forward, mMotionController._CameraTransform.forward, mMotionController._Transform.up); if (!mLinkRotation && Mathf.Abs(lToCameraAngle) <= _RotationSpeed * rDeltaTime) { mLinkRotation = true; } if (!mLinkRotation) { float lRotationAngle = Mathf.Abs(lToCameraAngle); float lRotationSign = Mathf.Sign(lToCameraAngle); lToCameraAngle = lRotationSign * Mathf.Min(_RotationSpeed * rDeltaTime, lRotationAngle); } Quaternion lRotation = Quaternion.AngleAxis(lToCameraAngle, Vector3.up); mActorController.Yaw = mActorController.Yaw * lRotation; mActorController._Transform.rotation = mActorController.Tilt * mActorController.Yaw; }
/// <summary> /// Allows the motor to process after the camera and controller have completed /// </summary> public override void PostRigLateUpdate() { base.PostRigLateUpdate(); // Determine if we rotate the anchor to match our rotation if (_RotateAnchor && Anchor != null) { if (_RotateAnchorAlias.Length == 0 || RigController.InputSource == null || RigController.InputSource.IsPressed(_RotateAnchorAlias)) { if (mCharacterController != null) { float lToCameraAngle = Vector3Ext.HorizontalAngleTo(Anchor.forward, RigController.Transform.forward, Anchor.up); Quaternion lRotation = Quaternion.AngleAxis(lToCameraAngle, Vector3.up); mCharacterController.Yaw = mCharacterController.Yaw * lRotation; Anchor.rotation = mCharacterController.Tilt * mCharacterController.Yaw; } else { float lToCameraAngle = Vector3Ext.HorizontalAngleTo(Anchor.forward, RigController.Transform.forward, Anchor.up); Quaternion lRotation = Quaternion.AngleAxis(lToCameraAngle, Vector3.up); Anchor.rotation = Anchor.rotation * lRotation; } } } }
/// <summary> /// Allows the motor to process after the camera and controller have completed /// </summary> public override void PostRigLateUpdate() { base.PostRigLateUpdate(); Transform lAnchorTransform = Anchor; // If the anchor updates its rotation based on the rig, we need to get the final position of the rig if (_IsActorMatchingRotation && (RigController.ActiveMotor == this)) { RigController._Transform.position = lAnchorTransform.position + (lAnchorTransform.rotation * AnchorOffset) + (lAnchorTransform.rotation * _Offset); RigController._Transform.rotation = Quaternion.Euler(RigController._Transform.rotation.eulerAngles.x, lAnchorTransform.rotation.eulerAngles.y, 0f); } if (_RotateAnchor && lAnchorTransform != null) { bool lIsRotatingAnchor = _RotateAnchorAlias.Length == 0 || RigController.InputSource == null || RigController.InputSource.IsPressed(_RotateAnchorAlias); if (lIsRotatingAnchor || mWasRotatingAnchor) { if (mCharacterController != null) { float lToCameraAngle = Vector3Ext.HorizontalAngleTo(lAnchorTransform.forward, RigController.Transform.forward, lAnchorTransform.up); Quaternion lRotation = Quaternion.AngleAxis(lToCameraAngle, Vector3.up); mCharacterController.Yaw = mCharacterController.Yaw * lRotation; lAnchorTransform.rotation = mCharacterController.Tilt * mCharacterController.Yaw; } else { float lToCameraAngle = Vector3Ext.HorizontalAngleTo(lAnchorTransform.forward, RigController.Transform.forward, lAnchorTransform.up); Quaternion lRotation = Quaternion.AngleAxis(lToCameraAngle, Vector3.up); lAnchorTransform.rotation = lAnchorTransform.rotation * lRotation; } mAnchorLastRotation = lAnchorTransform.rotation; // Since we are no longer rotating, we need to clear our out values if (!lIsRotatingAnchor) { _Euler.y = LocalYaw; _Euler.x = LocalPitch; _EulerTarget = _Euler; mViewVelocityY = 0f; mViewVelocityX = 0f; mWasRotatingAnchor = false; } } } else { mWasRotatingAnchor = false; } //Debug.Log("Char Yaw:" + RigController.Anchor.rotation.eulerAngles.y.ToString("f5") + " Anchor Yaw:" + Anchor.rotation.eulerAngles.y.ToString("f5") + " Cam Yaw:" + RigController._Transform.eulerAngles.y.ToString("f5")); }
/// <summary> /// Look at the incoming message to determine if it means we should react /// </summary> /// <param name="rMessage"></param> public override void OnMessageReceived(IMessage rMessage) { if (rMessage == null) { return; } if (rMessage.IsHandled) { return; } if (mActorController.State.Stance != EnumControllerStance.TRAVERSAL) { return; } if (rMessage is CombatMessage) { CombatMessage lCombatMessage = rMessage as CombatMessage; // Attack messages if (lCombatMessage.Attacker == mMotionController.gameObject) { } // Defender messages else if (lCombatMessage.Defender == mMotionController.gameObject) { if (rMessage.ID == CombatMessage.MSG_DEFENDER_DAMAGED) { if (!mIsActive) { Vector3 lLocalPosition = mMotionController._Transform.InverseTransformPoint(lCombatMessage.HitPoint); Vector3 lLocalDirection = (lLocalPosition - Vector3.zero).normalized; float lAttackAngle = Vector3Ext.HorizontalAngleTo(Vector3.forward, lLocalDirection, Vector3.up); mMotionController.ActivateMotion(this, (int)lAttackAngle); } else { mMotionController.SetAnimatorMotionPhase(mMotionLayer._AnimatorLayerIndex, PHASE_START, Parameter, true); } rMessage.IsHandled = true; } } } else if (rMessage is DamageMessage) { if (rMessage.ID == CombatMessage.MSG_DEFENDER_DAMAGED) { mMotionController.ActivateMotion(this, 0); rMessage.IsHandled = true; } } }
/// <summary> /// Rotate to the specified target's position over time /// </summary> /// <param name="rTarget">Transform we are rotating to</param> /// <param name="rSpeed">Degrees per second to rotate</param> /// <param name="rDeltaTime">Current delta time</param> /// <param name="rRotation">Resulting delta rotation needed to get to the target</param> protected void RotateToTarget(Transform rTarget, float rSpeed, float rDeltaTime, ref Quaternion rRotation) { Vector3 lNewPosition = mMotionController._Transform.position + (mMotionController._Transform.rotation * mMotionController.RootMotionMovement); Vector3 lToTarget = rTarget.position - lNewPosition; float lSpeed = (rSpeed > 0f ? rSpeed : _ToTargetRotationSpeed); float lToAnchorAngle = Vector3Ext.HorizontalAngleTo(mMotionController._Transform.forward, lToTarget.normalized, mMotionController._Transform.up); lToAnchorAngle = Mathf.Sign(lToAnchorAngle) * Mathf.Min(lSpeed * rDeltaTime, Mathf.Abs(lToAnchorAngle)); rRotation = Quaternion.AngleAxis(lToAnchorAngle, Vector3.up); }
/// <summary> /// When we want to rotate based on the camera direction, we need to tweak the actor /// rotation AFTER we process the camera. Otherwise, we can get small stutters during camera rotation. /// /// This is the only way to keep them totally in sync. It also means we can't run any of our AC processing /// as the AC already ran. So, we do minimal work here /// </summary> /// <param name="rDeltaTime"></param> /// <param name="rUpdateCount"></param> /// <param name="rCamera"></param> private void OnCameraUpdated(float rDeltaTime, int rUpdateCount, BaseCameraRig rCamera) { if (mMotionController._CameraTransform == null) { return; } float lToCameraAngle = Vector3Ext.HorizontalAngleTo(mMotionController._Transform.forward, mMotionController._CameraTransform.forward, mMotionController._Transform.up); float lRotationAngle = Mathf.Abs(lToCameraAngle); float lRotationSign = Mathf.Sign(lToCameraAngle); if (!mLinkRotation && lRotationAngle <= (_RotationSpeed / 60f) * TimeManager.Relative60FPSDeltaTime) { mLinkRotation = true; } // Record the velocity for our idle pivoting if (lRotationAngle < 1f) { float lVelocitySign = Mathf.Sign(mYawVelocity); mYawVelocity = mYawVelocity - (lVelocitySign * rDeltaTime * 10f); if (Mathf.Sign(mYawVelocity) != lVelocitySign) { mYawVelocity = 0f; } } else { mYawVelocity = lRotationSign * 12f; } // If we're not linked, rotate smoothly if (!mLinkRotation) { lToCameraAngle = lRotationSign * Mathf.Min((_RotationSpeed / 60f) * TimeManager.Relative60FPSDeltaTime, lRotationAngle); } Quaternion lRotation = Quaternion.AngleAxis(lToCameraAngle, Vector3.up); mActorController.Yaw = mActorController.Yaw * lRotation; mActorController._Transform.rotation = mActorController.Tilt * mActorController.Yaw; }
/// <summary> /// Rotates the body to point towards the cross hairs /// </summary> protected void RotateChestToTargetForward(Vector3 rTarget, float rWeight) { if (rWeight == 0f) { return; } float lHAngle = 0f; float lVAngle = 0f; Transform lBody = mMotionController._Transform; Transform lCamera = mMotionController._CameraTransform; Transform lSpine = mMotionController.Animator.GetBoneTransform(HumanBodyBones.Spine); Transform lChest = mMotionController.Animator.GetBoneTransform(HumanBodyBones.Chest); // The target direction helps determine how we'll rotate the arm Vector3 lTargetDirection = (lCamera != null ? lCamera.forward : rTarget); lHAngle = _HorizontalAimAngle * rWeight; lVAngle = (_VerticalAimAngle + Vector3Ext.HorizontalAngleTo(lBody.forward, lTargetDirection, lBody.right)) * rWeight; lVAngle = Mathf.Clamp(lVAngle, -65f, 65f); if (lSpine != null && lChest != null) { lHAngle = lHAngle * 0.5f; } if (lSpine != null && lChest != null) { lVAngle = lVAngle * 0.5f; } if (lSpine != null) { lSpine.rotation = Quaternion.AngleAxis(lHAngle, lBody.up) * Quaternion.AngleAxis(lVAngle, lBody.right) * lSpine.rotation; } if (lChest != null) { lChest.rotation = Quaternion.AngleAxis(lHAngle, lBody.up) * Quaternion.AngleAxis(lVAngle, lBody.right) * lChest.rotation; } }
/// <summary> /// Coroutine for moving the actor to the right position /// </summary> /// <param name="rMotion"></param> /// <returns></returns> public virtual IEnumerator MoveToTargetInternal(IInteractableCore rInteractableCore) { bool lStoredWalkRunMotionEnabled = false; bool lStoredUseTransformPosition = false; bool lStoredUseTransformRotation = false; MotionController lMotionController = mMotionController; ActorController lActorController = lMotionController._ActorController; // Enable AC positioning lStoredUseTransformPosition = lActorController.UseTransformPosition; lActorController.UseTransformPosition = true; lStoredUseTransformRotation = lActorController.UseTransformRotation; lActorController.UseTransformRotation = true; // Enable our strafing motion MotionControllerMotion lWalkRunMotion = lMotionController.GetMotion(_WalkRunMotion, true); if (lWalkRunMotion != null) { lStoredWalkRunMotionEnabled = lWalkRunMotion.IsEnabled; lWalkRunMotion.IsEnabled = true; } Vector3 lTargetPosition = lActorController._Transform.position; if (rInteractableCore.ForcePosition) { if (rInteractableCore.TargetLocation != null) { lTargetPosition = rInteractableCore.TargetLocation.position; } else if (rInteractableCore.TargetDistance > 0f) { Vector3 lInteractablePosition = rInteractableCore.gameObject.transform.position; lInteractablePosition.y = lActorController._Transform.position.y; lTargetPosition = lInteractablePosition + ((lActorController._Transform.position - lInteractablePosition).normalized * rInteractableCore.TargetDistance); } } Vector3 lTargetForward = lActorController._Transform.forward; if (rInteractableCore.ForceRotation) { if (rInteractableCore.TargetLocation != null) { lTargetForward = rInteractableCore.TargetLocation.forward; } else { Vector3 lInteractablePosition = rInteractableCore.gameObject.transform.position; lInteractablePosition.y = lActorController._Transform.position.y; lTargetForward = (lInteractablePosition - lActorController._Transform.position).normalized; } } // Move to the target position and rotation Vector3 lDirection = lTargetPosition - lActorController._Transform.position; float lAngle = Vector3Ext.HorizontalAngleTo(lActorController._Transform.forward, lTargetForward); while (HorizontalDistance(lActorController._Transform.position, lTargetPosition) > 0.01f || Mathf.Abs(lAngle) > 0.1f) { float lDistance = Mathf.Min(lDirection.magnitude, _WalkSpeed * Time.deltaTime); lActorController._Transform.position = lActorController._Transform.position + (lDirection.normalized * lDistance); float lYaw = Mathf.Sign(lAngle) * Mathf.Min(Mathf.Abs(lAngle), _RotationSpeed * Time.deltaTime); lActorController._Transform.rotation = (lActorController._Transform.rotation * Quaternion.Euler(0f, lYaw, 0f)); yield return(new WaitForEndOfFrame()); lDirection = lTargetPosition - lActorController._Transform.position; lAngle = Vector3Ext.HorizontalAngleTo(lActorController._Transform.forward, lTargetForward); } // Activate BasicInteraction mActiveForm = rInteractableCore.Form; InteractableCore = rInteractableCore; lMotionController.ActivateMotion(this); // Give some final frames to get to the exact position yield return(new WaitForSeconds(0.2f)); // Reset the motion and movement options if (lWalkRunMotion != null) { lWalkRunMotion.IsEnabled = lStoredWalkRunMotionEnabled; } lActorController.UseTransformPosition = lStoredUseTransformPosition; lActorController.UseTransformRotation = lStoredUseTransformRotation; lMotionController.TargetNormalizedSpeed = 1f; }
/// <summary> /// Grabs the euler changes that happen this frame. /// </summary> /// <param name="rAnchorYaw">Anchor yaw that should be added</param> /// <returns></returns> public virtual Vector3 GetFrameEuler(bool rUseYawLimits, bool rUsePitchLimits = true) { Vector3 lFrameEuler = Vector3.zero; // If the camera was moved externally, we need to recalculate the last focus position if (RigController.LastPosition != RigController._Transform.position) { mFocusLastPosition = GetFocusPosition(RigController._Transform.rotation); } // Now we can grab the movement if (mTarget != null) { mTargetForward = (mTarget.position - mRigTransform.Position).normalized; } if (mTargetForward.sqrMagnitude > 0f) { Quaternion lTargetRotation = Quaternion.LookRotation(mTargetForward, Anchor.up); Quaternion lLocalRotation = Quaternion.Inverse(Anchor.rotation) * RigController._Transform.rotation; Quaternion lLocalTargetRotation = Quaternion.Inverse(Anchor.rotation) * lTargetRotation; float lDeltaYaw = lLocalTargetRotation.eulerAngles.y - lLocalRotation.eulerAngles.y; if (lDeltaYaw > 180f) { lDeltaYaw = lDeltaYaw - 360f; } else if (lDeltaYaw < -180f) { lDeltaYaw = lDeltaYaw + 360f; } float lDeltaPitch = lLocalTargetRotation.eulerAngles.x - lLocalRotation.eulerAngles.x; if (lDeltaPitch > 180f) { lDeltaPitch = lDeltaPitch - 360f; } else if (lDeltaPitch < -180f) { lDeltaPitch = lDeltaPitch + 360f; } if (mTargetYawSpeed <= 0f) { lFrameEuler.y = lDeltaYaw; } else { lFrameEuler.y = Mathf.Sign(lDeltaYaw) * Mathf.Min(mTargetYawSpeed * Time.deltaTime, Mathf.Abs(lDeltaYaw)); } if (mTargetPitchSpeed <= 0f) { lFrameEuler.x = lDeltaPitch; } else { lFrameEuler.x = Mathf.Sign(lDeltaPitch) * Mathf.Min(mTargetPitchSpeed * Time.deltaTime, Mathf.Abs(lDeltaPitch)); } if (Mathf.Abs(lFrameEuler.y) < EPSILON && Mathf.Abs(lFrameEuler.x) < EPSILON) { _Euler.y = LocalYaw; _Euler.x = LocalPitch; _EulerTarget = _Euler; if (mAutoClearTarget) { mTargetForward = Vector3.zero; } } } else if (mTargetYaw < float.MaxValue || mTargetPitch < float.MaxValue) { if (mTargetYaw < float.MaxValue) { float lDeltaYaw = mTargetYaw - LocalYaw; if (mTargetYawSpeed <= 0f) { lFrameEuler.y = lDeltaYaw; } else { lFrameEuler.y = Mathf.Sign(lDeltaYaw) * Mathf.Min(mTargetYawSpeed * Time.deltaTime, Mathf.Abs(lDeltaYaw)); } // TRT 04/09/2017 - Added to stop trying to reach the target when the character is rotating Transform lAnchorTransform = Anchor; float lAnchorRootYawDelta = Vector3Ext.HorizontalAngleTo(mAnchorLastRotation.Forward(), lAnchorTransform.forward, lAnchorTransform.up); if (Mathf.Abs(lFrameEuler.y) - Mathf.Abs(lAnchorRootYawDelta * 2f) < EPSILON) { _Euler.y = mTargetYaw; _EulerTarget.y = mTargetYaw; //if (mAutoClearTarget) { mTargetYaw = float.MaxValue; } } if (mAutoClearTarget && Mathf.Abs(lDeltaYaw) < EPSILON) { mTargetYaw = float.MaxValue; } } if (mTargetPitch < float.MaxValue) { float lDeltaPitch = mTargetPitch - LocalPitch; if (mTargetPitchSpeed <= 0f) { lFrameEuler.x = lDeltaPitch; } else { lFrameEuler.x = Mathf.Sign(lDeltaPitch) * Mathf.Min(mTargetPitchSpeed * Time.deltaTime, Mathf.Abs(lDeltaPitch)); } if (Mathf.Abs(lFrameEuler.x) < EPSILON) { _Euler.x = mTargetPitch; _EulerTarget.x = mTargetPitch; //if (mAutoClearTarget) { mTargetPitch = float.MaxValue; } } if (mAutoClearTarget && Mathf.Abs(lDeltaPitch) < EPSILON) { mTargetPitch = float.MaxValue; } } } else { IInputSource lInputSource = RigController.InputSource; if (lInputSource.IsViewingActivated) { if (_IsYawEnabled && lFrameEuler.y == 0f) { lFrameEuler.y = lInputSource.ViewX * mDegreesYPer60FPSTick; } if (_IsPitchEnabled && lFrameEuler.x == 0f) { lFrameEuler.x = (RigController._InvertPitch || _InvertPitch ? -1f : 1f) * lInputSource.ViewY * mDegreesXPer60FPSTick; } } // Grab the smoothed yaw _EulerTarget.y = (rUseYawLimits && (_MinYaw > -180f || _MaxYaw < 180f) ? Mathf.Clamp(_EulerTarget.y + lFrameEuler.y, _MinYaw, _MaxYaw) : _EulerTarget.y + lFrameEuler.y); lFrameEuler.y = (_Smoothing <= 0f ? _EulerTarget.y : SmoothDamp(_Euler.y, _EulerTarget.y, _Smoothing * 0.001f, Time.deltaTime)) - _Euler.y; _Euler.y = _Euler.y + lFrameEuler.y; // Grab the smoothed pitch _EulerTarget.x = (rUsePitchLimits && (_MinPitch > -180f || _MaxPitch < 180f) ? Mathf.Clamp(_EulerTarget.x + lFrameEuler.x, _MinPitch, _MaxPitch) : _EulerTarget.x + lFrameEuler.x); lFrameEuler.x = (_Smoothing <= 0f ? _EulerTarget.x : SmoothDamp(_Euler.x, _EulerTarget.x, _Smoothing * 0.001f, Time.deltaTime)) - _Euler.x; _Euler.x = _Euler.x + lFrameEuler.x; } return(lFrameEuler); }
/// <summary> /// Grabs the combatants that fit the specified criteria /// </summary> /// <param name="rHunter">Transform who is searching for the combatants</param> /// <param name="rSeekOrigin">Combat origin of the hunter</param> /// <param name="rFilter">Filters we'll use to limit which combatants are returned</param> /// <param name="rCombatantHits">List of CombatantHit values who are the combatants</param> /// <param name="rIgnore">Transform that we won't consider a target (typically the character)</param> /// <returns>Count of combatants returned</returns> public static int QueryCombatTargets(Transform rSeeker, Vector3 rSeekOrigin, CombatFilter rFilter, List <CombatTarget> rCombatTargets, Transform rIgnore) { if (rSeeker == null) { return(0); } if (rCombatTargets == null) { return(0); } #if OOTII_PROFILE com.ootii.Utilities.Profiler.Start(rSeeker.name + ".QueryCombatTargets"); #endif Collider[] lHitColliders; rCombatTargets.Clear(); int lHitCount = RaycastExt.SafeOverlapSphere(rSeekOrigin, rFilter.MaxDistance, out lHitColliders, rFilter.Layers, rIgnore); for (int i = 0; i < lHitCount; i++) { GameObject lGameObject = lHitColliders[i].gameObject; // Don't count the ignore if (lGameObject.transform == rSeeker) { continue; } if (lGameObject.transform == rIgnore) { continue; } // Determine if the combatant has the appropriate tag if (rFilter.Tag != null && rFilter.Tag.Length > 0) { if (!lGameObject.CompareTag(rFilter.Tag)) { continue; } } // We only care about combatants we'll enage with ICombatant lCombatant = null; Transform lHitTransform = lHitColliders[i].transform; while (lHitTransform != null) { lCombatant = lGameObject.GetComponent <ICombatant>(); if (lCombatant != null) { break; } lHitTransform = lHitTransform.parent; } if (rFilter.RequireCombatant && lCombatant == null) { continue; } // Determine if the combatant is within range Vector3 lClosestPoint = Vector3.zero; ActorController lActorController = lGameObject.GetComponent <ActorController>(); if (lActorController != null) { lClosestPoint = lActorController.ClosestPoint(rSeekOrigin); } else { lClosestPoint = GeometryExt.ClosestPoint(rSeekOrigin, lHitColliders[i]); } // If we have an invalid point, stop if (lClosestPoint == Vector3Ext.Null) { continue; } // Determine if the point is in range bool lIsValid = true; Vector3 lToClosestPoint = lClosestPoint - rSeekOrigin; float lDistance = lToClosestPoint.magnitude; if (rFilter.MinDistance > 0f && lDistance < rFilter.MinDistance) { lIsValid = false; } if (rFilter.MaxDistance > 0f && lDistance > rFilter.MaxDistance) { lIsValid = false; } // Ensure we're not ontop of the combatant. In that case, it's probably the ground Vector3 lDirection = lToClosestPoint.normalized; if (lDirection == -rSeeker.up) { lIsValid = false; } // Check if we're within the field of view float lHAngle = Vector3Ext.HorizontalAngleTo(rSeeker.forward, lDirection, rSeeker.up); if (rFilter.HorizontalFOA > 0f && Mathf.Abs(lHAngle) > rFilter.HorizontalFOA * 0.5f) { lIsValid = false; } float lVAngle = Vector3Ext.HorizontalAngleTo(rSeeker.forward, lDirection, rSeeker.right); if (rFilter.VerticalFOA > 0f && Mathf.Abs(lVAngle) > rFilter.VerticalFOA * 0.5f) { lIsValid = false; } // This is an odd test, but we have to do it. If the closest point of a sphere is out of the FOA, it may // be that the top of the sphere is in the FOA. So, we'll grab the top of the sphere and test it. if (!lIsValid && lCombatant != null && lHitColliders[i] is SphereCollider) { lIsValid = true; SphereCollider lSphereCollider = lHitColliders[i] as SphereCollider; lClosestPoint = lCombatant.Transform.position + (lCombatant.Transform.rotation * (lSphereCollider.center + (Vector3.up * lSphereCollider.radius))); lToClosestPoint = lClosestPoint - rSeekOrigin; lDistance = lToClosestPoint.magnitude; if (rFilter.MinDistance > 0f && lDistance < rFilter.MinDistance) { lIsValid = false; } if (rFilter.MaxDistance > 0f && lDistance > rFilter.MaxDistance) { lIsValid = false; } // Ensure we're not ontop of the combatant. In that case, it's probably the ground lDirection = lToClosestPoint.normalized; if (lDirection == -rSeeker.up) { lIsValid = false; } // Check if we're within the field of view lHAngle = Vector3Ext.HorizontalAngleTo(rSeeker.forward, lDirection, rSeeker.up); if (rFilter.HorizontalFOA > 0f && Mathf.Abs(lHAngle) > rFilter.HorizontalFOA * 0.5f) { lIsValid = false; } lVAngle = Vector3Ext.HorizontalAngleTo(rSeeker.forward, lDirection, rSeeker.right); if (rFilter.VerticalFOA > 0f && Mathf.Abs(lVAngle) > rFilter.VerticalFOA * 0.5f) { lIsValid = false; } } if (mShowDebug) { GraphicsManager.DrawPoint(rSeekOrigin, Color.white, null, 2f); GraphicsManager.DrawPoint(lClosestPoint, Color.red, null, 2f); } if (lIsValid) { // Add the combatant to our list CombatTarget lTargetInfo = new CombatTarget(); lTargetInfo.SeekOrigin = rSeekOrigin; lTargetInfo.Collider = lHitColliders[i]; lTargetInfo.Combatant = lCombatant; lTargetInfo.ClosestPoint = lClosestPoint; lTargetInfo.Distance = lDistance; lTargetInfo.Direction = lDirection; lTargetInfo.HorizontalAngle = lHAngle; lTargetInfo.VerticalAngle = lVAngle; rCombatTargets.Add(lTargetInfo); } } // Sort the combatants by distance if (rCombatTargets.Count > 1) { rCombatTargets.Sort((rLeft, rRight) => rLeft.Distance.CompareTo(rRight.Distance)); // We also want to remove duplicates for (int i = 0; i < rCombatTargets.Count; i++) { Transform lTarget = rCombatTargets[i].Collider.transform; if (rCombatTargets[i].Combatant != null) { lTarget = rCombatTargets[i].Combatant.Transform; } // Check if there's a duplicate and remove it for (int j = rCombatTargets.Count - 1; j > i && j > 0; j--) { Transform lNextTarget = rCombatTargets[j].Collider.transform; if (rCombatTargets[j].Combatant != null) { lNextTarget = rCombatTargets[j].Combatant.Transform; } if (lNextTarget == lTarget) { rCombatTargets.RemoveAt(j); } } } } #if OOTII_PROFILE float lTime = Utilities.Profiler.Stop(rSeeker.name + ".QueryCombatTargets"); //Utilities.Debug.Log.FileWrite(rSeeker.name + ".QueryCombatTargets time:" + lTime.ToString("f5") + "ms"); #endif // Finally, return the count return(rCombatTargets.Count); }
/// <summary> /// Updates the motor over time. This is called by the controller /// every update cycle so movement 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> /// <param name="rTiltAngle">Amount of tilting the camera needs to do to match the anchor</param> public override CameraTransform RigLateUpdate(float rDeltaTime, int rUpdateIndex, float rTiltAngle = 0f) { Transform lAnchorTransform = Anchor; if (lAnchorTransform == null) { return(mRigTransform); } if (RigController == null) { return(mRigTransform); } Transform lCameraTransform = RigController._Transform; // Determine how much the anchor's yaw changed (we want to stay relative to the anchor direction) float lAnchorRootYawDelta = Vector3Ext.HorizontalAngleTo(mAnchorLastRotation.Forward(), lAnchorTransform.forward, lAnchorTransform.up); float lAnchorYaw = (Mathf.Abs(rTiltAngle) >= 2f ? lAnchorRootYawDelta : 0f); if (!RigController.RotateAnchorOffset) { lAnchorRootYawDelta = 0f; lAnchorYaw = 0f; } // Grab any euler changes this frame mFrameEuler = GetFrameEuler(true, true); // RigController.RotateAnchorOffset); // Get our predicted based on the frame and anchor changes. We want this to get the right-vector Quaternion lNewCameraRotation = Quaternion.AngleAxis(mFrameEuler.y + lAnchorYaw, (RigController.RotateAnchorOffset ? RigController.Tilt.Up() : Vector3.up)) * lCameraTransform.rotation; // Now grab the full (vertical + horizontal) position of our final focus. //Vector3 lNewFocusPosition = lAnchorTransform.position + (lAnchorTransform.up * lOffset.y); //lNewFocusPosition = lNewFocusPosition + (lNewCameraRotation.Right() * lOffset.x); Vector3 lNewFocusPosition = GetFocusPosition(lNewCameraRotation); // Default the value Vector3 lNewCameraPosition = lCameraTransform.position; Vector3 lToFocusPosition = lCameraTransform.forward; // If we're tilting, act like a fixed if (RigController.FrameForceToFollowAnchor || Mathf.Abs(rTiltAngle) >= 2f) { // Get the local position and the right vector of the camera relative to the last frame Matrix4x4 lOldFocusMatrix = Matrix4x4.TRS(mFocusLastPosition, (RigController.RotateAnchorOffset ? mAnchorLastRotation : Quaternion.identity), Vector3.one); Matrix4x4 lNewFocusMatrix = Matrix4x4.TRS(lNewFocusPosition, (RigController.RotateAnchorOffset ? lAnchorTransform.rotation : Quaternion.identity), Vector3.one); // The matrix will add our anchor delta. But, we also added it when we use the LocalYaw // We'll remove it so we don't double up. if (mTargetYaw < float.MaxValue) { mFrameEuler.y = mFrameEuler.y - lAnchorRootYawDelta; } // If nothing has changed, we won't update. This is important due to the fact // that the inverse of the matix causes a small floating point error to move us. if (mFrameEuler.sqrMagnitude != 0f || lOldFocusMatrix != lNewFocusMatrix) { Vector3 lLocalPosition = lOldFocusMatrix.inverse.MultiplyPoint(lCameraTransform.position); Vector3 lLocalCameraRight = lOldFocusMatrix.inverse.MultiplyVector(lCameraTransform.right); // Rotate the old local position based on the frame's rotation changes Quaternion lLocalRotation = Quaternion.AngleAxis(mFrameEuler.y, Vector3.up) * Quaternion.AngleAxis(mFrameEuler.x, lLocalCameraRight); lLocalPosition = lLocalRotation * lLocalPosition; // Grab the new position based on the updated matrix lNewCameraPosition = lNewFocusMatrix.MultiplyPoint(lLocalPosition); } } // If we have a target forward, act like a fixed else if (mTargetForward.sqrMagnitude > 0f) { lNewCameraRotation = lNewCameraRotation * Quaternion.AngleAxis(mFrameEuler.x, Vector3.right); lNewCameraPosition = lNewFocusPosition - (lNewCameraRotation.Forward() * mDistance); } else { lNewCameraRotation = lNewCameraRotation * Quaternion.AngleAxis(mFrameEuler.x, Vector3.right); Vector3 lOldCameraPosition = mFocusLastPosition - (lNewCameraRotation.Forward() * mDistance); // Grab the new focus position using the drag rotation we get lToFocusPosition = lNewFocusPosition - lOldCameraPosition; if (lToFocusPosition.sqrMagnitude < 0.0001f) { lToFocusPosition = lCameraTransform.forward; } lNewCameraRotation = Quaternion.LookRotation(lToFocusPosition.normalized, (RigController.RotateAnchorOffset ? lAnchorTransform.up : Vector3.up)); //lNewFocusPosition = lAnchorTransform.position + (lAnchorTransform.up * lOffset.y); //lNewFocusPosition = lNewFocusPosition + (lNewCameraRotation.Right() * lOffset.x); lNewFocusPosition = GetFocusPosition(lNewCameraRotation); // If something is between the new focus position and the anchor, the new focus position is the anchor //Color lHitColor = Color.black; //RaycastHit lFocusHit; Vector3 lAnchorPosition = Vector3.zero; if (RigController.RotateAnchorOffset) { lAnchorPosition = lAnchorTransform.position + (lAnchorTransform.rotation * AnchorOffset) + (lAnchorTransform.up * _Offset.y); } else { lAnchorPosition = lAnchorTransform.position + AnchorOffset + (Vector3.up * _Offset.y); } lToFocusPosition = lNewFocusPosition - lAnchorPosition; // Get the drag direction and pull out the horizontal component lToFocusPosition = lNewFocusPosition - lOldCameraPosition; if (lToFocusPosition.sqrMagnitude < 0.0001f) { lToFocusPosition = lCameraTransform.forward; } Vector3 lToFocusPositionVertical = Vector3.Project(lToFocusPosition, (RigController.RotateAnchorOffset ? RigController.Tilt.Up() : Vector3.up)); Vector3 lToFocusPositionHorizontal = lToFocusPosition - lToFocusPositionVertical; // Get the pitch rotation regardless of our tilt lNewCameraRotation = lCameraTransform.rotation * Quaternion.AngleAxis(mFrameEuler.x, Vector3.right); Vector3 lRotationEuler = (RigController.RotateAnchorOffset ? Quaternion.Inverse(RigController.Tilt) * lNewCameraRotation : lNewCameraRotation).eulerAngles; // Add the pitch to our look-at rotation Quaternion lNewAnchorRotation = Quaternion.LookRotation(lToFocusPositionHorizontal, (RigController.RotateAnchorOffset ? RigController.Tilt.Up() : Vector3.up)) * Quaternion.Euler(lRotationEuler.x, 0f, 0f); // Using the rotation, grab the new camera position lNewCameraPosition = lNewFocusPosition - (lNewAnchorRotation.Forward() * mDistance); } // Determine our rotation lToFocusPosition = lNewFocusPosition - lNewCameraPosition; if (lToFocusPosition.sqrMagnitude < 0.0001f) { lToFocusPosition = lCameraTransform.forward; } lNewCameraRotation = Quaternion.LookRotation(lToFocusPosition.normalized, (RigController.RotateAnchorOffset ? lAnchorTransform.up : Vector3.up)); // We have to do a final check if we have exceeded rotation limits Quaternion lNewLocalCameraRotation = (RigController.RotateAnchorOffset ? Quaternion.Inverse(Anchor.transform.rotation) : Quaternion.identity) * lNewCameraRotation; Vector3 lNewLocalEuler = lNewLocalCameraRotation.eulerAngles; if (lNewLocalEuler.y > 180f) { lNewLocalEuler.y = lNewLocalEuler.y - 360f; } else if (lNewLocalEuler.y < -180f) { lNewLocalEuler.y = lNewLocalEuler.y + 360f; } if (lNewLocalEuler.x > 180f) { lNewLocalEuler.x = lNewLocalEuler.x - 360f; } else if (lNewLocalEuler.x < -180f) { lNewLocalEuler.x = lNewLocalEuler.x + 360f; } float lYaw = (_MinYaw > -180f || _MaxYaw < 180f ? Mathf.Clamp(lNewLocalEuler.y, _MinYaw, _MaxYaw) : lNewLocalEuler.y); float lPitch = Mathf.Clamp(lNewLocalEuler.x, _MinPitch, _MaxPitch); if (lYaw != lNewLocalEuler.y || lPitch != lNewLocalEuler.x) { lNewCameraRotation = (RigController.RotateAnchorOffset ? Anchor.transform.rotation : Quaternion.identity) * Quaternion.Euler(lPitch, lYaw, 0f); lNewCameraPosition = lNewFocusPosition - (lNewCameraRotation.Forward() * mDistance); } // Return the results mRigTransform.Position = lNewCameraPosition; mRigTransform.Rotation = lNewCameraRotation; return(mRigTransform); }
/// <summary> /// Updates the motor over time. This is called by the controller /// every update cycle so movement 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> /// <param name="rTiltAngle">Amount of tilting the camera needs to do to match the anchor</param> public override CameraTransform RigLateUpdate(float rDeltaTime, int rUpdateIndex, float rTiltAngle = 0f) { Transform lAnchorTransform = Anchor; if (lAnchorTransform == null) { return(mRigTransform); } if (RigController == null) { return(mRigTransform); } //Quaternion lTilt = RigController.Tilt; Transform lCameraTransform = RigController._Transform; // Determine how much the anchor's yaw changed (we want to stay relative to the anchor direction) float lAnchorRootYawDelta = Vector3Ext.HorizontalAngleTo(mAnchorLastRotation.Forward(), lAnchorTransform.forward, lAnchorTransform.up); float lAnchorYaw = (Mathf.Abs(rTiltAngle) >= 2f ? lAnchorRootYawDelta : 0f); if (!RigController.RotateAnchorOffset) { lAnchorRootYawDelta = 0f; lAnchorYaw = 0f; } // Grab any euler changes this frame mFrameEuler = GetFrameEuler(true, RigController.RotateAnchorOffset); // Get our predicted based on the frame and anchor changes. We want this to get the right-vector Quaternion lNewCameraRotation = Quaternion.AngleAxis(mFrameEuler.y + lAnchorYaw, (RigController.RotateAnchorOffset ? RigController.Tilt.Up() : Vector3.up)) * lCameraTransform.rotation; // Now grab the full (vertical + horizontal) position of our final focus. //Vector3 lNewFocusPosition = lAnchorTransform.position + (lAnchorTransform.up * lAnchorOffset.y); //lNewFocusPosition = lNewFocusPosition + (lNewCameraRotation.Right() * lAnchorOffset.x); Vector3 lNewFocusPosition = GetFocusPosition(lNewCameraRotation); // Default the value Vector3 lNewCameraPosition = lCameraTransform.position; Vector3 lToFocusPosition = lCameraTransform.forward; // If we're tilting, act like a fixed if (_RotateWithAnchor || RigController.FrameForceToFollowAnchor || Mathf.Abs(rTiltAngle) >= 2f) { // Get the local position and the right vector of the camera relative to the last frame Matrix4x4 lOldFocusMatrix = Matrix4x4.TRS(mFocusLastPosition, (RigController.RotateAnchorOffset ? mAnchorLastRotation : Quaternion.identity), Vector3.one); Matrix4x4 lNewFocusMatrix = Matrix4x4.TRS(lNewFocusPosition, (RigController.RotateAnchorOffset ? lAnchorTransform.rotation : Quaternion.identity), Vector3.one); // The matrix will add our anchor delta. But, we also added it when we use the LocalYaw // We'll remove it so we don't double up. if (mTargetYaw < float.MaxValue) { mFrameEuler.y = mFrameEuler.y - lAnchorRootYawDelta; } // If nothing has changed, we won't update. This is important due to the fact // that the inverse of the matix causes a small floating point error to move us. if (mFrameEuler.sqrMagnitude != 0f || lOldFocusMatrix != lNewFocusMatrix) { Vector3 lLocalPosition = lOldFocusMatrix.inverse.MultiplyPoint(lCameraTransform.position); Vector3 lLocalCameraRight = lOldFocusMatrix.inverse.MultiplyVector(lCameraTransform.right); // Rotate the old local position based on the frame's rotation changes Quaternion lLocalRotation = Quaternion.AngleAxis(mFrameEuler.y, Vector3.up) * Quaternion.AngleAxis(mFrameEuler.x, lLocalCameraRight); lLocalPosition = lLocalRotation * lLocalPosition; // Grab the new position based on the updated matrix lNewCameraPosition = lNewFocusMatrix.MultiplyPoint(lLocalPosition); // The problem is that we may not be allowing the camera to pull back to the desired distance. // So, apply the distance to the direction and that's our new position. Vector3 lDirection = (lNewCameraPosition - lNewFocusPosition).normalized; lNewCameraPosition = lNewFocusPosition + (lDirection * mDistance); } // Ensure our camera isn't inbetween the focus and the anchor float lNewCameraDistance = Vector3.Distance(AnchorPosition, lNewCameraPosition); float lNewFocusDistance = Vector3.Distance(AnchorPosition, lNewFocusPosition); if (lNewCameraDistance < lNewFocusDistance) { lNewCameraPosition = lNewFocusPosition - (lNewCameraRotation.Forward() * mDistance); } } else { lNewCameraRotation = lNewCameraRotation * Quaternion.AngleAxis(mFrameEuler.x, Vector3.right); lNewCameraPosition = lNewFocusPosition - (lNewCameraRotation.Forward() * mDistance); } // Determine our rotation lToFocusPosition = lNewFocusPosition - lNewCameraPosition; if (lToFocusPosition.sqrMagnitude < 0.0001f) { lToFocusPosition = lCameraTransform.forward; } // Ensure we have the right rotation lNewCameraRotation = Quaternion.LookRotation(lToFocusPosition.normalized, (RigController.RotateAnchorOffset ? lAnchorTransform.up : Vector3.up)); // Ensure we're the correct distance from the focus point if (lToFocusPosition.magnitude != mDistance) { lNewCameraPosition = lNewFocusPosition - (lToFocusPosition.normalized * mDistance); } // Return the results mRigTransform.Position = lNewCameraPosition; mRigTransform.Rotation = lNewCameraRotation; return(mRigTransform); }