libGDX. Урок 18. Реализация экрана Меню.

Реализация экрана Меню

В этом уроке мы создадим экран меню, с которого будет начинаться наши игра при запуске. Для этого необходимо: две кнопки Start и Exit и задний фон. По нажатии кнопки Start пользователь будет перенаправлен на экран игры. По нажатии кнопки Exit приложение будет завершать работу.

Для того, чтобы реализовать экран меню нужно создать новый класс MenuScreen в пакете com.mygdx.basketball:

создаем новый класс в android studio

Введите следующий код в этот класс:

package com.mygdx.basketball;

import com.badlogic.gdx.Screen;


public class MenuScreen implements Screen {
    @Override
    public void show() {

    }
    @Override
    public void render(float delta) {

    }
    @Override
    public void resize(int width, int height) {

    }
    @Override
    public void pause() {

    }
    @Override
    public void resume() {

    }
    @Override
    public void hide() {

    }
    @Override
    public void dispose() {

    }

}

Чтобы реализовать различные экраны, в libGDX существует интерфейс Screen. А так как это интерфейс, то нам самим придется реализовать его методы. Все эти методы аналогичны методам интерфейса ApplicationListener, которым мы пользовались ранее. Но особое внимание уделим методам show() и hide(). Эти методы вызываются, когда экран отображается/активен (show()) или скрыт/ не активен (hide()). Теперь объявим в этом классе необходимые нам переменные:

public class MenuScreen implements Screen {

    SpriteBatch batch; // объект для отрисовки спрайтов нашей игры
    OrthographicCamera camera; // область просмотра нашей игры

    Texture startButtonTexture;
    Texture exitButtonTexture;
    Texture backGroundTexture;
    Sprite startButtonSprite;
    Sprite exitButtonSprite;
    Sprite backGroundSprite;

    private static float BUTTON_RESIZE_FACTOR = 800f; // задаём относительный размер
    private static float START_VERT_POSITION_FACTOR = 2.7f; // задаём позицию конпки start
    private static float EXIT_VERT_POSITION_FACTOR = 4.2f; // задаём позицию кнопки exit

