在调整大小后,LibGDX FitViewport如何保持纵横比?

时间:2017-11-19 17:40:14

标签: libgdx resize viewport aspect-ratio

我有点困惑为什么我的FitViewport在调整窗口大小时没有保持纵横比。 我认为它应该始终保持纵横比,然后用黑条填满屏幕,用于未使用的区域。

然而对我来说,它并没有保持宽高比,圆圈变成省略号。 (见截图)。

创建游戏渲染器时的代码(32和18是我的世界单位)

viewport = new FitViewport(32, 18);
camera = viewport.getCamera();
visibleArea = new Rectangle(0, 0, viewport.getScreenWidth(), viewport.getScreenHeight());
scissors = new Rectangle();

调整窗口大小时的代码

 public void resize(int width, int height) {
   Gdx.app.debug(TAG, "Resizing to " + width + " x " + height);
   viewport.update(width, height);
   visibleArea.set(0, 0, viewport.getScreenWidth(), viewport.getScreenHeight());

渲染方法

public void render(float alpha) {
  viewport.calculateScissors(batch.getTransformMatrix(), visibleArea, scissors);
  ScissorStack.pushScissors(scissors);

  viewport.apply();
  setView(camera.combined, visibleArea.x, visibleArea.y, visibleArea.width, visibleArea.height);

  batch.begin();
  // ...
  batch.end();

  ScissorStack.popScissors();
}

correct aspect ratio on startup

wrong aspect ratio on resize

1 个答案:

答案 0 :(得分:1)

好的问题实际上是我的framebuffer light方法( prepareLightFrameBuffer ),它也调用了batch.begin();和batch.end()。 这似乎搞砸了视图(或将其重置为某种东西?)。为了解决这个问题,我再次应用了视口,并在渲染方法中再次设置了视图(注意:我也有一个舞台,所以我认为必须在这里调用viewport.apply()在stage.render()方法中)。

如果有人有兴趣,这是GameRenderer的完整代码。我猜不知道它可以简化,但我不是OpenGL / Matrix专家所以我不知道怎么做:))

package com.lok.game;

import java.util.Comparator;

import com.badlogic.ashley.core.ComponentMapper;
import com.badlogic.ashley.core.Entity;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.tiled.TiledMapImageLayer;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.badlogic.gdx.maps.tiled.tiles.AnimatedTiledMapTile;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.utils.ScissorStack;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.lok.game.ecs.components.AnimationComponent;
import com.lok.game.ecs.components.CollisionComponent;
import com.lok.game.ecs.components.MapRevelationComponent;
import com.lok.game.ecs.components.SizeComponent;
import com.lok.game.map.Map;
import com.lok.game.map.Map.Portal;
import com.lok.game.map.MapManager;

public class GameRenderer extends OrthogonalTiledMapRenderer {
private final static String TAG = GameRenderer.class.getName();

private static class yPositionComparator implements Comparator<Entity> {
    private final ComponentMapper<SizeComponent> sizeComponentMapper;

    private yPositionComparator(ComponentMapper<SizeComponent> sizeComponentMapper) {
        this.sizeComponentMapper = sizeComponentMapper;
    }

