【问题标题】:problems with multiple cameras and viewports in libgdxlibgdx 中多个相机和视口的问题
【发布时间】:2017-07-18 00:25:47
【问题描述】:

所以我正在做一个简单的游戏,但我认为我可能会使事情复杂化。我有一个用于 GameScreen 的摄像头和一个跟随玩家位置的视口,当它到达两侧的点时,摄像头会停止跟随玩家并停留在一个点上。

这本身就可以正常工作,但后来我想在游戏中添加暂停菜单和其他一些功能,创建一个带有自己的相机和视口以及舞台和 shapeRenderer 的 hud 类。

问题来了,当我在我的游戏屏幕中创建这个 hud 的实例时,我在玩游戏时正在看的相机看起来像是 hudCam,它不会跟随玩家并且基本上不会让我看到玩家它到达屏幕的边缘。

这是我的 GameScreen 类:

public class GameScreen implements Screen {

    WowInvasion game;
    ScrollingBackground background;
    private OrthographicCamera gameCam;
    private Viewport gameViewPort;

    /*
    Basically I wanna keep the same sprites running while in the menu, playing and till dead

    therefore, I'll have a switch statement with cases on where the games at, inside the functions needed. That way I'll keep
    the game has a background for the menu and there's no need for running a second screen.
     */
    public static final int MAIN_MENU = 0;
    public static final int GAME = 1;
    private static int state = 1; //current state. starts with MAIN_MENU //DEBUGGING GAME SCREEN

    //STAGES
    private GameStage gameStage; //game ui
    private menuStage mainMenu; //Main menu of the game
    private Hud hud;

    //Resources
    private TextureAtlas atlas; //for the textures most
    private Skin skin; //for the styles and fonts

    //Sprites
    private Player player;

    //Shapes
    private float progressPower; //for the power to build up
    private final float POWER_CHARGED = 1000; //limit to get power
    private final float DECREASING_POWER = 20; //limit to get power

    public GameScreen(WowInvasion game){
        this.game = game;
        gameCam = new OrthographicCamera();
        gameCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
        gameViewPort = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, gameCam);
        progressPower = 0f;
        game.wowAssetManager.loadTexturesGameScreen(); // tells our asset manger that we want to load the images set in loadImages method
        game.wowAssetManager.loadSkins(); //load the needed skins
        game.wowAssetManager.manager.finishLoading(); // tells the asset manager to load the images and wait until finsihed loading.
        skin = game.wowAssetManager.manager.get("ui/menuSkin.json");
    }

    @Override
    public void show() {
        game.batch.setProjectionMatrix(gameCam.combined);
        background = new ScrollingBackground();
        atlas = game.wowAssetManager.manager.get(WowAssetManager.GAME_ATLAS); //declaring atlas
        mainMenu = new menuStage(gameViewPort, game.batch, skin, game); //pass it so that we only use one batch and one same viewport
        gameStage = new GameStage(gameViewPort, game.batch, skin, game);
        hud = new Hud(game.batch, skin);
        player = new Player(atlas.findRegion("player"), (int) gameCam.viewportWidth / 2, (int) gameCam.viewportHeight / 2);

        switch(state){
            case MAIN_MENU:
                Gdx.input.setInputProcessor(mainMenu);
                break;
            case GAME:
                background.setFixedSpeed(false); //does not work in here
        }
    }

    @Override
    public void render(float delta) {
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        if(state ==  GAME) {
            background.setFixedSpeed(false);
            player.update(delta, gameCam.viewportWidth, gameCam.viewportHeight); //updating player for movement
           //really cheap way to charge power with velocity
            if(progressPower != POWER_CHARGED) {
                progressPower += Math.abs(player.getVelocity().x) + Math.abs(player.getVelocity().y);
                progressPower -= DECREASING_POWER;
            }
            else
                progressPower = POWER_CHARGED / 4;
        }


        mainMenu.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f)); //updating while making sure delta won't be more than 1/30f.
        gameStage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
        game.batch.begin();
        background.updateAndRender(delta, game.batch); //updating scrolling background
        player.draw(game.batch);
        game.batch.end();
        mainMenu.draw(); //draw the menu stage
        gameStage.draw(); //draw the ui stage for the game
        hud.getStage().draw();
        hud.renderRotateMeter();

        updateCamera(0, WowInvasion.WIDTH);
        System.out.println(player.getPosition().x);
    }

    public void updateCamera(float startX, float endX){
        Vector3 position = gameCam.position;
        //linear interpolation : a + (b - a) * lerp
        //b = player position
        //a = current camera position
        //lerp = interpolation factor
            position.x = gameCam.position.x + (player.getPosition().x - gameCam.position.x) * .1f;

        //making the camera stay when the player gets to close to the sides
        if(position.x < startX) {
            position.x = startX;
        }

        if(position.x > endX){
            position.x = endX;
        }

        gameCam.position.set(position);
        gameCam.update();
    }

    @Override
    public void resize(int width, int height) {
       gameViewPort.update(width, height);
        //hud.getViewport().update(width, height);
    }

    @Override
    public void pause() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void hide() {
        dispose();
    }

    @Override
    public void dispose() {
        mainMenu.dispose();
        gameStage.dispose();
        game.dispose();
        hud.dispose();
    }

    public static void setState(int state) {
        GameScreen.state = state;
    }

}

这是我的HUD:

