Як боротися з різними співвідношеннями сторін у 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()текстуру, зміна розміру екрана змістила б сенсорний прослуховувач на області текстури на кілька пікселів вправо або вліво, залежно від зміни розміру, і це було явно небажано.

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

Чи може хтось із членів спільноти допомогти мені вирішити цю проблему?


Не могли б ви зробити посилання на блог чи форум, у яких була та сама проблема?
Стів Блеквелл

@SteveBlackwell ось посилання: java-gaming.org/index.php?topic=25685.new
Рафай

@SteveBlackwell Будь ласка, перегляньте оновлене запитання та подивіться, чи можете ви допомогти у цьому.
Рафай

1
Я не дуже багато знаю про Сцену, але, дивлячись сюди , здається, це слід було виправити. Ці документи не надто багато допомоги для центрування. Можливо, ви могли б трохи перемістити камеру або відрегулювати область перегляду.
Стів Блеквелл

1
Ну, моє питання насправді вирішено. Я публікую це як відповідь.
Рафай,

Відповіді:


51

Як це зробити сьогодні:

Оскільки це одне з найвідоміших питань щодо libgdx тут, я дам йому невелике оновлення :

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

Все, що вам потрібно знати про це, можна знайти тут .


Я спробував це, але все ще мої TextureRegionStreches. Якщо залишити reszie(int width, int height)функцію порожньою. Він TextureRegionне розтягується.
Мухаммед Бабар

Але навіть при використанні Viewports нам доводиться використовувати окремі текстури для різних пропорцій, чи не так? Чи існує логіка для спрощення цього методу? Або графічне проектування слід робити, тримаючи його незалежним від резолюцій?
WeirdElfB0,

Хороший підручник з використання Viewports: gamefromscratch.com/post/2014/12/09/…
ubzack

Додавши до цієї відповіді та відповідаючи на запитання у коментарях, ви можете намалювати свій світ за співвідношенням 16: 9 і надати більшої висоти своїй текстурі, щоб мати змогу завантажуватись у 4: 3. Потім, створюючи екземпляр вашого вікна перегляду, ви можете перевірити фактичне співвідношення сторін екрана, і, зберігаючи стандартну ширину, ви можете надати вашому огляду висоту, яка відповідатиме співвідношенню сторінок, яке ви використовуєте. Недоліком є ​​те, що у вас буде багато «порожнього» місця з роздільною здатністю 4: 3, але якщо ваша текстура це впорається, все буде
виглядати

24

EDIT: libGDX розвинувся. Найкраща відповідь зараз - це ніхто користувачем. Обов’язково перевірте посилання на Viewportдокументацію. .

Коли SteveBlack розмістив посилання на випуск, про який повідомляли на сценічному класі, я пішов туди просто, щоб виявити, що проблема (яка насправді не була моєю проблемою) була вирішена в останніх щоночниках.

Дослідивши тут і там в Інтернеті проблему, з якою я стикався, я не міг знайти рішень, тому вирішив зв’язатися з людиною, яка повідомила про помилку безпосередньо. Після цього він відповів мені на форумах 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 параметрами зараз недоступні!
Мухаммед Бабар

Я змінив прийняту відповідь на відповідь, опубліковану користувачем, як зазначив Стів Блеквелл, оскільки вона видається останньою та правильною.
Рафай,

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 це те, що я робив, як і рік тому, добре, я дізнався про те, що непроектується .. добре, коли ваша камера завжди в однаковому положенні ..
Boldijar Paul,

0

Цей код працює для мене з останнім оновленням: * OrthographicCamera cam, це не робить обрізання, просто змінює область перегляду, тому ширина все ще "в багато" разів більша за фактичне вікно / пристрій

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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.