    @Override
    public int compare(Entity o1, Entity o2) {
        if (o1 == o2) {
            return 0;
            } else if (o1 == null) {
            return -1;
            } else if (o2 == null) {
            return 1;
        }

        return sizeComponentMapper.get(o1).boundingRectangle.y > sizeComponentMapper.get(o2).boundingRectangle.y ? -1 : 1;
    }

}

private SizeComponent                  cameraLockEntitySizeComponent;
private MapRevelationComponent              cameraLockEntityRevelationComponent;

private Map                          map;
private TiledMapTileLayer                  groundLayer;
private final Array<TiledMapTileLayer>          backgroundLayers;
private final Array<TiledMapTileLayer>          foregroundLayers;
private TiledMapImageLayer                  lightMapLayer;

private final yPositionComparator              entityComparator;

private final ComponentMapper<SizeComponent>      sizeComponentMapper;
private final ComponentMapper<AnimationComponent> animationComponentMapper;

private final Camera                  camera;
private final Viewport                  viewport;
private final Rectangle                  visibleArea;
private final Rectangle                  scissors;

private final ShapeRenderer                  shapeRenderer;

private FrameBuffer                      frameBuffer;
private final AtlasRegion                  lightTexture;
private final AtlasRegion                  shadowTexture;

public GameRenderer() {
    super(null, MapManager.WORLD_UNITS_PER_PIXEL);

    if (Gdx.app.getLogLevel() == Application.LOG_DEBUG) {
        Gdx.app.debug(TAG, "Creating in debug mode");
        shapeRenderer = new ShapeRenderer();
        } else {
        Gdx.app.debug(TAG, "Creating in non-debug mode");
        shapeRenderer = null;
    }

    viewport = new FitViewport(32, 18);
    camera = viewport.getCamera();
    visibleArea = new Rectangle();
    scissors = new Rectangle();

    this.backgroundLayers = new Array<TiledMapTileLayer>();
    this.foregroundLayers = new Array<TiledMapTileLayer>();

    this.sizeComponentMapper = ComponentMapper.getFor(SizeComponent.class);
    this.animationComponentMapper = ComponentMapper.getFor(AnimationComponent.class);

    this.entityComparator = new yPositionComparator(sizeComponentMapper);

    final TextureAtlas textureAtlas = AssetManager.getManager().getAsset("lights/lights.atlas", TextureAtlas.class);
    lightTexture = textureAtlas.findRegion("light");
    shadowTexture = textureAtlas.findRegion("shadow");
    frameBuffer = null;
}

public void setMap(Map map) {
    this.map = map;
    super.setMap(map.getTiledMap());

    this.backgroundLayers.clear();
    this.foregroundLayers.clear();
    this.lightMapLayer = null;
    for (MapLayer mapLayer : map.getTiledMap().getLayers()) {
        if (mapLayer instanceof TiledMapTileLayer) {
            if ("ground".equals(mapLayer.getName())) {
                groundLayer = (TiledMapTileLayer) mapLayer;
                } else if (mapLayer.getName().startsWith("background")) {
                backgroundLayers.add((TiledMapTileLayer) mapLayer);
                } else {
                foregroundLayers.add((TiledMapTileLayer) mapLayer);
            }
            } else if (mapLayer instanceof TiledMapImageLayer) {
            lightMapLayer = (TiledMapImageLayer) mapLayer;
        }
    }
}

public void resize(int width, int height) {
    Gdx.app.debug(TAG, "Resizing with " + width + "x" + height + " from viewport " + viewport.getScreenWidth() + "x" + viewport.getScreenHeight());
    viewport.update(width, height, false);
    visibleArea.set(0, 0, viewport.getWorldWidth(), viewport.getWorldHeight());
    Gdx.app.debug(TAG, "To viewport " + viewport.getScreenWidth() + "x" + viewport.getScreenHeight());

    if (frameBuffer != null) {
        frameBuffer.dispose();
    }

    try {
        frameBuffer = FrameBuffer.createFrameBuffer(Pixmap.Format.RGBA8888, viewport.getScreenWidth(), viewport.getScreenHeight(), false);
        } catch (GdxRuntimeException e) {
        frameBuffer = FrameBuffer.createFrameBuffer(Pixmap.Format.RGB565, viewport.getScreenWidth(), viewport.getScreenHeight(), false);
    }
}

public void lockCameraToEntity(Entity entity) {
    if (entity == null) {
        cameraLockEntitySizeComponent = null;
        cameraLockEntityRevelationComponent = null;
        } else {
        cameraLockEntityRevelationComponent = entity.getComponent(MapRevelationComponent.class);
        cameraLockEntitySizeComponent = entity.getComponent(SizeComponent.class);

        if (cameraLockEntitySizeComponent == null) {
            throw new GdxRuntimeException("Trying to lock camera to an entity without size component: " + entity);
        }
    }
}

private void interpolateEntities(float alpha) {
    for (Entity entity : map.getEntities()) {
        final SizeComponent sizeComp = sizeComponentMapper.get(entity);

        final float invAlpha = 1.0f - alpha;
        sizeComp.interpolatedPosition.x = sizeComp.interpolatedPosition.x * invAlpha + sizeComp.boundingRectangle.x * alpha;
        sizeComp.interpolatedPosition.y = sizeComp.interpolatedPosition.y * invAlpha + sizeComp.boundingRectangle.y * alpha;
    }
}

public void render(float alpha) {
    AnimatedTiledMapTile.updateAnimationBaseTime();
    interpolateEntities(alpha);
    map.getEntities().sort(entityComparator);

    if (cameraLockEntitySizeComponent != null) {
        camera.position.set(cameraLockEntitySizeComponent.interpolatedPosition, 0);
        visibleArea.setCenter(cameraLockEntitySizeComponent.interpolatedPosition);
    }

    prepareLightFrameBuffer();

    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);

