Как бороться с разными соотношениями сторон в libGDX?

81

Я реализовал несколько экранов с использованием libGDX, которые, очевидно, будут использовать Screenкласс, предоставляемый фреймворком libGDX. Однако реализация для этих экранов работает только с предварительно определенными размерами экрана. Например, если спрайт был предназначен для экрана размером 640 x 480 (соотношение сторон 4: 3), он не будет работать должным образом на других размерах экрана, потому что спрайты выходят за границы экрана и не масштабируются до размера экрана. вообще. Более того, если бы libGDX обеспечивал простое масштабирование, проблема, с которой я столкнулся, все еще существовала бы, потому что это привело бы к изменению соотношения сторон экрана игры.

После исследования в Интернете я наткнулся на блог / форум, в котором обсуждалась та же проблема. Я реализовал его, и пока он работает нормально. Но я хочу подтвердить, является ли это лучшим вариантом для достижения этой цели или есть альтернативы получше. Ниже приведен код, показывающий, как я справляюсь с этой законной проблемой.

ССЫЛКА НА ФОРУМ: http://www.java-gaming.org/index.php?topic=25685.new

public class SplashScreen implements Screen {

    // Aspect Ratio maintenance
    private static final int VIRTUAL_WIDTH = 640;
    private static final int VIRTUAL_HEIGHT = 480;
    private static final float ASPECT_RATIO = (float) VIRTUAL_WIDTH / (float) VIRTUAL_HEIGHT;

    private Camera camera;
    private Rectangle viewport;
    // ------end------

    MainGame TempMainGame;

    public Texture splashScreen;
    public TextureRegion splashScreenRegion;
    public SpriteBatch splashScreenSprite;

    public SplashScreen(MainGame maingame) {
        TempMainGame = maingame;
    }

    @Override
    public void dispose() {
        splashScreenSprite.dispose();
        splashScreen.dispose();
    }

    @Override
    public void render(float arg0) {
        //----Aspect Ratio maintenance

        // update camera
        camera.update();
        camera.apply(Gdx.gl10);

        // set viewport
        Gdx.gl.glViewport((int) viewport.x, (int) viewport.y,
        (int) viewport.width, (int) viewport.height);

        // clear previous frame
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        // DRAW EVERYTHING
        //--maintenance end--

        splashScreenSprite.begin();
        splashScreenSprite.disableBlending();
        splashScreenSprite.draw(splashScreenRegion, 0, 0);
        splashScreenSprite.end();
    }

    @Override
    public void resize(int width, int height) {
        //--Aspect Ratio Maintenance--
        // calculate new viewport
        float aspectRatio = (float)width/(float)height;
        float scale = 1f;
        Vector2 crop = new Vector2(0f, 0f);

        if(aspectRatio > ASPECT_RATIO) {
            scale = (float) height / (float) VIRTUAL_HEIGHT;
            crop.x = (width - VIRTUAL_WIDTH * scale) / 2f;
        } else if(aspectRatio < ASPECT_RATIO) {
            scale = (float) width / (float) VIRTUAL_WIDTH;
            crop.y = (height - VIRTUAL_HEIGHT * scale) / 2f;
        } else {
            scale = (float) width / (float) VIRTUAL_WIDTH;
        }

        float w = (float) VIRTUAL_WIDTH * scale;
        float h = (float) VIRTUAL_HEIGHT * scale;
        viewport = new Rectangle(crop.x, crop.y, w, h);
        //Maintenance ends here--
    }

    @Override
    public void show() {
        camera = new OrthographicCamera(VIRTUAL_WIDTH, VIRTUAL_HEIGHT); //Aspect Ratio Maintenance

        splashScreen = new Texture(Gdx.files.internal("images/splashScreen.png"));
        splashScreenRegion = new TextureRegion(splashScreen, 0, 0, 640, 480);
        splashScreenSprite = new SpriteBatch();

        if(Assets.load()) {
            this.dispose();
            TempMainGame.setScreen(TempMainGame.mainmenu);
        }
    }
}

ОБНОВЛЕНИЕ: недавно я узнал, что libGDX имеет некоторые собственные функции для поддержания соотношения сторон, которые я хотел бы здесь обсудить. При поиске проблемы с соотношением сторон в Интернете я наткнулся на несколько форумов / разработчиков, у которых была проблема «Как сохранить соотношение сторон на экранах разных размеров?» Одно из решений, которое действительно сработало для меня, было опубликовано выше.

