Разделяем игру на экраны
В этой короткой статье мы немного изменим примитивную одноэкранную игру из предыдущей статьи, добавив экран, который впоследствии станет главным меню нашей игрушки.
На данный момент у нас в подпроекте "core" всего один класс Drop:
public class Drop implements ApplicationListener { public void create() {...} public void render() {...} public void resize(int width, int height) {} public void pause() {} public void resume() {} public void dispose() {...} }
Он расширен засчет интерфейса ApplicationListener, т.е. представляет из себя класс, который первым делом вызывается при запуске игры (из класса-лаунчера).
Для создания многоэкранных приложений в LibGDX существует класс Game. Это наследник ApplicationListener, в него добавлены всего лишь 2 метода:
public void setScreen (Screen screen) public Screen getScreen ()
Как вы можете догадаться, они предназначены для работы с игровыми экранами.
Что же такое Screen? По факту это тот же самый ApplicationListener, но вызывается не классом-лаунчером, а классом-игрой. Класс Game позволяет переключаться с экрана на экран, благодаря чему можно сделать меню для игры, сплэш скрин, различные внутриигровые окна типа карты, окон торговли, разговора, миниигр.
Поскольку окон, в отличие от самой игры, может быть несколько и их можно создать одновременно, то метод create() заменён обычным конструктором класса. Второе отличие Screen от ApplicationListener - метод render(float delata). Класс Game вызывает метод render с аргументом времени, прошедшим с момента последнего вызова этого же метода. Т.е. окна всегда знают, когда обновлялись в предыдущий раз. И последнее отличие - наличие двух вспомогательных методов для работы экранов.
Создаём экран с игрой
Так как у нас уже есть готовая игра, то проще будет начать с игрового экрана, а потом уже добавить меню. Для этого нам необходимо скопировать класс Drop и назвать копию GameScreen. Класс Drop мы полностью изменим и превратим в обработчик экранов, а класс GameScreen подправим так, чтобы он стал игровым экраном. Последним сейчас и займёмся.
Для этого мы меняем класс-расширитель с ApplicationListener на Screen:
public class GameScreen implements ApplicationListener public class GameScreen implements Screen
И сразу же получаем 3 ошибки. Начнём исправлять с самого простого - добавим методу render() аргумент:
public void render(float delta) {
Теперь изменим create() на конструктор.
@Override public void GameScreen() {
Меняем на
public GameScreen(final Drop game) {
В качестве аргумента экран получит ссылку на обработчик экранов. Чтобы её не потерять, нужно создать переменную в блоке переменных
private Drop game;
Ну и конечно же необходимо присвоить полученное значение:
public GameScreen(final Drop game) { this.game = game; ... }
Для работоспособности экрана осталось лишь добавить отсутствующие методы
@Override public void show() { // TODO Auto-generated method stub } @Override public void hide() { // TODO Auto-generated method stub }
Игровое окно готово, чуть позже мы его немножко оптимизируем, а сейчас займемся обработчиком окон.
Класс Drop
Первым делом меняем интерфейс ApplicationListener на класс-предок Game
public class Drop implements ApplicationListener { public class Drop extends Game {
Поскольку теперь это не класс с игрой, а всего лишь связующее звено для экранов, то мы можем смело удалить весь игровой код и заменить этим:
package com.badlogic.drop; import com.badlogic.gdx.Game; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; public class Drop extends Game { public SpriteBatch batch; public BitmapFont font; public void create() { batch = new SpriteBatch(); font = new BitmapFont(); this.setScreen(new GameScreen(this)); } public void render() { super.render(); // Архиважно! Иначе ничего не будет рисоваться. } public void dispose() { batch.dispose(); font.dispose(); } }
Хорошим тоном в ООП считается реюзабельность объектов, в частности, в нашей игре мы будем использовать один и тот же батч для всех игровых экранов. Поэтому мы создаём публичные переменные
public SpriteBatch batch; public BitmapFont font;
Для того, чтобы к ним могли все обращаться через вызов класса Drop из переменной game.
game.batch.draw(...);
Аналогично со шрифтом. При использовании пустого конструктора создаётся стандартный Arial шрифт.
В методе create() батч и фонт инициализируются, а также запускается акивный экран - тот экран, который игрок увидит первым делом.
Метод render() запускает отрисовку активного игрового экрана. Если вы перфекционист и вам не нравятся лишние процедурные вызовы, можете заменить вызов метода-предка на строку
if (screen != null) screen.render(Gdx.graphics.getDeltaTime());
Метод dispose(), разумеется, очищает глобальные переменные.
Зачаток главного меню
Сейчас мы не будем делать меню как таковое, привычное для нас. Оно будет без каких-либо кнопок, в нём будет лишь одна пояснительная строчка о том, что нужно тыкнуть мышью(пальцем) в любое место на экране, чтобы начать игру. Делать насстоящее меню мы будем в следующей статье.
Создайте третий класс, назовите его MainMenuScreen и расширьте интерфейсом Screen.
Создайте ему конструктор с аргументом Drop объявите соответствующую переменную:
public class MainMenuScreen implements Screen { private Drop game; public MainMenuScreen(final Drop game){ this.game = game; } ... }
А теперь в методе render(float delta) выведем строчку текста.
public void render(float delta) { Gdx.gl.glClearColor(0, 0, 0.2f, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); game.batch.begin(); game.font.draw(game.batch, "Tap anywhere to begin!", 100, 100); game.batch.end(); if (Gdx.input.isTouched()) { game.setScreen(new GameScreen(game)); dispose(); } }
Сперва при помощи модуля gl очищаем экран. Затем испольуем глобальные батч и фонт для рисования строки. И в конце мы ловим клик и переключаемся на игровой экран.
Чтобы "включить" наше меню, необходимо исправить обработчик экранов. Помните, что там устанавливается сразу игровой экран? Заменяем игровой экран на экран меню.
this.setScreen(new GameScreen(this)); this.setScreen(new MainMenuScreen(this));
Теперь перед началом игры мы видим синий экран с белой надписью. Кликаем мышкой - и начинаем игру. Но это ещё не всё, нам нужно немножко оптимизировать GameScreen.
Исправляем GameScreen
В данный момент GameScreen использует свой собственный батч для прорисовки. Нам нужно убрать его.
Вернее, заменить глобальным, который хранится в обработчике экранов. Это значит, что нужно удалить переменную,
private SpriteBatch batch;
её инициализацию
// и батча batch = new SpriteBatch();
и её очищение.
batch.dispose();
И заменить все вызовы на вызовы глобальной переменной
batch.end(); game.batch.end();
Разумеется, батч и фонт - не единственное, что стоит вынести из игрового экрана. Например, текстуры и звуки - ведь их можно использовать в других экранах. Запустить фоновую музыку в главном меню, пустить там же фоном капли и т.д.. Но это в качестве домашнего задания.
Также в качестве самостоятельной работы вы можете добавить счетчик в GameScreen, учитывать каждую пойманную каплю и выводить счёт на экран.
Смотрите также:
Комментарии
Здесь еще никто не оставил комментарий
CollectableItemData.cs
[CreateMenuItem(fileName = "newItem", menuName = "Data/Items/Collectable", order = 51]