void LateUpdate() { viewFrustum = DebugDraw.CalculateViewFrustum(camera, ref nearClipDimensions); // Pull values from controller/keyboard float rightX = Input.GetAxis("RightStickX"); float rightY = Input.GetAxis("RightStickY"); float leftX = Input.GetAxis("Horizontal"); float leftY = Input.GetAxis("Vertical"); float mouseWheel = Input.GetAxis("Mouse ScrollWheel"); float mouseWheelScaled = mouseWheel * mouseWheelSensitivity; float leftTrigger = Input.GetAxis("Target"); bool bButtonPressed = Input.GetButton("ExitFPV"); bool qKeyDown = Input.GetKey(KeyCode.Q); bool eKeyDown = Input.GetKey(KeyCode.E); bool lShiftKeyDown = Input.GetKey(KeyCode.LeftShift); // Abstraction to set right Y when using mouse if (mouseWheel != 0) { rightY = mouseWheelScaled; } if (qKeyDown) { rightX = 1; } if (eKeyDown) { rightX = -1; } if (lShiftKeyDown) { leftTrigger = 1; } characterOffset = followXform.position + (distanceUp * followXform.up); Vector3 lookAt = characterOffset; targetPosition = Vector3.zero; // Determine camera state // * Targeting * if (leftTrigger > TARGETING_THRESHOLD) { barEffect.coverage = Mathf.SmoothStep(barEffect.coverage, widescreen, targetingTime); camState = CamStates.Target; } else { barEffect.coverage = Mathf.SmoothStep(barEffect.coverage, 0f, targetingTime); // * First Person * if (rightY > firstPersonThreshold && camState != CamStates.Free && !follow.IsInLocomotion()) { // Reset look before entering the first person mode xAxisRot = 0; lookWeight = 0f; camState = CamStates.FirstPerson; } // * Free * if ((rightY < freeThreshold || mouseWheel < 0f) && System.Math.Round(follow.Speed, 2) == 0) { camState = CamStates.Free; savedRigToGoal = Vector3.zero; } // * Behind the back * if ((camState == CamStates.FirstPerson && bButtonPressed) || (camState == CamStates.Target && leftTrigger <= TARGETING_THRESHOLD)) { camState = CamStates.Behind; } } // Set the Look At Weight - amount to use look at IK vs using the head's animation follow.Animator.SetLookAtWeight(lookWeight); // Execute camera state switch (camState) { case CamStates.Behind: ResetCamera(); // Only update camera look direction if moving if (follow.Speed > follow.LocomotionThreshold && follow.IsInLocomotion() && !follow.IsInPivot()) { lookDir = Vector3.Lerp(followXform.right * (leftX < 0 ? 1f : -1f), followXform.forward * (leftY < 0 ? -1f : 1f), Mathf.Abs(Vector3.Dot(this.transform.forward, followXform.forward))); Debug.DrawRay(this.transform.position, lookDir, Color.white); // Calculate direction from camera to player, kill Y, and normalize to give a valid direction with unit magnitude curLookDir = Vector3.Normalize(characterOffset - this.transform.position); curLookDir.y = 0; Debug.DrawRay(this.transform.position, curLookDir, Color.green); // Damping makes it so we don't update targetPosition while pivoting; camera shouldn't rotate around player curLookDir = Vector3.SmoothDamp(curLookDir, lookDir, ref velocityLookDir, lookDirDampTime); } targetPosition = characterOffset + followXform.up * distanceUp - Vector3.Normalize(curLookDir) * distanceAway; Debug.DrawLine(followXform.position, targetPosition, Color.magenta); break; case CamStates.Target: ResetCamera(); lookDir = followXform.forward; curLookDir = followXform.forward; targetPosition = characterOffset + followXform.up * distanceUp - lookDir * distanceAway; break; case CamStates.FirstPerson: // Looking up and down // Calculate the amount of rotation and apply to the firstPersonCamPos GameObject xAxisRot += (leftY * 0.5f * firstPersonLookSpeed); xAxisRot = Mathf.Clamp(xAxisRot, firstPersonXAxisClamp.x, firstPersonXAxisClamp.y); firstPersonCamPos.XForm.localRotation = Quaternion.Euler(xAxisRot, 0, 0); // Superimpose firstPersonCamPos GameObject's rotation on camera Quaternion rotationShift = Quaternion.FromToRotation(this.transform.forward, firstPersonCamPos.XForm.forward); this.transform.rotation = rotationShift * this.transform.rotation; // Move character model's head follow.Animator.SetLookAtPosition(firstPersonCamPos.XForm.position + firstPersonCamPos.XForm.forward); lookWeight = Mathf.Lerp(lookWeight, 1.0f, Time.deltaTime * firstPersonLookSpeed); // Looking left and right // Similarly to how character is rotated while in locomotion, use Quaternion * to add rotation to character Vector3 rotationAmount = Vector3.Lerp(Vector3.zero, new Vector3(0f, fPSRotationDegreePerSecond * (leftX < 0f ? -1f : 1f), 0f), Mathf.Abs(leftX)); Quaternion deltaRotation = Quaternion.Euler(rotationAmount * Time.deltaTime); follow.transform.rotation = (follow.transform.rotation * deltaRotation); // Move camera to firstPersonCamPos targetPosition = firstPersonCamPos.XForm.position; // Smoothly transition look direction towards firstPersonCamPos when entering first person mode lookAt = Vector3.Lerp(targetPosition + followXform.forward, this.transform.position + this.transform.forward, camSmoothDampTime * Time.deltaTime); Debug.DrawRay(Vector3.zero, lookAt, Color.black); Debug.DrawRay(Vector3.zero, targetPosition + followXform.forward, Color.white); Debug.DrawRay(Vector3.zero, firstPersonCamPos.XForm.position + firstPersonCamPos.XForm.forward, Color.cyan); // Choose lookAt target based on distance lookAt = (Vector3.Lerp(this.transform.position + this.transform.forward, lookAt, Vector3.Distance(this.transform.position, firstPersonCamPos.XForm.position))); break; case CamStates.Free: lookWeight = Mathf.Lerp(lookWeight, 0.0f, Time.deltaTime * firstPersonLookSpeed); Vector3 rigToGoal = characterOffset - cameraXform.position; rigToGoal.y = 0f; Debug.DrawRay(cameraXform.transform.position, rigToGoal, Color.red); // Panning in and out // If statement works for positive values; don't tween if stick not increasing in either direction; also don't tween if user is rotating // Checked against rightStickThreshold because very small values for rightY mess up the Lerp function if (rightY < lastStickMin && rightY < -1f * rightStickThreshold && rightY <= rightStickPrevFrame.y && Mathf.Abs(rightX) < rightStickThreshold) { // Zooming out distanceUpFree = Mathf.Lerp(distanceUp, distanceUp * distanceUpMultiplier, Mathf.Abs(rightY)); distanceAwayFree = Mathf.Lerp(distanceAway, distanceAway * distanceAwayMultipler, Mathf.Abs(rightY)); targetPosition = characterOffset + followXform.up * distanceUpFree - RigToGoalDirection * distanceAwayFree; lastStickMin = rightY; } else if (rightY > rightStickThreshold && rightY >= rightStickPrevFrame.y && Mathf.Abs(rightX) < rightStickThreshold) { // Zooming in // Subtract height of camera from height of player to find Y distance distanceUpFree = Mathf.Lerp(Mathf.Abs(transform.position.y - characterOffset.y), camMinDistFromChar.y, rightY); // Use magnitude function to find X distance distanceAwayFree = Mathf.Lerp(rigToGoal.magnitude, camMinDistFromChar.x, rightY); targetPosition = characterOffset + followXform.up * distanceUpFree - RigToGoalDirection * distanceAwayFree; lastStickMin = float.PositiveInfinity; } // Store direction only if right stick inactive if (rightX != 0 || rightY != 0) { savedRigToGoal = RigToGoalDirection; } // Rotating around character cameraXform.RotateAround(characterOffset, followXform.up, freeRotationDegreePerSecond * (Mathf.Abs(rightX) > rightStickThreshold ? rightX : 0f)); // Still need to track camera behind player even if they aren't using the right stick; achieve this by saving distanceAwayFree every frame if (targetPosition == Vector3.zero) { targetPosition = characterOffset + followXform.up * distanceUpFree - savedRigToGoal * distanceAwayFree; } break; } CompensateForWalls(characterOffset, ref targetPosition); SmoothPosition(cameraXform.position, targetPosition); transform.LookAt(lookAt); // Make sure to cache the unscaled mouse wheel value if using mouse/keyboard instead of controller rightStickPrevFrame = new Vector2(rightX, rightY); //mouseWheel != 0 ? mouseWheelScaled : rightY); }
void LateUpdate() { //Pull values from the controller/keyboard float rightX = Input.GetAxis("RightStickX"); float rightY = Input.GetAxis("RightStickY"); float leftX = Input.GetAxis("Horizontal"); float leftY = Input.GetAxis("Vertical"); Debug.Log(rightX); Vector3 characterOffset = followXform.position + new Vector3(0f, distanceUp, 0f); Vector3 lookAt = characterOffset; Vector3 targetPosition = Vector3.zero; //Determine camera state if (Input.GetAxis("Target") > TARGETING_THRESHOLD) { barEffect.coverage = Mathf.SmoothStep(barEffect.coverage, widescreen, targetingTime); camState = CamStates.Target; } else { barEffect.coverage = Mathf.SmoothStep(barEffect.coverage, 0f, targetingTime); // * First Person * if (rightY > firstPersonThreshold && camState != CamStates.Free && !follow.IsInLocomotion()) { //Reset look before entering the first person mode xAxisRot = 0; lookWeight = 0f; camState = CamStates.FirstPerson; } // * Free Camera * if (rightY < freeThreshold && System.Math.Round(follow.Speed, 2) == 0) { camState = CamStates.Free; savedRigToGoal = Vector3.zero; } // * Behind the back * if ((camState == CamStates.FirstPerson && Input.GetButton("ExitFPV")) || (camState == CamStates.Target && (Input.GetAxis("Target") <= TARGETING_THRESHOLD))) { camState = CamStates.Behind; } } //Set the look at weight - amount to use to look at IK vs using the head's animation ** Needs to be setup properly for UNITY5 //follow.Animator.SetLookAtWeight(lookWeight); //Execute camera state switch (camState) { case CamStates.Behind: ResetCamera(); // Only update camera look direction if moving if (follow.Speed > follow.LocomotionThreshold && follow.IsInLocomotion()) { lookDir = Vector3.Lerp(followXform.right * (leftX < 0 ? 1f : -1f), followXform.forward * (leftY < 0 ? -1f : 1f), Mathf.Abs(Vector3.Dot(this.transform.forward, followXform.forward))); Debug.DrawRay(this.transform.position, lookDir, Color.white); // Calculate direction from camera to player, kill Y, and normalize to give a calid direction with unit magnitude curLookDir = Vector3.Normalize(characterOffset - this.transform.position); curLookDir.y = 0; Debug.DrawRay(this.transform.position, curLookDir, Color.green); // Damping makes it so we don't update targetPosition while pivoting; camera shouldn't rotate around player curLookDir = Vector3.SmoothDamp(curLookDir, lookDir, ref velocityLookDir, lookDirDampTime); } targetPosition = characterOffset + followXform.up * distanceUp - Vector3.Normalize(curLookDir) * distanceAway; Debug.DrawLine(followXform.position, targetPosition, Color.magenta); break; case CamStates.Target: ResetCamera(); lookDir = followXform.forward; curLookDir = followXform.forward; targetPosition = characterOffset + followXform.up * distanceUp - lookDir * distanceAway; break; case CamStates.FirstPerson: //Looking up and down //Calculate the amount of rotation and apply to the firsPersonCamPos GameObject xAxisRot += (leftY * firstPersonLookSpeed); xAxisRot = Mathf.Clamp(xAxisRot, firstPersonXAxisClamp.x, firstPersonXAxisClamp.y); firstPersonCamPos.Xform.localRotation = Quaternion.Euler(xAxisRot, 0, 0); // Superimpose firstPersonCamPos GameObject's rotation on camera Quaternion rotationShift = Quaternion.FromToRotation(this.transform.forward, firstPersonCamPos.Xform.forward); this.transform.rotation = rotationShift * this.transform.rotation; // Move character model's head **Needs to be updated for UNITY 5 //follow.Animator.SetLookAtPosition(firstPersonCamPos.Xform.position + firstPersonCamPos.Xform.forward); //lookWeight = Mathf.Lerp(lookWeight, 1.0f, Time.deltaTime * firstPersonLookSpeed); // Looking left and right // Similarly to how character is rotated while in locomotion, use Quaternion * to add rotation to character Vector3 rotationAmount = Vector3.Lerp(Vector3.zero, new Vector3(0f, fPSRotationDegreePerSecond * (leftX < 0f ? -1f : 1f), 0f), Mathf.Abs(leftX)); Quaternion deltaRotation = Quaternion.Euler(rotationAmount * Time.deltaTime); follow.transform.rotation = (follow.transform.rotation * deltaRotation); // Move camera to first person position targetPosition = firstPersonCamPos.Xform.position; // Smoothly transition look direction towards firstPersonCamPos when entering first person mode lookAt = Vector3.Lerp(targetPosition + followXform.forward, this.transform.position + this.transform.forward, camSmoothDampTime * Time.deltaTime); Debug.DrawRay(Vector3.zero, lookAt, Color.black); Debug.DrawRay(Vector3.zero, targetPosition + followXform.forward, Color.white); Debug.DrawRay(Vector3.zero, firstPersonCamPos.Xform.position + firstPersonCamPos.Xform.forward, Color.cyan); // Choose look at target based on distance lookAt = (Vector3.Lerp(this.transform.position + this.transform.forward, lookAt, Vector3.Distance(this.transform.position, firstPersonCamPos.Xform.position))); break; case CamStates.Free: lookWeight = Mathf.Lerp(lookWeight, 0.0f, Time.deltaTime * firstPersonLookSpeed); // Move height and distance from character in separate parentRig transform since RoatateAround has control of both postion and rotation Vector3 rigToGoalDirection = Vector3.Normalize(characterOffset - this.transform.position); // Can't calculate distanceAway from a vector with Y axis rotation in it; zero it out rigToGoalDirection.y = 0f; Vector3 rigToGoal = characterOffset - parentRig.position; rigToGoal.y = 0; Debug.DrawRay(parentRig.transform.position, rigToGoal, Color.red); //Moving camera in and out //If statement works for positive values; don't tween if stick not increasing in either direction; // also don't tween if user is rotating. Checked agains RIGHT X THRESHOLD because very small values for rightY mess up the Lerp function if (rightY < -1f * rightStickThreshold && rightY <= rightStickPrevFrame.y && Mathf.Abs(rightX) < rightStickThreshold) { distanceUpFree = Mathf.Lerp(distanceUp, distanceUp * distanceUpMultiplier, Mathf.Abs(rightY)); distanceAwayFree = Mathf.Lerp(distanceAway, distanceAway * distanceAwayMultiplier, Mathf.Abs(rightY)); targetPosition = characterOffset + followXform.up * distanceUpFree - rigToGoalDirection * distanceAwayFree; } else if (rightY > rightStickThreshold && rightY >= rightStickPrevFrame.y && Mathf.Abs(rightX) < rightStickThreshold) { //Subtract height of camera from height of player to find Y distance distanceUpFree = Mathf.Lerp(Mathf.Abs(transform.position.y - characterOffset.y), camMindDistFromChar.y, rightY); //Use magnitude function to find X distance distanceAwayFree = Mathf.Lerp(rigToGoal.magnitude, camMindDistFromChar.x, rightY); targetPosition = characterOffset + followXform.up * distanceUpFree - rigToGoalDirection * distanceAwayFree; } //Store direction onlu if right stick inactive if (rightX != 0 || rightY != 0) { savedRigToGoal = rigToGoalDirection; } //Rotating Around parentRig.RotateAround(characterOffset, followXform.up, freeRotationDegreePerSecond * (Mathf.Abs(rightX) > rightStickThreshold ? rightX : 0f)); // Still need to track camera behind player even if they aren't using the right stick; achive this by saving distanceAwayFree every frame if (targetPosition == Vector3.zero) { targetPosition = characterOffset + followXform.up * distanceUpFree - savedRigToGoal * distanceAwayFree; } //SmoothPosition(transform.position, targetPosition); //transform.LookAt(lookAt); break; } //if (camState != CamStates.Free) //{ CompensateForWalls(characterOffset, ref targetPosition); SmoothPosition(this.transform.position, targetPosition); //make sure the camera is looking the right way! transform.LookAt(lookAt); //} rightStickPrevFrame = new Vector2(rightX, rightY); }
// happens after all updates so we can be sure camera will be moving to right position void LateUpdate() { // Pull values from controller/keyboard float rightX = Input.GetAxis("RightStickX"); float rightY = Input.GetAxis("RightStickY"); // Setup character offset Vector3 characterOffset = followXform.position + new Vector3(0f, distanceUp, 0f); // Set default lookAt value to point at character Vector3 lookAt = characterOffset; // Determine camera state if (Input.GetAxis("Target") > targetingThreshold) { // barEffect.coverage = Mathf.SmoothStep (barEffect.coverage, widescreen, targetingTime); // Reset the head's position to face body's forward follow.Animator.SetLookAtPosition(follow.transform.position + follow.transform.forward); lookWeight = 0f; camState = CamStates.Target; } else { // barEffect.coverage = Mathf.SmoothStep (barEffect.coverage, 0f, targetingTime); // Enter FP mode if not moving and player pushes rightY up or down if ((Mathf.Abs(rightY) + Mathf.Abs(rightX)) > firstPersonThreshold && camState != CamStates.FirstPerson && !follow.IsInLocomotion()) { // Set the clipping plane when entering first person mode this.camera.nearClipPlane = camFirstPersonNearClippingPlane; camState = CamStates.FirstPerson; } // Exit FP mode if player hits exit button and target mode if player is no longer requesting targeting if ((camState == CamStates.FirstPerson && Input.GetButton("ExitFPV")) || (camState == CamStates.Target && (Input.GetAxis("Target") <= targetingThreshold))) { // Set the clipping plane when entering first person mode this.camera.nearClipPlane = camDefaultNearClippingPlane; // Reset the head's position to face body's forward follow.Animator.SetLookAtPosition(follow.transform.position + follow.transform.forward); lookWeight = 0f; camState = CamStates.Behind; } } // Set the Look Weight - amount to use look at IK vs using the head's animation follow.Animator.SetLookAtWeight(lookWeight); // Execute camera state switch (camState) { case CamStates.Behind: { // Calculate the direction from camera to player, kill Y, and normalize to give a valid direction with unit magnitude lookDir = characterOffset - this.transform.position; lookDir.y = 0; lookDir.Normalize(); // Debug.DrawRay (this.transform.position, lookDir, Color.yellow); // setting the target position to be the correct offset from the follow target // targetPosition = followTransform.position + followTransform.up * distanceUp - followTransform.forward * distanceAway; // setting the target position to be the correct distance from the follow target targetPosition = characterOffset + followXform.up * distanceUp - lookDir * distanceAway; // Debug.DrawRay (this.transform.position, targetPosition, Color.green); // Debug.DrawRay (followTransform.position, followTransform.up * distanceUp, Color.red); // Debug.DrawRay (followTransform.position, -1f * followTransform.forward * distanceAway, Color.blue); // Debug.DrawLine (followTransform.position, targetPosition, Color.magenta); break; } case CamStates.Target: { // position camera behind player by turning camera to face player's forward lookDir = followXform.forward; targetPosition = characterOffset + followXform.up * distanceUp - lookDir * distanceAway; break; } case CamStates.FirstPerson: // Looking up and down // Calculate the amount of rotation and apply to the firstPersonCamPos GameObject xAxisRot += (rightY * firstPersonLookSpeed); xAxisRot = Mathf.Clamp(xAxisRot, firstPersonXAxisClamp.x, firstPersonXAxisClamp.y); yAxisRot += (rightX * firstPersonLookSpeed); yAxisRot = Mathf.Clamp(yAxisRot, firstPersonYAxisClamp.x, firstPersonYAxisClamp.y); firstPersonCamPos.XForm.localRotation = Quaternion.Euler(xAxisRot, yAxisRot, 0); // Superimpose firstPersonCamPos GameObject's rotation on camera Quaternion rotationShift = Quaternion.FromToRotation(this.transform.forward, firstPersonCamPos.XForm.forward); this.transform.rotation = rotationShift * this.transform.rotation; // Move the character model's head follow.Animator.SetLookAtPosition(firstPersonCamPos.XForm.position + firstPersonCamPos.XForm.forward); lookWeight = Mathf.Lerp(lookWeight, 1.0f, Time.deltaTime * firstPersonLookSpeed); // Move camera to firstPersonCamPos targetPosition = firstPersonCamPos.XForm.position; // Choose lookAt target based on distance lookAt = (Vector3.Lerp(this.transform.position + this.transform.forward, lookAt, Vector3.Distance(this.transform.position, firstPersonCamPos.XForm.position))); break; } compensateForWalls(characterOffset, ref targetPosition); smoothPosition(transform.position, targetPosition); // make sure the camera is looking the right way! transform.LookAt(lookAt); }