Позже, когда я приступил к реализации touchDown()методов для экрана, я обнаружил, что из-за масштабирования при изменении размера координаты, которые я реализовал, touchDown()будут сильно меняться. После работы с некоторым кодом для преобразования координат в соответствии с изменением размера экрана я значительно уменьшил это количество, но мне не удалось поддерживать их с высокой точностью. Например, если бы я реализовал touchDown()текстуру, изменение размера экрана сместило бы touchListener в области текстуры на несколько пикселей вправо или влево, в зависимости от изменения размера, а это, очевидно, было нежелательно.

Позже я узнал, что у класса stage есть собственная встроенная функция для поддержания соотношения сторон ( boolean stretch = false). Теперь, когда я реализовал свой экран с помощью класса stage, он хорошо поддерживает соотношение сторон. Однако при изменении размера или других размерах экрана создаваемая черная область всегда появляется в правой части экрана; то есть экран не отцентрован, что делает его довольно уродливым, если черная область существенно большая.

Может ли кто-нибудь из членов сообщества помочь мне решить эту проблему?

Рафай
источник
Не могли бы вы дать ссылку на блог или форум, в котором была такая же проблема?
Стив Блэквелл
@SteveBlackwell вот ссылка: java-gaming.org/index.php?topic=25685.new
Рафай,
@SteveBlackwell Просмотрите обновленный вопрос и посмотрите, можете ли вы помочь в этом.
Rafay
1
Я не очень много знаю о сцене, но, глядя сюда , кажется, что это нужно было исправить. Эти документы не слишком много помощи для центрирования. Может быть, вы могли бы немного переместить камеру или настроить область просмотра.
Стив Блэквелл,
1
Что ж, моя проблема действительно решена. Выкладываю как ответ.
Rafay

Ответы:

51

Как это сделать сейчас:

Поскольку это один из самых известных вопросов по libgdx, я дам ему небольшое обновление :

LibGDX v1.0 представлен Viewportдля решения этой проблемы. Его намного проще использовать, а стратегию масштабирования можно подключить, что означает, что одна строка может изменить поведение, и вы можете поиграть с ней и посмотреть, какая из них лучше всего подходит для вашей игры.

Все, что вам нужно знать об этом, можно найти здесь .

ни один. Никто
источник
Я пробовал, но все еще мои TextureRegionрастяжки. Если оставить reszie(int width, int height)функцию пустой. НЕ TextureRegionрастягивается.
Мухаммад Бабар
Но даже при использовании видовых экранов мы должны использовать отдельные текстуры для разных соотношений сторон, не так ли? Есть ли логика для упрощения этого метода? Или графический дизайн должен быть выполнен независимо от разрешения?
WeirdElfB0y
Хорошее руководство по использованию Viewports: gamefromscratch.com/post/2014/12/09/…
ubzack
Добавляя к этому ответу и отвечая на вопросы в комментариях, вы можете нарисовать свой мир для формата 16: 9 и увеличить высоту текстуры, чтобы иметь возможность загружаться в формате 4: 3. Затем, при создании экземпляра окна просмотра, вы можете проверить фактическое соотношение сторон экрана и, сохраняя стандарт ширины, задать высоту окна просмотра, которая будет соответствовать текущему соотношению сторон экрана. Недостатком является то, что у вас будет много «пустого» пространства при разрешении 4: 3, но если ваша текстура может справиться с этим, все будет отображаться как нарисованное, а не растянутое
Клитос Г.
24

РЕДАКТИРОВАТЬ: libGDX эволюционировала. Лучший ответ сейчас - это никто из пользователей. Обязательно проверьте ссылку на Viewportдокументацию. .

Когда SteveBlack разместил ссылку на проблему, о которой сообщалось в классе stage, я пошел туда, чтобы обнаружить, что проблема (которая на самом деле не была моей проблемой) была решена в последних выпусках.

Изучив кое-где в Интернете возникшую у меня проблему, я не смог найти никаких решений, поэтому решил связаться с человеком, который напрямую сообщил об ошибке. После этого он ответил мне на форумах libgdx, и я в долгу перед ним за то, что он мне помог. Ссылка здесь

Это была одна строка кода, и все, что вам нужно было сделать, это:

В методе resize ():

stage.setViewport(640, 480, false);
stage.getCamera().position.set(640/2, 480/2, 0);