    viewport.apply();
    setView(camera.combined, visibleArea.x, visibleArea.y, visibleArea.width, visibleArea.height);
    batch.begin();
    viewport.calculateScissors(batch.getTransformMatrix(), visibleArea, scissors);
    ScissorStack.pushScissors(scissors);
    if (groundLayer != null) {
        renderTileLayer(groundLayer);
    }
    for (Entity entity : map.getEntities()) {
        renderEntityShadow(entity);
    }
    for (TiledMapTileLayer layer : backgroundLayers) {
        renderTileLayer(layer);
    }
    for (Entity entity : map.getEntities()) {
        renderEntity(entity);
    }
    for (TiledMapTileLayer layer : foregroundLayers) {
        renderTileLayer(layer);
    }
    batch.end();

    applyLightFrameBuffer();

    if (Gdx.app.getLogLevel() == Application.LOG_DEBUG) {
        renderDebugInformation();
    }

    ScissorStack.popScissors();
}

private void renderEntityShadow(Entity entity) {
    final AnimationComponent animationComp = animationComponentMapper.get(entity);

    if (animationComp.animation != null) {
        final SizeComponent sizeComp = sizeComponentMapper.get(entity);
        if (!viewBounds.overlaps(sizeComp.boundingRectangle)) {
            return;
        }

        if (cameraLockEntityRevelationComponent != null && !Intersector.overlaps(cameraLockEntityRevelationComponent.revelationCircle, sizeComp.boundingRectangle)) {
            return;
        }

        batch.draw(shadowTexture, sizeComp.interpolatedPosition.x, sizeComp.interpolatedPosition.y - sizeComp.boundingRectangle.height * 0.2f, sizeComp.boundingRectangle.width,
        sizeComp.boundingRectangle.height * 0.5f);
    }
}

private void renderEntity(Entity entity) {
    final AnimationComponent animationComp = animationComponentMapper.get(entity);

    if (animationComp.animation != null) {
        final SizeComponent sizeComp = sizeComponentMapper.get(entity);
        if (!viewBounds.overlaps(sizeComp.boundingRectangle)) {
            return;
        }

        if (cameraLockEntityRevelationComponent != null && !Intersector.overlaps(cameraLockEntityRevelationComponent.revelationCircle, sizeComp.boundingRectangle)) {
            return;
        }

        final TextureRegion keyFrame = animationComp.animation.getKeyFrame(animationComp.animationTime, true);
        batch.draw(keyFrame, sizeComp.interpolatedPosition.x, sizeComp.interpolatedPosition.y, sizeComp.boundingRectangle.width, sizeComp.boundingRectangle.height);
    }
}