public class Hud implements Disposable{

    private Stage stage;
    private Viewport viewport;
    Button buttonPause, buttonResume;
    private OrthographicCamera hudCam;
    private ShapeRenderer sp; //like a batch for shapes

    public Hud(SpriteBatch sb, Skin skin){
        hudCam = new OrthographicCamera();
        hudCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
        viewport = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, hudCam);
        stage = new Stage(viewport, sb);
        sp = new ShapeRenderer();

        Table table = new Table();
        table.top();
        //this makes the table the size of the stage
        table.setFillParent(true);

        buttonPause = new Button(skin, "pause");
        buttonPause.setTransform(true);
        buttonPause.addListener(new ClickListener(){ //listener to handle event
            @Override
            public void clicked(InputEvent event, float x, float y) {

            }
        });

        buttonResume = new Button(skin, "resume");
        buttonResume.setTransform(true);
        buttonResume.setScale(0.5f);
        buttonResume.addListener(new ClickListener(){
            @Override
            public void clicked(InputEvent event, float x, float y) {
                buttonResume.setVisible(false);

            }
        });

        table.add(buttonPause);
        table.row();
        table.add(buttonResume);

        stage.addActor(table);
    }

    public void renderRotateMeter(){
        sp.setProjectionMatrix(hudCam.combined);
        sp.begin(ShapeRenderer.ShapeType.Filled);
        sp.setColor(Color.YELLOW);
        sp.rect(hudCam.position.x,hudCam.position.y, WowInvasion.WIDTH / 2, 20);
        sp.end();
    }

    public Viewport getViewport() {
        return viewport;
    }

    public Stage getStage() {
        return stage;
    }

    @Override
    public void dispose() {
        stage.dispose();
        sp.dispose();
    }
}

提前致谢!

编辑

所以我尝试将 gameCam 的参数传递给 hud,而不是制作一个新的 OrthographicCamera,我使用的是一个也有 hudCamara 的,玩家的运动是完美的,除了现在来自 Hud 的薄动不动就动。

【问题讨论】:

    标签: java android camera libgdx


    【解决方案1】:

    您似乎只将 projectionMatrix 设置为仅 HUD 摄像头,如中所示

    sp.setProjectionMatrix(hudCam.combined);
    

    在绘制调用之前尝试将其设置为与 HUD 类之外的其他内容相同。

    要记住的另一件事是,当您在游戏中使用多个 ViewportCamera 时,大多数情况下它将是 1 个 Viewport 与 1 个 Camera 匹配并与另一组一起使用就像你的情况一样。在绘图调用中,您还需要调用Viewport 类的apply()apply(true) 来告诉系统您将根据哪个视口进行绘制,因此它将遵循视口附加相机设置的屏幕坐标.

    因此假设您有 2 个对象需要在不同的视口中连续调用,请按照以下代码进行操作。根据 libgdx API,方法调用是正确的,但变量名是虚构的。

    // draw objA adhering to viewportA (thus cameraA) <-- assume it's player cam
    sb.setProjectionMatrix(cameraA.combined);
    viewportA.apply();
    objA.draw();
    
    // draw objB adhering to viewportB (thus cameraB) <-- assume it's HUD cam
    sb.setProjectionMatrix(cameraB.combined);
    viewportB.apply(true);  // send in true as for HUD, we always want to center the screen
    objB.draw();
    

    总之,在连续绘制调用中绘制使用多个相机和视口的对象时要记住两件事。

    1. 将投影矩阵设置为SpriteBatchShapeRenderer
    2. 调用Viewport 类的apply()apply(true),让它知道您使用此视口。

    【讨论】:

    • 非常感谢这个答案。我发现我必须为两者设置ProyectionMatrix,但你确实帮助清楚地看到它以供进一步使用。谢谢!
    • 真的很高兴它有帮助!干杯。
    • 嘿..对不起,但我试图创建与我的游戏屏幕分开的菜单(它们以前是一起的),我在其中创建了一个新相机和一个视口。我执行与游戏屏幕中完全相同的代码,但是当我点击播放更改为我的播放屏幕时……相机位于底部的一半!我尝试手动更改位置但不起作用。唯一有效的是将“gameviewPort.apply(true)”添加到我的游戏屏幕,但是相机根本不跟随玩家……apply(true) 在做什么?因为没有布尔值甚至不起作用
    • 嘿,true 会将附加到该视口的相机居中。这里的问题是游戏和他的屏幕使用相同的视口。所以应用一个会影响两个。我建议始终为游戏和 hud 屏幕创建至少 2 个视口(因此是 2 个摄像机)。然后在整个游戏生命周期中使用它。无论如何,如果游戏的相机不跟随玩家,仅使用 hud 或游戏视口但使用 apply(true) 也是可行的。
    • 此外,这种针对多个视口和相机的设置不仅适用于相机跟随播放器,而且当我们使用不同类型的视口以不同方式在屏幕上渲染事物时,即益智游戏,游戏内容始终位于屏幕中心(使用 FitViewport),但它的背景需要完全渲染(使用 ExtendViewport)。当您通过 apply() 切换视口但在切换到游戏内容时未提供 true 时,这些东西将始终被绘制为左对齐而不是居中。希望它足够清楚。
    猜你喜欢
    • 1970-01-01
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-04
    • 2015-07-24
    • 1970-01-01
    相关资源
    最近更新 更多