Где 640 X 480 - это разрешение вашего TextureRegion, которое описывает предполагаемое соотношение сторон. Если ваш размер TextureRegion был 320 X 240, тогда оба аргумента должны быть изменены на новое разрешение, чтобы добиться цели.

Ссылка на профиль исходного человека, который действительно решил мою проблему

Рафай
источник
6
В libGDX 0.9.6 достаточно stage.setViewport (640, 480, false), так как он уже включает вызов stage.getCamera () .position.set (640/2, 480/2, 0);
Alexis Pautrot
1
setViewportс 3 параметрами сейчас недоступны!
Мухаммад Бабар
Я изменил принятый ответ на ответ, опубликованный пользователем noone, как указал Стив Блэквелл, потому что он кажется последним и правильным.
Rafay
7

Черные полосы слева / справа или сверху / снизу выглядят лучше, чем просто искажение всей сцены под размер экрана. Если вы нацеливаетесь на соотношение сторон, которое находится в середине возможных диапазонов (4: 3, вероятно, нижний предел, 16: 9, вероятно, высокий предел), тогда полосы должны оставаться небольшими для большинства устройств. Это также позволяет вам использовать большую часть экрана даже на больших экранах, и у вас уже есть код этого парня, так что это довольно просто. Было бы еще лучше, если бы это была просто опция, встроенная в libgdx.

Но я считаю, что лучше всего использовать весь экран. Я еще этого не делал, но это будет подход, который я выберу в своем следующем проекте. Идея в том, что если у кого-то экран шире, он должен видеть больше по бокам. Крис Пруэтт рассказывает о том, как это сделать, в одном из своих выступлений ( ссылка на место в разговоре - на самом деле, все это на самом деле довольно хорошо). Идея состоит в том, чтобы масштабировать по высоте, а затем установить достаточно широкое окно просмотра, чтобы соответствовать экрану. OpenGL должен позаботиться об отображении всего остального. То , как он делает это здесь .

Для libgdx, возможно, есть простой способ сделать это с помощью scene2d, перемещая камеру по сцене, но я никогда не работал с scene2d. В любом случае запуск приложения как собственного окна с изменяемым размером - гораздо более простой способ протестировать несколько размеров экрана, чем создание группы AVD.

Стив Блэквелл
источник
«В любом случае, запуск приложения в качестве собственного окна с изменяемым размером - гораздо более простой способ протестировать несколько размеров экрана, чем создание группы AVD». Хорошо сказано. на самом деле я пытаюсь работать над этим явлением, чтобы обеспечить максимальную совместимость, вместо того, чтобы ориентироваться на разные размеры экрана с разными размерами текстур.
Рафай
3

Класс ResolutionFileResolver позволяет разрешать имена файлов с наилучшим разрешением. Я считаю, что это также будет работать с разными пропорциями, если вы создали спрайты для этих соотношений сторон. В AssetManagerTest есть пример использования .

Доран
источник
0

Я использую этот метод из http://blog.acamara.es/2012/02/05/keep-screen-aspect-ratio-with-different-resolutions-using-libgdx/

Я сделал эту функцию, которая возвращает точку на экране, к которой прикоснулся, с уменьшенным масштабом до желаемого игрового положения.

public Vector2 getScaledPos(float x, float y) {
    float yR = viewport.height / (y - viewport.y);
    y = CAMERA_HEIGHT / yR;

    float xR = viewport.width / (x - viewport.x);
    x = CAMERA_WIDTH / xR;

    return new Vector2(x, CAMERA_HEIGHT - y);
}

Используйте это при касании / подъеме / перетаскивании, и вы можете проверить касание в своей игре.

Болдижар Пол
источник
Это похоже на метод непроектирования камеры. Это то же самое?
milosmns
@milosmns, это то, как я делал, как и год назад, хорошо, что я обнаружил непроектную вещь .. хорошо, когда ваша камера всегда в одном и том же положении ..
Болдиджар Пол
0

Этот код работает для меня с последним обновлением: * OrthographicCamera - это камера, это не делает обрезку, просто меняет область просмотра, поэтому ширина все еще "во много" раз больше, чем фактическое окно / устройство

public void resize(int width, int height) {
    int newW = width, newH = height;
    if (cam.viewportWidth > width) {
        float scale = (float) cam.viewportWidth / (float) width;
        newW *= scale;
        newH *= scale;
    }

    // true here to flip the Y-axis
    cam.setToOrtho(true, newW, newH);
}
Милосмнс
источник