package com.github.czyzby.kiwi.util.gdx.viewport; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.utils.Scaling; import com.badlogic.gdx.utils.viewport.ScalingViewport; import com.github.czyzby.kiwi.util.gdx.GdxUtilities; /** Combines {@link com.badlogic.gdx.utils.viewport.ScreenViewport screen} and * {@link com.badlogic.gdx.utils.viewport.FitViewport fit} viewports functionalities. Similarly to screen viewport, * world size is changed on each update (resize), so {@link #update(int, int, boolean)} should be generally called with * {@code true} parameter (camera should be centered). Tries to keep the passed aspect ratio by applying letterboxing * (horizontal or vertical black bars. * * <p> * On contrary to regular screen viewport, this viewport analyzes screen density (pixel per inch ratio) to preserve * correct look on every platform, including mobiles. This is very convenient for GUIs (especially when using fit * viewport with the same aspect ratio for game logic rendering), as they will not be scaled when the screen is resized * (comparably to using similarly to screen viewport), and yet should still look acceptable on mobile devices * (comparably to using fit viewport with a fixed world size). * * @author MJ */ public class LetterboxingViewport extends ScalingViewport { private float scaleX; private float scaleY; private float targetPpiX; private float targetPpiY; private float aspectRatio; /** Creates a new letterboxing viewport with 4/3 aspect ratio and default target PPI chosen according to the current * platform. */ public LetterboxingViewport() { this(GdxUtilities.isMobile() ? 160f : 96f); } /** Creates a new letterboxing viewport with 4/3 aspect ratio. * * @param targetPpi this is the targeted pixel per inch ratio, which allows to scale the viewport correctly on * different devices. Usually about 96 for desktop and WebGL platforms, 160 for mobiles. */ public LetterboxingViewport(final float targetPpi) { this(targetPpi, targetPpi, 4f / 3f); } /** @param targetPpi this is the targeted pixel per inch ratio, which allows to scale the viewport correctly on * different devices. Usually about 96 for desktop and WebGL platforms, 160 for mobiles. * @param aspectRatio width divided by height. Will preserve this aspect ratio by applying letterboxing. */ public LetterboxingViewport(final float targetPpi, final float aspectRatio) { this(targetPpi, targetPpi, aspectRatio); } /** @param targetPpiX this is the targeted pixel per inch ratio on X axis, which allows to scale the viewport * correctly on different devices. Usually about 96 for desktop and WebGL platforms, 160 for mobiles. * @param targetPpiY targeted pixel per inch ratio on Y axis. Usually about 96 for desktop and WebGL platforms, 160 * for mobiles. * @param aspectRatio width divided by height. Will preserve this aspect ratio by applying letterboxing. */ public LetterboxingViewport(final float targetPpiX, final float targetPpiY, final float aspectRatio) { super(Scaling.fit, 0f, 0f); // Temporary setting world size to mock values. this.targetPpiX = targetPpiX; this.targetPpiY = targetPpiY; this.aspectRatio = aspectRatio; updateScale(); updateWorldSize(); } /** Forces update of current pixel per unit ratio according to screen density. * * @see com.badlogic.gdx.Graphics#getDensity() * @see com.badlogic.gdx.Graphics#getPpiX() * @see com.badlogic.gdx.Graphics#getPpiY() */ public void updateScale() { scaleX = targetPpiX / Gdx.graphics.getPpiX(); scaleY = targetPpiY / Gdx.graphics.getPpiY(); } @Override public void update(final int screenWidth, final int screenHeight, final boolean centerCamera) { updateWorldSize(screenWidth, screenHeight); super.update(screenWidth, screenHeight, centerCamera); } /** Forces update of current world size according to window size. Will try to keep the set aspect ratio. */ public void updateWorldSize() { updateWorldSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); } /** Forces update of current world size according to window size. Will try to keep the set aspect ratio. * * @param screenWidth current screen width. * @param screenHeight current screen height. */ private void updateWorldSize(final int screenWidth, final int screenHeight) { final float width = screenWidth * scaleX; final float height = screenHeight * scaleY; final float fitHeight = width / aspectRatio; if (fitHeight > height) { setWorldSize(height * aspectRatio, height); } else { setWorldSize(width, fitHeight); } } /** @return the targeted pixel per inch ratio on X axis, which allows to scale the viewport correctly on different * devices. Usually about 96 for desktop and WebGL platforms, 160 for mobiles. */ public float getTargetPpiX() { return targetPpiX; } /** @param targetPpiX the targeted pixel per inch ratio on X axis, which allows to scale the viewport correctly on * different devices. Usually about 96 for desktop and WebGL platforms, 160 for mobiles. */ public void setTargetPpiX(final float targetPpiX) { this.targetPpiX = targetPpiX; } /** @return the targeted pixel per inch ratio on Y axis, which allows to scale the viewport correctly on different * devices. Usually about 96 for desktop and WebGL platforms, 160 for mobiles. */ public float getTargetPpiY() { return targetPpiY; } /** @param targetPpiY the targeted pixel per inch ratio on Y axis, which allows to scale the viewport correctly on * different devices. Usually about 96 for desktop and WebGL platforms, 160 for mobiles. */ public void setTargetPpiY(final float targetPpiY) { this.targetPpiY = targetPpiY; } /** @return virtual viewport width divided by height. Affects viewport world size during resizing by forcing it to * add letterboxing. Defaults to 4/3. */ public float getAspectRatio() { return aspectRatio; } /** Allows to directly modify unit per pixel ratio by bypassing PPI check. * * @param scaleX will be used to multiply screen width to obtain the virtual viewport width during resizing. */ public void setScaleX(final float scaleX) { this.scaleX = scaleX; } /** Allows to directly modify unit per pixel ratio by bypassing PPI check. * * @param scaleY will be used to multiply screen height to obtain the virtual viewport height during resizing. */ public void setScaleY(final float scaleY) { this.scaleY = scaleY; } /** @param aspectRatio virtual viewport width divided by height. Affects viewport world size during resizing by * forcing it to add letterboxing. Defaults to 4/3. */ public void setAspectRatio(final float aspectRatio) { this.aspectRatio = aspectRatio; } }