public override MultiAimInverseConstraintJob Create(Animator animator, ref T data, Component component) { var job = new MultiAimInverseConstraintJob(); job.driven = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject); job.drivenParent = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject.parent); job.drivenOffset = Vector3Property.Bind(animator, component, data.offsetVector3Property); job.aimAxis = data.aimAxis; WeightedTransformArray sourceObjects = data.sourceObjects; WeightedTransformArrayBinder.BindReadWriteTransforms(animator, component, sourceObjects, out job.sourceTransforms); WeightedTransformArrayBinder.BindWeights(animator, component, sourceObjects, data.sourceObjectsProperty, out job.sourceWeights); job.sourceOffsets = new NativeArray <Quaternion>(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); for (int i = 0; i < sourceObjects.Count; ++i) { if (data.maintainOffset) { var aimDirection = data.constrainedObject.rotation * data.aimAxis; var dataToSource = sourceObjects[i].transform.position - data.constrainedObject.position; var rot = QuaternionExt.FromToRotation(dataToSource, aimDirection); job.sourceOffsets[i] = Quaternion.Inverse(rot); } else { job.sourceOffsets[i] = Quaternion.identity; } } job.weightBuffer = new NativeArray <float>(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); return(job); }
/// <summary> /// Use this for initialization /// </summary> protected override void Awake() { base.Awake(); if (_Anchor != null && this.enabled) { ICharacterController lController = InterfaceHelper.GetComponent <ICharacterController>(_Anchor.gameObject); if (lController != null) { IsInternalUpdateEnabled = false; IsFixedUpdateEnabled = false; lController.OnControllerPostLateUpdate += OnControllerLateUpdate; } mTilt = QuaternionExt.FromToRotation(_Transform.up, _Anchor.up); mToCameraDirection = _Transform.position - _Anchor.position; mToCameraDirection.y = 0f; mToCameraDirection.Normalize(); if (mToCameraDirection.sqrMagnitude == 0f) { mToCameraDirection = -_Anchor.forward; } } // Object that will provide access to the keyboard, mouse, etc if (_InputSourceOwner != null) { mInputSource = InterfaceHelper.GetComponent <IInputSource>(_InputSourceOwner); } // Default the speed we'll use to rotate mDegreesPer60FPSTick = _RotationSpeed / 60f; }
/// <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> /// Create a rotation velocity that rotates the character based on input /// </summary> /// <param name="rInputFromAvatarAngle"></param> /// <param name="rDeltaTime"></param> protected virtual void RotateActorToTarget(Transform rTarget, float rSpeed) { // Get the forward looking direction Vector3 lForward = (rTarget.position - mActorController._Transform.position).normalized; // We do the inverse tilt so we calculate the rotation in "natural up" space vs. "actor up" space. Quaternion lInvTilt = QuaternionExt.FromToRotation(mActorController._Transform.up, Vector3.up); // Character's forward direction of the actor in "natural up" Vector3 lActorForward = lInvTilt * mActorController._Transform.forward; // Target forward in "natural up" Vector3 lTargetForward = lInvTilt * lForward; // Ensure we don't exceed our rotation speed float lActorToTargetAngle = NumberHelper.GetHorizontalAngle(lActorForward, lTargetForward); if (rSpeed > 0f && Mathf.Abs(lActorToTargetAngle) > rSpeed * Time.deltaTime) { lActorToTargetAngle = Mathf.Sign(lActorToTargetAngle) * rSpeed * Time.deltaTime; } // Add the rotation to our character Quaternion lRotation = Quaternion.AngleAxis(lActorToTargetAngle, Vector3.up); if (mActorController._UseTransformPosition && mActorController._UseTransformRotation) { _Transform.rotation = _Transform.rotation * lRotation; } else { mActorController.Rotate(lRotation, Quaternion.identity); } }
/// <summary> /// Provides a place to set the properties of the animator /// </summary> /// <param name="rInput">Vector3 representing the input</param> /// <param name="rMove">Vector3 representing the amount of movement taking place (in world space)</param> /// <param name="rRotate">Quaternion representing the amount of rotation taking place</param> protected override void SetAnimatorProperties(Vector3 rInput, Vector3 rMovement, Quaternion rRotation) { if (mInputSource == null || !mInputSource.IsEnabled) { return; } // Jump based on space bool lIsInJump = !mActorController.State.IsGrounded; if (mInputSource.IsJustPressed("Jump")) { if (!lIsInJump && !mIsInJumpToWall) { lIsInJump = true; // We need to check if we're actually jumping towards a wall RaycastHit lHitInfo; if (RaycastExt.SafeRaycast(transform.position + (transform.up * JumpToWallHeight), transform.forward, out lHitInfo, JumpToWallDistance)) { mIsInJumpToWall = true; mJumpToWallPoint = lHitInfo.point; mJumpToWallNormal = lHitInfo.normal; mJumpToWallElapsedTime = 0f; mSavedOrientToGroundSpeed = mActorController.OrientToGroundSpeed; mActorController.OrientToGroundSpeed = JumpToWallOrientSpeed; } // Perform the jump mActorController.AddImpulse(transform.up * _JumpForce); } } // Direction of the camera float lDirection = 0f; // We do the inverse tilt so we calculate the rotation in "natural up" space vs. "actor up" space. Quaternion lInvTilt = QuaternionExt.FromToRotation(mActorController._Transform.up, Vector3.up); // Forward direction of the actor in "natural up" Vector3 lControllerForward = lInvTilt * mActorController._Transform.forward; // Get the angular difference between the camera-based input and the spider's "natural-up" forward lDirection = NumberHelper.GetHorizontalAngle(lControllerForward, rInput); mAnimator.SetFloat("Direction", lDirection); mAnimator.SetFloat("Speed", rInput.magnitude); mAnimator.SetBool("Jump", lIsInJump); }
/// <summary> /// Causes us to ignore user input and force the camera to the specified localangles /// </summary> /// <param name="rYaw">Target local yaw</param> /// <param name="rPitch">Target local pitch</param> /// <param name="rSpeed">Degrees per second we'll rotate. A value of -1 uses the current yaw speed.</param> /// <param name="rAutoClearTarget">Determines if we'll clear the target once we reach it.</param> public override void SetTargetYawPitch(float rYaw, float rPitch, float rSpeed = -1F, bool rAutoClearTarget = true) { Vector3 lNewAnchorPosition = _Anchor.position + (_Anchor.rotation * _AnchorOffset); // Grab the rotation amount. We do the inverse tilt so we calculate the rotation in // "natural up" space. Later we'll use the tilt to put it back into "anchor up" space. Quaternion lInvTilt = QuaternionExt.FromToRotation(_Anchor.up, Vector3.up); // Yaw is simple as we can go 360 Quaternion lYaw = Quaternion.AngleAxis((rYaw - LocalYaw), lInvTilt * _Transform.up); // Pitch is more complicated since we can't go beyond the north/south pole float lPitchAngle = Vector3.Angle(mToCameraDirection, lInvTilt * _Anchor.up); float lPitchDelta = rPitch - LocalPitch; if (lPitchAngle < MIN_PITCH && lPitchDelta > 0f) { lPitchDelta = 0f; } else if (lPitchAngle > MAX_PITCH && lPitchDelta < 0f) { lPitchDelta = 0f; } Quaternion lPitch = Quaternion.AngleAxis(lPitchDelta, lInvTilt * _Transform.right); // Calculate the new "natural up" direction mToCameraDirection = lPitch * lYaw * mToCameraDirection; // Update our tilt to match the anchor's tilt mTilt = QuaternionExt.FromToRotation(mTilt.Up(), _Anchor.up) * mTilt; // Put the new direction relative to the anchor's tilt Vector3 lToCameraDirection = mTilt * mToCameraDirection; if (lToCameraDirection.sqrMagnitude == 0f) { lToCameraDirection = -_Anchor.forward; } // Calculate the new orbit center (anchor) and camera position Vector3 lNewCameraPosition = lNewAnchorPosition + (lToCameraDirection.normalized * _Radius); Quaternion lNewCameraRotation = Quaternion.LookRotation(lNewAnchorPosition - lNewCameraPosition, _Anchor.up); _Transform.position = lNewCameraPosition; _Transform.rotation = lNewCameraRotation; }
/// <summary> /// When we want to rotate based on the camera direction (which input does), 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; } // Get out early if we we aren't modifying the view. if (mMotionController._InputSource != null && mMotionController._InputSource.ViewX == 0f) { return; } // We do the inverse tilt so we calculate the rotation in "natural up" space vs. "actor up" space. Quaternion lInvTilt = QuaternionExt.FromToRotation(mMotionController._Transform.up, Vector3.up); // Forward direction of the actor in "natural up" Vector3 lControllerForward = lInvTilt * mMotionController._Transform.forward; // 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, Vector3.up); // Transform joystick from world space to camera space. Now the input is relative // to how the camera is facing. Vector3 lMoveDirection = lToCamera * mMotionController.State.InputForward; float lInputFromAvatarAngle = NumberHelper.GetHorizontalAngle(lControllerForward, lMoveDirection); // Clear the link if we're out of rotation range if (Mathf.Abs(lInputFromAvatarAngle) > _RotationSpeed * rDeltaTime * 5f) { mIsRotationLocked = false; } // We only want to do this is we're very very close to the desired angle. This will remove any stuttering if (_RotationSpeed == 0f || mIsRotationLocked || Mathf.Abs(lInputFromAvatarAngle) < _RotationSpeed * rDeltaTime * 1f) { mIsRotationLocked = true; // Since we're after the camera update, we have to force the rotation outside the normal flow Quaternion lRotation = Quaternion.AngleAxis(lInputFromAvatarAngle, Vector3.up); mActorController.Yaw = mActorController.Yaw * lRotation; mActorController._Transform.rotation = mActorController.Tilt * mActorController.Yaw; } }
/// <summary> /// When we want to rotate based on the camera direction (which input does), 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> protected virtual void OnCameraUpdated(float rDeltaTime, int rUpdateIndex, BaseCameraRig rCamera) { if (!_RotateWithCamera) { return; } if (_RequireTarget && mCombatant != null && mCombatant.IsTargetLocked) { return; } // Get out early if we we aren't modifying the view. if (mMotionController._InputSource != null && mMotionController._InputSource.ViewX == 0f) { return; } // We do the inverse tilt so we calculate the rotation in "natural up" space vs. "actor up" space. Quaternion lInvTilt = QuaternionExt.FromToRotation(mMotionController._Transform.up, Vector3.up); // Forward direction of the actor in "natural up" Vector3 lActorForward = lInvTilt * mMotionController._Transform.forward; // Camera forward in "natural up" Vector3 lCameraForward = lInvTilt * mMotionController._CameraTransform.forward; // Get the rotation angle to the camera float lActorToCameraAngle = NumberHelper.GetHorizontalAngle(lActorForward, lCameraForward); // Clear the link if we're out of rotation range if (Mathf.Abs(lActorToCameraAngle) > _RotationSpeed * rDeltaTime * 5f) { mIsRotationLocked = false; } // We only want to do this is we're very very close to the desired angle. This will remove any stuttering if (_RotationSpeed == 0f || mIsRotationLocked || Mathf.Abs(lActorToCameraAngle) < _RotationSpeed * rDeltaTime * 1f) { mIsRotationLocked = true; // Since we're after the camera update, we have to force the rotation outside the normal flow Quaternion lRotation = Quaternion.AngleAxis(lActorToCameraAngle, Vector3.up); mActorController.Yaw = mActorController.Yaw * lRotation; mActorController._Transform.rotation = mActorController.Tilt * mActorController.Yaw; } }
/// <summary> /// Create a rotation velocity that rotates the character based on input /// </summary> /// <param name="rInputFromAvatarAngle"></param> /// <param name="rDeltaTime"></param> protected void RotateToDirection(Vector3 rForward, float rSpeed, float rDeltaTime, ref Quaternion rRotation) { // We do the inverse tilt so we calculate the rotation in "natural up" space vs. "actor up" space. Quaternion lInvTilt = QuaternionExt.FromToRotation(mMotionController._Transform.up, Vector3.up); // Forward direction of the actor in "natural up" Vector3 lActorForward = lInvTilt * mMotionController._Transform.forward; // Camera forward in "natural up" Vector3 lTargetForward = lInvTilt * rForward; // Ensure we don't exceed our rotation speed float lActorToCameraAngle = NumberHelper.GetHorizontalAngle(lActorForward, lTargetForward); if (rSpeed > 0f && Mathf.Abs(lActorToCameraAngle) > rSpeed * rDeltaTime) { lActorToCameraAngle = Mathf.Sign(lActorToCameraAngle) * rSpeed * rDeltaTime; } // We only want to do this is we're very very close to the desired angle. This will remove any stuttering rRotation = Quaternion.AngleAxis(lActorToCameraAngle, Vector3.up); }
/// <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> /// LateUpdate logic for the controller should be done here. This allows us /// to support dynamic and fixed update times /// </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 RigLateUpdate(float rDeltaTime, int rUpdateIndex) { Vector3 lNewAnchorPosition = _Anchor.position + (_Anchor.rotation * _AnchorOffset); Vector3 lNewCameraPosition = _Transform.position; Quaternion lNewCameraRotation = _Transform.rotation; // At certain times, we may force the rig to face the direction of the actor if (_FrameForceToFollowAnchor) { // Grab the rotation amount. We do the inverse tilt so we calculate the rotation in // "natural up" space. Later we'll use the tilt to put it back into "anchor up" space. Quaternion lInvTilt = QuaternionExt.FromToRotation(_Anchor.up, Vector3.up); // Determine the global direction the character should face float lAngle = NumberHelper.GetHorizontalAngle(_Transform.forward, _Anchor.forward, _Anchor.up); Quaternion lYaw = Quaternion.AngleAxis(lAngle, lInvTilt * _Anchor.up); // Pitch is more complicated since we can't go beyond the north/south pole Quaternion lPitch = Quaternion.identity; if (mInputSource.IsViewingActivated) { float lPitchAngle = Vector3.Angle(mToCameraDirection, lInvTilt * _Anchor.up); float lPitchDelta = (_InvertPitch ? -1f : 1f) * mInputSource.ViewY; if (lPitchAngle < MIN_PITCH && lPitchDelta > 0f) { lPitchDelta = 0f; } else if (lPitchAngle > MAX_PITCH && lPitchDelta < 0f) { lPitchDelta = 0f; } lPitch = Quaternion.AngleAxis(lPitchDelta, lInvTilt * _Transform.right); } // Calculate the new "natural up" direction mToCameraDirection = lPitch * lYaw * mToCameraDirection; // Update our tilt to match the anchor's tilt mTilt = QuaternionExt.FromToRotation(mTilt.Up(), _Anchor.up) * mTilt; // Put the new direction relative to the anchor's tilt Vector3 lToCameraDirection = mTilt * mToCameraDirection; if (lToCameraDirection.sqrMagnitude == 0f) { lToCameraDirection = -_Anchor.forward; } // Calculate the new orbit center (anchor) and camera position lNewCameraPosition = lNewAnchorPosition + (lToCameraDirection.normalized * _Radius); lNewCameraRotation = Quaternion.LookRotation(lNewAnchorPosition - lNewCameraPosition, _Anchor.up); // Disable the force _FrameForceToFollowAnchor = false; } // If we're not forcing a follow, do our normal processing else { if (mInputSource.IsViewingActivated) { // Grab the rotation amount. We do the inverse tilt so we calculate the rotation in // "natural up" space. Later we'll use the tilt to put it back into "anchor up" space. Quaternion lInvTilt = QuaternionExt.FromToRotation(_Anchor.up, Vector3.up); // Yaw is simple as we can go 360 Quaternion lYaw = Quaternion.AngleAxis(mInputSource.ViewX * mDegreesPer60FPSTick, lInvTilt * _Transform.up); // Pitch is more complicated since we can't go beyond the north/south pole float lPitchAngle = Vector3.Angle(mToCameraDirection, lInvTilt * _Anchor.up); float lPitchDelta = (_InvertPitch ? -1f : 1f) * mInputSource.ViewY; if (lPitchAngle < MIN_PITCH && lPitchDelta > 0f) { lPitchDelta = 0f; } else if (lPitchAngle > MAX_PITCH && lPitchDelta < 0f) { lPitchDelta = 0f; } Quaternion lPitch = Quaternion.AngleAxis(lPitchDelta, lInvTilt * _Transform.right); // Calculate the new "natural up" direction mToCameraDirection = lPitch * lYaw * mToCameraDirection; } // Update our tilt to match the anchor's tilt mTilt = QuaternionExt.FromToRotation(mTilt.Up(), _Anchor.up) * mTilt; // Put the new direction relative to the anchor's tilt Vector3 lToCameraDirection = mTilt * mToCameraDirection; if (lToCameraDirection.sqrMagnitude == 0f) { lToCameraDirection = -_Anchor.forward; } // Calculate the new orbit center (anchor) and camera position lNewCameraPosition = lNewAnchorPosition + (lToCameraDirection.normalized * _Radius); lNewCameraRotation = Quaternion.LookRotation(lNewAnchorPosition - lNewCameraPosition, _Anchor.up); } // Set the values _Transform.position = lNewCameraPosition; _Transform.rotation = lNewCameraRotation; }
public void ProcessAnimation(AnimationStream stream) { var jointCount = boneChain.Length; var boneCount = jointCount - 1; var tipPosition = tipTarget.GetPosition(stream); var weight = jobWeight.Get(stream); var positions = new NativeArray <float3>(jointCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var tangents = new NativeArray <float3>(jointCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var normals = new NativeArray <float3>(jointCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var v0 = boneChain[0].GetRotation(stream) * Vector3.up; // fill the local buffer of positions for (var i = 0; i < jointCount; i++) { positions[i] = boneChain[i].GetPosition(stream); } // perform solver iterations for (var i = 0; i < iterationCount; i++) { // force final position to the tip target... unsure if this is the best... positions[positions.Length - 1] = tipPosition; // project distance constraints for (var j = 0; j < boneCount; j++) { var invMass0 = j == 0 ? 0f : 1f; var invMass1 = j == boneCount - 1 ? 0 : 1f; var p0 = positions[j + 0]; var p1 = positions[j + 1]; var d = restLengths[j]; var C = distance(p0, p1) - d; if (C < float.Epsilon && C > -float.Epsilon) { continue; } var direction = normalize(p0 - p1); var lambda = C * direction; positions[j + 0] -= invMass0 / (invMass0 + invMass1) * lambda; positions[j + 1] += invMass1 / (invMass0 + invMass1) * lambda; } // project collisions for (var j = 0; j < jointCount; j++) { var p = positions[j]; p.y = max(p.y, minimumHeight); positions[j] = p; } } // Set the new bone rotations for (var i = 0; i < boneCount; i++) { var prevDir = boneChain[i + 1].GetPosition(stream) - boneChain[i + 0].GetPosition(stream); var newDir = positions[i + 1] - positions[i]; var currentRotation = boneChain[i].GetRotation(stream); var additionalRotation = QuaternionExt.FromToRotation(prevDir, newDir); boneChain[i].SetRotation(stream, Quaternion.Lerp(currentRotation, additionalRotation * currentRotation, weight)); } boneChain[jointCount - 1].SetRotation(stream, tipTarget.GetRotation(stream)); positions.Dispose(); tangents.Dispose(); normals.Dispose(); }
/// <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 // ----------------------------------------------------------------- // If the input source says we can, rotate based on the yaw. if (mInputSource.IsViewingActivated) { // The input from the mouse already takes the frame rate into account. By doing // the multiplication here, we keep the rotation consistant across frame rates. lRotation = Quaternion.Euler(0f, lYaw * mDegreesPer60FPSTick, 0f); // Rotate our actor 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); } } }
/// <summary> /// Called every frame so the driver can process input and /// update the actor controller. /// </summary> protected override void Update() { if (!_IsEnabled) { return; } if (mAnimator == null) { return; } if (mActorController == null) { return; } if (mInputSource == null || !mInputSource.IsEnabled) { return; } float lDeltaTime = TimeManager.SmoothedDeltaTime; Vector3 lMovement = Vector3.zero; Quaternion lRotation = Quaternion.identity; Vector3 lInput = new Vector3(mInputSource.MovementX, 0f, mInputSource.MovementY); // Convert the input to be relative to the camera Vector3 lCameraBasedInput = lInput; if (CameraTransform != null) { lCameraBasedInput = CameraTransform.rotation * lInput; } // If we're in the middle of the jump, move towards the wall and rotate if needed if (mIsInJumpToWall) { Vector3 lToPoint = mJumpToWallPoint - transform.position; // Increment our timer mJumpToWallElapsedTime = mJumpToWallElapsedTime + Time.deltaTime; // If we're close to the target or the time has elapsed, stop if (lToPoint.magnitude < JumpToWallArrivalDistance || (JumpToWallTimeout > 0f && mJumpToWallElapsedTime > JumpToWallTimeout)) { mIsInJumpToWall = false; mJumpToWallPoint = Vector3.zero; mJumpToWallNormal = Vector3.zero; mJumpToWallElapsedTime = 0f; mActorController.OrientToGroundSpeed = mSavedOrientToGroundSpeed; mActorController.SetTargetGroundNormal(Vector3.zero); // Reenable gravity once we're on mActorController.IsGravityEnabled = true; } else { mActorController.SetTargetGroundNormal(mJumpToWallNormal); lMovement = lToPoint.normalized * Mathf.Min(JumpToWallSpeed * lDeltaTime, lToPoint.magnitude); mActorController.Move(lMovement); // Don't let gravity pull us down as we're trying to head to the target mActorController.IsGravityEnabled = false; } } // Move like normal else { // Rotate based on the input (and root motion if there is any) Quaternion lUserRotation = Quaternion.identity; if (mInputSource.IsViewingActivated) { float lYaw = mInputSource.ViewX; lUserRotation = Quaternion.Euler(0f, lYaw * mDegreesPer60FPSTick, 0f); } lRotation = mRootMotionRotation * lUserRotation; mActorController.Rotate(lRotation); // We do 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 (relative to the camera), we add the tilt lMovement = lTilt * lCameraBasedInput * _MovementSpeed * lDeltaTime; if (mRootMotionMovement.sqrMagnitude > 0f) { lMovement = mRootMotionMovement; } mActorController.Move(lMovement); } // Tell the animator what to do next SetAnimatorProperties(lCameraBasedInput, lMovement, lRotation); }
/// <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); } } }