private void CalculateSafeAreaAndCutouts() { var orientationData = m_SupportedOrientations[m_RenderedOrientation]; var safeArea = orientationData.safeArea; Rect onScreenSafeArea = new Rect(); // Calculating where on the screen to draw safe area onScreenSafeArea = safeArea; switch (m_RenderedOrientation) { case ScreenOrientation.Portrait: onScreenSafeArea.y = m_Screen.height - safeArea.height - safeArea.y; break; case ScreenOrientation.PortraitUpsideDown: break; case ScreenOrientation.LandscapeLeft: onScreenSafeArea.y = safeArea.x; onScreenSafeArea.x = safeArea.y; onScreenSafeArea.height = safeArea.width; onScreenSafeArea.width = safeArea.height; break; case ScreenOrientation.LandscapeRight: onScreenSafeArea.y = m_Screen.height - safeArea.width - safeArea.x; onScreenSafeArea.x = m_Screen.width - safeArea.height - safeArea.y; onScreenSafeArea.width = safeArea.height; onScreenSafeArea.height = safeArea.width; break; } if (!m_IsFullScreen) { switch (m_RenderedOrientation) { case ScreenOrientation.PortraitUpsideDown: onScreenSafeArea.yMin += m_Screen.navigationBarHeight; break; case ScreenOrientation.LandscapeLeft: case ScreenOrientation.LandscapeRight: case ScreenOrientation.Portrait: onScreenSafeArea.yMax -= m_Screen.navigationBarHeight; break; } } ScreenSpaceSafeArea = onScreenSafeArea; OnScreenSpaceSafeAreaChanged?.Invoke(ScreenSpaceSafeArea); var screenWidthInOrientation = IsRenderingLandscape ? m_Screen.height : m_Screen.width; var screenHeightInOrientation = IsRenderingLandscape ? m_Screen.width : m_Screen.height; // The inverse of safe area, that is the size of borders excluded from the safe area. // It is more convenient to scale these borders to correct resolution than to scale safe area rect. var unsafeBorders = new Vector4() { x = safeArea.x, y = screenHeightInOrientation - safeArea.height - safeArea.y, z = screenWidthInOrientation - safeArea.width - safeArea.x, w = safeArea.y }; // Need to exclude unsafe area hidden by insets. It is obscured by the inset and therefore disappears. var insetsInOrientation = InsetsInCurrentOrientation; unsafeBorders -= insetsInOrientation; // A negative unsafe border can happen because inset encroaches on the safe area. In other words, unsafe border is smaller than the inset. // In these cases the safe area will border the inset directly on that side of the screen, so unsafe border is clamped to 0. unsafeBorders.x = Mathf.Clamp(unsafeBorders.x, 0, float.MaxValue); unsafeBorders.y = Mathf.Clamp(unsafeBorders.y, 0, float.MaxValue); unsafeBorders.z = Mathf.Clamp(unsafeBorders.z, 0, float.MaxValue); unsafeBorders.w = Mathf.Clamp(unsafeBorders.w, 0, float.MaxValue); // This is the resolution of the part of the screen where game rendering is occuring, it might be different than the actual rendering resolution. var renderAreaWidth = screenWidthInOrientation - insetsInOrientation.x - insetsInOrientation.z; var renderAreaHeight = screenHeightInOrientation - insetsInOrientation.y - insetsInOrientation.w; var resolutionWidthScale = m_CurrentWidth / renderAreaWidth; var resolutionHeightScale = m_CurrentHeight / renderAreaHeight; unsafeBorders.x *= resolutionWidthScale; unsafeBorders.y *= resolutionHeightScale; unsafeBorders.z *= resolutionWidthScale; unsafeBorders.w *= resolutionHeightScale; m_CurrentSafeArea = new Rect(Mathf.Round(unsafeBorders.x), Mathf.Round(unsafeBorders.w), Mathf.Round(m_CurrentWidth - unsafeBorders.x - unsafeBorders.z), Mathf.Round(m_CurrentHeight - unsafeBorders.y - unsafeBorders.w)); if (orientationData.cutouts == null || orientationData.cutouts.Length == 0) { m_CurrentCutouts = new Rect[0]; } else { // Calculating the cutouts and adding the ones that are inside the rendering area. List <Rect> currentCutouts = new List <Rect>(); for (int i = 0; i < orientationData.cutouts.Length; ++i) { var cutout = orientationData.cutouts[i]; var currentCutout = new Rect(Mathf.Round((cutout.x - insetsInOrientation.x) * resolutionWidthScale), Mathf.Round((cutout.y - insetsInOrientation.w) * resolutionHeightScale), Mathf.Round(cutout.width * resolutionWidthScale), Mathf.Round(cutout.height * resolutionHeightScale)); // Negative coordinates can happen if the cutout overlaps the inset. In other words, if the cutout is outside the rendering area. if (currentCutout.x >= 0 && currentCutout.y >= 0 && currentCutout.xMax <= m_CurrentWidth && currentCutout.yMax <= m_CurrentHeight) { currentCutouts.Add(currentCutout); } } m_CurrentCutouts = currentCutouts.ToArray(); } }
private void CalculateSafeAreaAndCutouts() { var safeArea = m_SupportedOrientations[m_RenderedOrientation].safeArea; Rect onScreenSafeArea = new Rect(); // Calculating where on the screen to draw safe area onScreenSafeArea = safeArea; switch (m_RenderedOrientation) { case ScreenOrientation.Portrait: onScreenSafeArea.y = m_Screen.height - safeArea.height - safeArea.y; break; case ScreenOrientation.PortraitUpsideDown: break; case ScreenOrientation.LandscapeLeft: onScreenSafeArea.y = safeArea.x; onScreenSafeArea.x = safeArea.y; onScreenSafeArea.height = safeArea.width; onScreenSafeArea.width = safeArea.height; break; case ScreenOrientation.LandscapeRight: onScreenSafeArea.y = m_Screen.height - safeArea.width - safeArea.x; onScreenSafeArea.x = m_Screen.width - safeArea.height - safeArea.y; onScreenSafeArea.width = safeArea.height; onScreenSafeArea.height = safeArea.width; break; } if (!m_IsFullScreen) { switch (m_RenderedOrientation) { case ScreenOrientation.PortraitUpsideDown: onScreenSafeArea.yMin += m_Screen.navigationBarHeight; break; case ScreenOrientation.LandscapeLeft: case ScreenOrientation.LandscapeRight: case ScreenOrientation.Portrait: onScreenSafeArea.yMax -= m_Screen.navigationBarHeight; break; } } ScreenSpaceSafeArea = onScreenSafeArea; OnScreenSpaceSafeAreaChanged?.Invoke(ScreenSpaceSafeArea); int scaledHeight = 0; var scaledNavigationBarHeight = Mathf.RoundToInt(m_DpiRatio * m_Screen.navigationBarHeight); if (!m_WasResolutionSet) { scaledHeight = scaledNavigationBarHeight; if (m_SupportedOrientations.ContainsKey(ScreenOrientation.Portrait)) { scaledHeight += Mathf.RoundToInt(m_DpiRatio * (m_Screen.height - m_SupportedOrientations[ScreenOrientation.Portrait].safeArea.height)); } } // Always consider the full screen mode width/height to scale the safe area & cutouts. float xScale, yScale; if (IsRenderingLandscape) { xScale = (float)(m_CurrentWidth + (m_IsFullScreen ? 0 : scaledHeight)) / m_Screen.height; yScale = (float)m_CurrentHeight / m_Screen.width; } else { xScale = (float)m_CurrentWidth / m_Screen.width; yScale = (float)(m_CurrentHeight + (m_IsFullScreen ? 0 : scaledHeight)) / m_Screen.height; } // Scale safe area. var odd = m_SupportedOrientations[m_RenderedOrientation]; var sa = odd.safeArea; if (m_IsFullScreen) { m_CurrentSafeArea = new Rect(Mathf.Round(sa.x * xScale), Mathf.Round(sa.y * yScale), Mathf.Round(sa.width * xScale), Mathf.Round(sa.height * yScale)); } else { if (m_WasResolutionSet) { m_CurrentSafeArea = new Rect(0, 0, m_CurrentWidth, m_CurrentHeight); // Set the safe area to current resolution in windowed mode with resolution set. } else { m_CurrentSafeArea = new Rect(0, 0, Mathf.Round(sa.width * xScale), Mathf.Round(sa.height * yScale)); } } // Consider the navigation bar height if it's windowed mode without resolution set. if (!m_IsFullScreen && !m_WasResolutionSet) { switch (m_RenderedOrientation) { case ScreenOrientation.Portrait: case ScreenOrientation.PortraitUpsideDown: m_CurrentSafeArea.height -= scaledNavigationBarHeight; break; case ScreenOrientation.LandscapeLeft: case ScreenOrientation.LandscapeRight: m_CurrentSafeArea.width -= scaledNavigationBarHeight; break; } } // For windowed mode, let's return empty cutouts for now. if (!m_IsFullScreen) { m_CurrentCutouts = new Rect[0]; return; } // Scale cutouts. if (odd.cutouts?.Length > 0) { m_CurrentCutouts = new Rect[odd.cutouts.Length]; for (int i = 0; i < odd.cutouts.Length; ++i) { var cutout = odd.cutouts[i]; m_CurrentCutouts[i] = new Rect(Mathf.Round(cutout.x * xScale), Mathf.Round(cutout.y * yScale), Mathf.Round(cutout.width * xScale), Mathf.Round(cutout.height * yScale)); } } else { m_CurrentCutouts = new Rect[0]; } }
public void ApplyChanges() { var updateSafeArea = false; var orientationEvent = false; var resolutionEvent = false; var fullScreenEvent = false; var screenSpaceSafeAreaEvent = false; var insetsEvent = false; if (m_RequestedOrientation != m_RenderedOrientation) { if (m_RequestedOrientation.IsLandscape() != m_RenderedOrientation.IsLandscape()) { // Swap resolution Width and Height if changing from Portrait to Landscape or vice versa if (m_WasResolutionSet) { (m_RequestedHeight, m_RequestedWidth) = (m_RequestedWidth, m_RequestedHeight); } else { m_RequestDefaultResolution = true; } } m_RenderedOrientation = m_RequestedOrientation; orientationEvent = true; m_RequestInsetUpdate = true; updateSafeArea = true; } if (m_RequestedFullScreen != m_IsFullScreen) { m_IsFullScreen = m_RequestedFullScreen; m_RequestInsetUpdate = true; // We only change the resolution if we never set the resolution by calling Screen.SetResolution(). if (!m_WasResolutionSet) { m_RequestDefaultResolution = true; } updateSafeArea = true; fullScreenEvent = true; } if (m_RequestInsetUpdate) { CalculateInsets(); insetsEvent = true; } if ((m_RequestedWidth != m_CurrentWidth || m_RequestedHeight != m_CurrentHeight) && m_WasResolutionSet) { m_CurrentWidth = m_RequestedWidth; m_CurrentHeight = m_RequestedHeight; updateSafeArea = true; resolutionEvent = true; } else if (m_RequestDefaultResolution) { CalculateResolutionWithInsets(); updateSafeArea = true; resolutionEvent = true; } if (updateSafeArea) { CalculateSafeAreaAndCutouts(); screenSpaceSafeAreaEvent = true; } if (orientationEvent) { OnOrientationChanged?.Invoke(); } if (resolutionEvent) { OnResolutionChanged?.Invoke(m_CurrentWidth, m_CurrentHeight); } if (fullScreenEvent) { OnFullScreenChanged?.Invoke(m_IsFullScreen); } if (screenSpaceSafeAreaEvent) { OnScreenSpaceSafeAreaChanged?.Invoke(ScreenSpaceSafeArea); } if (insetsEvent) { OnInsetsChanged?.Invoke(Insets); } m_RequestDefaultResolution = false; m_RequestedOrientation = m_RenderedOrientation; m_RequestedHeight = m_CurrentHeight; m_RequestedWidth = m_CurrentWidth; m_RequestInsetUpdate = false; }