    @Override
    public void show() {

Мы объявили текстуры и спрайты для заднего фона и кнопок. Так как в нашем интерфейсе отсутствует метод create(), то инициализацию переменных проведём в конструкторе. Для начала инициализируем объекты camera и batch:

private static float EXIT_VERT_POSITION_FACTOR = 4.2f; // задаём позицию кнопки exit

public MenuScreen(){

    // получаем размеры экрана устройства пользователя и записываем их в переменнные высоты и ширины
    float height= Gdx.graphics.getHeight();
    float width = Gdx.graphics.getWidth();
    // устанавливаем переменные высоты и ширины в качестве области просмотра нашей игры
    camera = new OrthographicCamera(width,height);
    // этим методом мы центруем камеру на половину высоты и половину ширины
    camera.setToOrtho(false);
    batch = new SpriteBatch();

}

@Override
public void show() {

Далее инициализируем текстуры и спрайты в этом же конструкторе:

    batch = new SpriteBatch();

    // инициализируем текстуры и спрайты
    startButtonTexture = new Texture(Gdx.files.internal("start_button.png"));
    exitButtonTexture = new Texture(Gdx.files.internal("exit_button.png"));
    backGroundTexture = new Texture(Gdx.files.internal("menubackground.jpg"));
    startButtonSprite = new Sprite(startButtonTexture);
    exitButtonSprite = new Sprite(exitButtonTexture);
    backGroundSprite = new Sprite(backGroundTexture);
    // устанавливаем размер и позиции
    startButtonSprite.setSize(startButtonSprite.getWidth() *(width/BUTTON_RESIZE_FACTOR), startButtonSprite.getHeight()*(width/BUTTON_RESIZE_FACTOR));
    exitButtonSprite.setSize(exitButtonSprite.getWidth() *(width/BUTTON_RESIZE_FACTOR), exitButtonSprite.getHeight()*(width/BUTTON_RESIZE_FACTOR));
    backGroundSprite.setSize(width,height);
    startButtonSprite.setPosition((width/2f -startButtonSprite.getWidth()/2) , width/START_VERT_POSITION_FACTOR);
    exitButtonSprite.setPosition((width/2f -exitButtonSprite.getWidth()/2) , width/EXIT_VERT_POSITION_FACTOR);
    // устанавливаем прозрачность заднего фона
    backGroundSprite.setAlpha(0.2f);

    }

@Override
public void show() {

В классе Sprite существует метод setAlpha(), в котором можно осуществить установку прозрачности спрайта. Значения приходятся на диапазон от 0 до 1. Значение 0 делает спрайт полностью прозрачным, а значение 1 делает спрайт полностью непрозрачным. Теперь нужно отрисовать все инициализированные объекты в методе render():

@Override
public void render(float delta) {

    // Очищаем экран
    Gdx.gl.glClearColor(1, 1, 1, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    batch.setProjectionMatrix(camera.combined);// устанавливаем в экземпляр spritebatch вид с камеры (области просмотра)

    //отрисовка игровых объектов
    batch.begin();
    backGroundSprite.draw(batch);
    startButtonSprite.draw(batch);
    exitButtonSprite.draw(batch);
    batch.end();

}

И наконец не забываем про очистку памяти. Добавьте эти строки в метод dispose():

@Override
public void dispose() {

    startButtonTexture.dispose();
    exitButtonTexture.dispose();
    batch.dispose();

}

Реализуем переход/переключение между экранами

Мы с вами создали экран меню, но не создали возможность его отображения и возможность перехода между экранами. На данный момент мы не можем вызвать созданный нами только что класс MenuScreen в классе AndroidLauncher (который в свою очередь служит запуском нашего приложения):

пример ошибки в android studio libgdx

Для этого нам потребуется создать новый класс, который будет наследовать (extend) класс Game из API LibGDX. Создайте класс MainGame в пакете com.mygdx.basketball и введите в него следующий код:

package com.mygdx.basketball;


import com.badlogic.gdx.Game;


public class MainGame extends Game {

    @Override
    public void create() {
        setScreen(new MenuScreen());
    }

}

Как видно, в методе create() мы вызываем метод setScreen(), чтобы изменить отображаемый в данный момент экран на экран из класса MenuScreen, передав в качестве аргумента его экземпляр.

И вот теперь вы можете в классе AndroidLauncher поменять аргумент в методе initialize():

public class AndroidLauncher extends AndroidApplication {
    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
        initialize(new MainGame()BasketBall(), config);
    }
}

переключение экранов в libgdx

Если запустить приложение сейчас, то вы увидите перед собой наш экран меню:

переключение экранов в libgdx android studio

Теперь приступим к реализации переходов/переключений между экранами. Для начала нам необходимо отредактировать класс BasketBall так, чтобы у нас появилась возможность вызвать его с экрана меню. Вместо наследования (extend) ApplicationAdapter нужно унаследовать интерфейс (implements) Screen:

public class BasketBall implements Screen {

Теперь добавьте недостающие методы, используя помощь Android Studio

импортирование библиотек в android studio

После чего удалите все вызовы super в реализованных методах, метод create() сделайте конструктором. Также придется сделать редакцию в сигнатуре (объявлении) метода render():

public void render (float delta) {

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

package com.mygdx.basketball;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.mygdx.basketball.managers.GameManager;
import com.mygdx.basketball.managers.InputManager;

public class BasketBall implements Screen {

    SpriteBatch batch; // объект для отрисовки спрайтов нашей игры
    OrthographicCamera camera; // область просмотра нашей игры

    public BasketBall () {

        // получаем размеры экрана устройства пользователя и записываем их в переменнные высоты и ширины
        float height = Gdx.graphics.getHeight();
        float width = Gdx.graphics.getWidth();
        camera = new OrthographicCamera(width,height);// устанавливаем переменные высоты и ширины в качестве области просмотра нашей игры
        camera.setToOrtho(false);// этим методом мы центруем камеру на половину высоты и половину ширины
        batch = new SpriteBatch();
        //вызываем метод initialize класса GameManager
        GameManager.initialize(width, height);
        Gdx.input.setInputProcessor(new InputManager(camera));// доступ класса InputManager для получения касаний/нажатий

    }

    @Override
    public void show() {

    }

    @Override
    public void resize(int width, int height) {

    }

    @Override
    public void pause() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void hide() {

    }

    @Override
    public void dispose() {

        batch.dispose();
        GameManager.dispose();
    }

    @Override
    public void render (float delta) {

        Gdx.gl.glClearColor(1, 1, 1, 1);// Очищаем экран
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        batch.setProjectionMatrix(camera.combined); // устанавливаем в экземпляр spritebatch вид с камеры (области просмотра)
        //отрисовка игровых объектов
        batch.begin();
        GameManager.renderGame(batch);
        batch.end();
    }

}

Теперь в классе MenuScreen объявим экземпляр класса MainGame. Это нам понадобится для того, чтобы у нас появилась возможность вызывать необходимый нам метод setScreen() для переходов/переключений между экранами. Также добавим параметр в конструктор класса MenuScreen, чтобы устанавливать необходимый нам экземпляр класса MainGame:

private static float EXIT_VERT_POSITION_FACTOR = 4.2f; // задаём позицию кнопки exit

MainGame game; // экземпляр класса MainGame нужен для доступа к вызову метода setScreen

public MenuScreen(MainGame game){

    this.game = game;

    // получаем размеры экрана устройства пользователя и записываем их в переменные высоты и ширины

Дополнительно нужно модифицировать строку в методе create() класса MainGame, где мы устанавливаем вызов нужного нам класса:

@Override
public void create() {
    setScreen(new MenuScreen(this));
}

Далее обработаем касания по кнопкам. Объявим временный вектор в классе MenuScreen для хранения входных координат:

MainGame game; // экземпляр класса MainGame нужен для доступа к вызову метода setScreen

Vector3 temp = new Vector3(); // временный вектор для "захвата" входных координат

public MenuScreen(MainGame game){

Теперь добавим метод handleTouch() в класс MenuScreen, в котором будем обрабатывать касания по экрану меню:

void handleTouch(){
    // Проверяем были ли касание по экрану?
    if(Gdx.input.justTouched()) {
        // Получаем координаты касания и устанавливаем эти значения в временный вектор
        temp.set(Gdx.input.getX(),Gdx.input.getY(), 0);
        // получаем координаты касания относительно области просмотра нашей камеры
        camera.unproject(temp);
        float touchX = temp.x;
        float touchY= temp.y;
        // обработка касания по кнопке Stare
        if((touchX>=startButtonSprite.getX()) && touchX<= (startButtonSprite.getX()+startButtonSprite.getWidth()) && (touchY>=startButtonSprite.getY()) && touchY<=(startButtonSprite.getY()+startButtonSprite.getHeight()) ){
            game.setScreen(new BasketBall()); // Переход к экрану игры
        }
        // обработка касания по кнопке Exit
        else if((touchX>=exitButtonSprite.getX()) && touchX<= (exitButtonSprite.getX()+exitButtonSprite.getWidth()) && (touchY>=exitButtonSprite.getY()) && touchY<=(exitButtonSprite.getY()+exitButtonSprite.getHeight()) ){
            Gdx.app.exit(); // выход из приложения
        }
    }
}

В этом методе, после того, как было произведено касание по экрану, мы проверяем сначала было ли это касание по кнопке Start. Если пользователь коснулся кнопки Start, то методом setScreen() мы переносим его на экран игры. Если же пользователь коснулся кнопки Exit, то будет выполнен выход из приложения. Вызов метода handleTouch произведём в методе render():

exitButtonSprite.draw(batch);
handleTouch();
batch.end();

Метод dispose(), служащий нам для освобождения ресурсов, вызовем в методе hide(), так как он не вызывается фреймворком автоматически. Когда мы переключаемся с одного экрана на другой, метод hide() вызывается для первого экрана:

@Override
public void hide() {
    dispose();
}
переключение экранов в libgdx

Код к этому уроку можно скачать ниже

Скачать исходники