Vector3 GetLookAtPoint(ref CameraState state) { var fwd = state.CorrectedOrientation * Vector3.forward; var camPos = state.CorrectedPosition; var aimDistance = AimDistance; // We don't want to hit targets behind the player var player = VirtualCamera.Follow; if (player != null) { var playerPos = Quaternion.Inverse(state.CorrectedOrientation) * (player.position - camPos); if (playerPos.z > 0) { camPos += fwd * playerPos.z; aimDistance -= playerPos.z; } } aimDistance = Mathf.Max(1, aimDistance); bool hasHit = RuntimeUtility.RaycastIgnoreTag( new Ray(camPos, fwd), out RaycastHit hitInfo, aimDistance, AimCollisionFilter, IgnoreTag); return(hasHit ? hitInfo.point : camPos + fwd * aimDistance); }
Vector3 GetLookAtPoint(ref CameraState state) { var fwd = state.CorrectedOrientation * Vector3.forward; var camPos = state.CorrectedPosition; bool hasHit = RuntimeUtility.RaycastIgnoreTag( new Ray(camPos, fwd), out RaycastHit hitInfo, AimDistance, AimCollisionFilter, IgnoreTag); return(hasHit ? hitInfo.point : camPos + fwd * AimDistance); }
Vector3 ComputeAimTarget(Vector3 cameraLookAt, Transform player) { // Adjust for actual player aim target (may be different due to offset) var playerPos = player.position; var dir = cameraLookAt - playerPos; if (RuntimeUtility.RaycastIgnoreTag(new Ray(playerPos, dir), out RaycastHit hitInfo, dir.magnitude, AimCollisionFilter, IgnoreTag)) { return(hitInfo.point); } return(cameraLookAt); }
Vector3 ComputeLookAtPoint(Vector3 camPos, Transform player) { // We don't want to hit targets behind the player var aimDistance = AimDistance; var playerOrientation = player.rotation; var fwd = playerOrientation * Vector3.forward; var playerPos = Quaternion.Inverse(playerOrientation) * (player.position - camPos); if (playerPos.z > 0) { camPos += fwd * playerPos.z; aimDistance -= playerPos.z; } aimDistance = Mathf.Max(1, aimDistance); bool hasHit = RuntimeUtility.RaycastIgnoreTag(new Ray(camPos, fwd), out RaycastHit hitInfo, aimDistance, AimCollisionFilter, IgnoreTag); return(hasHit ? hitInfo.point : camPos + fwd * aimDistance); }
Vector3 PullCameraInFrontOfNearestObstacle( Vector3 cameraPos, Vector3 lookAtPos, int layerMask, ref RaycastHit hitInfo) { Vector3 displacement = Vector3.zero; Vector3 dir = cameraPos - lookAtPos; float targetDistance = dir.magnitude; if (targetDistance > Epsilon) { dir /= targetDistance; float minDistanceFromTarget = Mathf.Max(m_MinimumDistanceFromTarget, Epsilon); if (targetDistance < minDistanceFromTarget + Epsilon) { displacement = dir * (minDistanceFromTarget - targetDistance); } else { float rayLength = targetDistance - minDistanceFromTarget; if (m_DistanceLimit > Epsilon) { rayLength = Mathf.Min(m_DistanceLimit, rayLength); } // Make a ray that looks towards the camera, to get the obstacle closest to target Ray ray = new Ray(cameraPos - rayLength * dir, dir); rayLength += k_PrecisionSlush; if (rayLength > Epsilon) { if (RuntimeUtility.RaycastIgnoreTag( ray, out hitInfo, rayLength, layerMask, m_IgnoreTag)) { // Pull camera forward in front of obstacle float adjustment = Mathf.Max(0, hitInfo.distance - k_PrecisionSlush); displacement = ray.GetPoint(adjustment) - cameraPos; } } } } return(displacement); }
bool CheckForTargetObstructions(CameraState state) { if (state.HasLookAt) { Vector3 lookAtPos = state.ReferenceLookAt; Vector3 pos = state.CorrectedPosition; Vector3 dir = lookAtPos - pos; float distance = dir.magnitude; if (distance < Mathf.Max(m_MinimumDistanceFromTarget, Epsilon)) { return(true); } Ray ray = new Ray(pos, dir.normalized); if (RuntimeUtility.RaycastIgnoreTag(ray, out _, distance - m_MinimumDistanceFromTarget, m_CollideAgainst & ~m_TransparentLayers, m_IgnoreTag)) { return(true); } } return(false); }
void DrawReticle(CinemachineBrain brain) { if (!brain.IsLive(VirtualCamera) || brain.OutputCamera == null) { CinemachineCore.CameraUpdatedEvent.RemoveListener(DrawReticle); } else { var player = VirtualCamera.Follow; if (AimTargetReticle != null && player != null) { // Adjust for actual player aim target (may be different due to offset) var playerPos = player.position; var aimTarget = VirtualCamera.State.ReferenceLookAt; var dir = aimTarget - playerPos; if (RuntimeUtility.RaycastIgnoreTag(new Ray(playerPos, dir), out RaycastHit hitInfo, dir.magnitude, AimCollisionFilter, IgnoreTag)) { aimTarget = hitInfo.point; } AimTargetReticle.position = brain.OutputCamera.WorldToScreenPoint(aimTarget); } } }
Vector3 RespectCameraRadius(Vector3 cameraPos, Vector3 lookAtPos) { Vector3 result = Vector3.zero; if (m_CameraRadius < Epsilon || m_CollideAgainst == 0) { return(result); } Vector3 dir = cameraPos - lookAtPos; float distance = dir.magnitude; if (distance > Epsilon) { dir /= distance; } // Pull it out of any intersecting obstacles RaycastHit hitInfo; int numObstacles = Physics.OverlapSphereNonAlloc( cameraPos, m_CameraRadius, s_ColliderBuffer, m_CollideAgainst, QueryTriggerInteraction.Ignore); if (numObstacles == 0 && m_TransparentLayers != 0 && distance > m_MinimumDistanceFromTarget + Epsilon) { // Make sure the camera position isn't completely inside an obstacle. // OverlapSphereNonAlloc won't catch those. float d = distance - m_MinimumDistanceFromTarget; Vector3 targetPos = lookAtPos + dir * m_MinimumDistanceFromTarget; if (RuntimeUtility.RaycastIgnoreTag(new Ray(targetPos, dir), out hitInfo, d, m_CollideAgainst, m_IgnoreTag)) { // Only count it if there's an incoming collision but not an outgoing one Collider c = hitInfo.collider; if (!c.Raycast(new Ray(cameraPos, -dir), out hitInfo, d)) { s_ColliderBuffer[numObstacles++] = c; } } } if (numObstacles > 0 && distance == 0 || distance > m_MinimumDistanceFromTarget) { var scratchCollider = RuntimeUtility.GetScratchCollider(); scratchCollider.radius = m_CameraRadius; Vector3 newCamPos = cameraPos; for (int i = 0; i < numObstacles; ++i) { Collider c = s_ColliderBuffer[i]; if (m_IgnoreTag.Length > 0 && c.CompareTag(m_IgnoreTag)) { continue; } // If we have a lookAt target, move the camera to the nearest edge of obstacle if (distance > m_MinimumDistanceFromTarget) { dir = newCamPos - lookAtPos; float d = dir.magnitude; if (d > Epsilon) { dir /= d; var ray = new Ray(lookAtPos, dir); if (c.Raycast(ray, out hitInfo, d + m_CameraRadius)) { newCamPos = ray.GetPoint(hitInfo.distance) - (dir * k_PrecisionSlush); } } } if (Physics.ComputePenetration( scratchCollider, newCamPos, Quaternion.identity, c, c.transform.position, c.transform.rotation, out var offsetDir, out var offsetDistance)) { newCamPos += offsetDir * offsetDistance; } } result = newCamPos - cameraPos; } // Respect the minimum distance from target - push camera back if we have to if (distance > Epsilon && m_MinimumDistanceFromTarget > Epsilon) { float minDistance = Mathf.Max(m_MinimumDistanceFromTarget, m_CameraRadius) + k_PrecisionSlush; Vector3 newOffset = cameraPos + result - lookAtPos; if (newOffset.magnitude < minDistance) { result = lookAtPos - cameraPos + dir * minDistance; } } return(result); }
Vector3 PushCameraBack( Vector3 currentPos, Vector3 pushDir, RaycastHit obstacle, Vector3 lookAtPos, Plane startPlane, float targetDistance, int iterations, ref VcamExtraState extra) { // Take a step along the wall. Vector3 pos = currentPos; Vector3 dir = Vector3.zero; if (!GetWalkingDirection(pos, pushDir, obstacle, ref dir)) { return(pos); } Ray ray = new Ray(pos, dir); float distance = GetPushBackDistance(ray, startPlane, targetDistance, lookAtPos); if (distance <= Epsilon) { return(pos); } // Check only as far as the obstacle bounds float clampedDistance = ClampRayToBounds(ray, distance, obstacle.collider.bounds); distance = Mathf.Min(distance, clampedDistance + k_PrecisionSlush); if (RuntimeUtility.RaycastIgnoreTag(ray, out var hitInfo, distance, m_CollideAgainst & ~m_TransparentLayers, m_IgnoreTag)) { // We hit something. Stop there and take a step along that wall. float adjustment = hitInfo.distance - k_PrecisionSlush; pos = ray.GetPoint(adjustment); extra.AddPointToDebugPath(pos); if (iterations > 1) { pos = PushCameraBack( pos, dir, hitInfo, lookAtPos, startPlane, targetDistance, iterations - 1, ref extra); } return(pos); } // Didn't hit anything. Can we push back all the way now? pos = ray.GetPoint(distance); // First check if we can still see the target. If not, abort dir = pos - lookAtPos; float d = dir.magnitude; if (d < Epsilon || RuntimeUtility.RaycastIgnoreTag( new Ray(lookAtPos, dir), out _, d - k_PrecisionSlush, m_CollideAgainst & ~m_TransparentLayers, m_IgnoreTag)) { return(currentPos); } // All clear ray = new Ray(pos, dir); extra.AddPointToDebugPath(pos); distance = GetPushBackDistance(ray, startPlane, targetDistance, lookAtPos); if (distance > Epsilon) { if (!RuntimeUtility.RaycastIgnoreTag(ray, out hitInfo, distance, m_CollideAgainst & ~m_TransparentLayers, m_IgnoreTag)) { pos = ray.GetPoint(distance); // no obstacles - all good extra.AddPointToDebugPath(pos); } else { // We hit something. Stop there and maybe take a step along that wall float adjustment = hitInfo.distance - k_PrecisionSlush; pos = ray.GetPoint(adjustment); extra.AddPointToDebugPath(pos); if (iterations > 1) { pos = PushCameraBack( pos, dir, hitInfo, lookAtPos, startPlane, targetDistance, iterations - 1, ref extra); } } } return(pos); }
private Vector3 RespectCameraRadius(Vector3 cameraPos, ref CameraState state) { Vector3 result = Vector3.zero; if (m_CameraRadius < Epsilon || m_CollideAgainst == 0) { return(result); } Vector3 dir = state.HasLookAt ? (cameraPos - state.ReferenceLookAt) : Vector3.zero; Ray ray = new Ray(); float distance = dir.magnitude; if (distance > Epsilon) { dir /= distance; ray = new Ray(state.ReferenceLookAt, dir); } // Pull it out of any intersecting obstacles RaycastHit hitInfo; int numObstacles = Physics.OverlapSphereNonAlloc( cameraPos, m_CameraRadius, mColliderBuffer, m_CollideAgainst, QueryTriggerInteraction.Ignore); if (numObstacles == 0 && m_TransparentLayers != 0 && distance > m_MinimumDistanceFromTarget + Epsilon) { // Make sure the camera position isn't completely inside an obstacle. // OverlapSphereNonAlloc won't catch those. float d = distance - m_MinimumDistanceFromTarget; Vector3 targetPos = state.ReferenceLookAt + dir * m_MinimumDistanceFromTarget; if (RuntimeUtility.RaycastIgnoreTag(new Ray(targetPos, dir), out hitInfo, d, m_CollideAgainst, m_IgnoreTag)) { // Only count it if there's an incoming collision but not an outgoing one Collider c = hitInfo.collider; if (!c.Raycast(new Ray(cameraPos, -dir), out hitInfo, d)) { mColliderBuffer[numObstacles++] = c; } } } if (numObstacles > 0 && distance == 0 || distance > m_MinimumDistanceFromTarget) { if (mCameraColliderGameObject == null) { mCameraColliderGameObject = new GameObject("CinemachineCollider Collider"); mCameraColliderGameObject.hideFlags = HideFlags.HideAndDontSave; mCameraColliderGameObject.transform.position = Vector3.zero; mCameraColliderGameObject.SetActive(true); mCameraCollider = mCameraColliderGameObject.AddComponent <SphereCollider>(); mCameraCollider.isTrigger = true; var rb = mCameraColliderGameObject.AddComponent <Rigidbody>(); rb.detectCollisions = false; rb.isKinematic = true; } mCameraCollider.radius = m_CameraRadius; Vector3 offsetDir; float offsetDistance; Vector3 newCamPos = cameraPos; for (int i = 0; i < numObstacles; ++i) { Collider c = mColliderBuffer[i]; if (m_IgnoreTag.Length > 0 && c.CompareTag(m_IgnoreTag)) { continue; } // If we have a lookAt target, move the camera to the nearest edge of obstacle if (distance > m_MinimumDistanceFromTarget) { dir = newCamPos - state.ReferenceLookAt; float d = dir.magnitude; if (d > Epsilon) { dir /= d; ray = new Ray(state.ReferenceLookAt, dir); if (c.Raycast(ray, out hitInfo, d + m_CameraRadius)) { newCamPos = ray.GetPoint(hitInfo.distance) - (dir * PrecisionSlush); } } } if (Physics.ComputePenetration( mCameraCollider, newCamPos, Quaternion.identity, c, c.transform.position, c.transform.rotation, out offsetDir, out offsetDistance)) { newCamPos += offsetDir * offsetDistance; } } result = newCamPos - cameraPos; } // Respect the minimum distance from target - push camera back if we have to if (distance > Epsilon) { float minDistance = Mathf.Max(m_MinimumDistanceFromTarget, m_CameraRadius) + PrecisionSlush; Vector3 newOffset = cameraPos + result - state.ReferenceLookAt; if (newOffset.magnitude < minDistance) { result = state.ReferenceLookAt - cameraPos + dir * minDistance; } } return(result); }