private void prepareLightFrameBuffer() {
    if (cameraLockEntityRevelationComponent != null) {
        frameBuffer.begin();

        final Color mapBackgroundColor = map.getBackgroundColor();
        Gdx.gl.glClearColor(mapBackgroundColor.r, mapBackgroundColor.g, mapBackgroundColor.b, mapBackgroundColor.a);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        setView(camera.combined, visibleArea.x, visibleArea.y, visibleArea.width, visibleArea.height);
        batch.begin();
        if (lightMapLayer != null) {
            batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE);
            renderImageLayer(lightMapLayer);
        }

        batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE);
        final Rectangle boundingRectangle = cameraLockEntitySizeComponent.boundingRectangle;
        batch.draw(lightTexture, cameraLockEntitySizeComponent.interpolatedPosition.x + boundingRectangle.width * 0.5f - cameraLockEntityRevelationComponent.revelationRadius, // x
        cameraLockEntitySizeComponent.interpolatedPosition.y + boundingRectangle.height * 0.5f - cameraLockEntityRevelationComponent.revelationRadius, // y
        cameraLockEntityRevelationComponent.revelationRadius * 2f, cameraLockEntityRevelationComponent.revelationRadius * 2f);

        batch.end();

        frameBuffer.end();
    }
}

private void applyLightFrameBuffer() {
    if (cameraLockEntityRevelationComponent != null) {
        batch.setProjectionMatrix(batch.getProjectionMatrix().idt());
        batch.setBlendFunction(GL20.GL_ZERO, GL20.GL_SRC_COLOR);
        batch.begin();
        batch.draw(frameBuffer.getColorBufferTexture(), -1, 1, 2, -2);
        batch.end();
    }
}

private void renderDebugInformation() {
    shapeRenderer.setProjectionMatrix(camera.combined);
    shapeRenderer.begin(ShapeType.Line);

    shapeRenderer.setColor(Color.RED);
    for (Rectangle rect : map.getCollisionAreas()) {
        shapeRenderer.rect(rect.x, rect.y, rect.width, rect.height);
    }

    for (Entity entity : map.getEntities()) {
        final CollisionComponent collisionComponent = entity.getComponent(CollisionComponent.class);
        final SizeComponent sizeComp = sizeComponentMapper.get(entity);
        if (collisionComponent != null) {
            shapeRenderer.setColor(Color.RED);
            shapeRenderer.rect(sizeComp.interpolatedPosition.x + collisionComponent.rectOffset.x, sizeComp.interpolatedPosition.y + collisionComponent.rectOffset.y,
            collisionComponent.collisionRectangle.width, collisionComponent.collisionRectangle.height);
        }

        if (sizeComp != null) {
            shapeRenderer.setColor(Color.BLUE);
            shapeRenderer.rect(sizeComp.interpolatedPosition.x, sizeComp.interpolatedPosition.y, sizeComp.boundingRectangle.width, sizeComp.boundingRectangle.height);
        }

    }

    shapeRenderer.setColor(Color.BLUE);
    for (Portal portal : map.getPortals()) {
        shapeRenderer.rect(portal.getArea().x, portal.getArea().y, portal.getArea().width, portal.getArea().height);
    }

    if (cameraLockEntityRevelationComponent != null) {
        shapeRenderer.setColor(Color.WHITE);
        shapeRenderer.circle(cameraLockEntitySizeComponent.interpolatedPosition.x + cameraLockEntitySizeComponent.boundingRectangle.width * 0.5f,
        cameraLockEntitySizeComponent.interpolatedPosition.y + cameraLockEntitySizeComponent.boundingRectangle.height * 0.5f,
        cameraLockEntityRevelationComponent.revelationCircle.radius, 64);
    }

    shapeRenderer.end();
}

@Override
public void dispose() {
    Gdx.app.debug(TAG, "Disposing Gamerenderer");
    super.dispose();
    if (shapeRenderer != null) {
        shapeRenderer.dispose();
    }
    if (frameBuffer != null) {
        frameBuffer.dispose();
    }
}
}