
В любой игре присутствует меню. Довольно сложно придумать иной способ навигации. В этой статье рассмотрим как создать свой экран с меню средствами libGDX. Попутно немного о проблеме соотношения сторон на различных устройствах.
Проблема с разрешением экрана
Основная проблема, с которой сталкиваются разработчики игр для платформы Android — неправильное отображение сцены на устройствах с разным соотношением сторон. Если вы посмотрите разрешения на различных устройствах, то обнаружите, что их диапазон довольно большой.
Ладно ещё 720×1280 можно без проблем масштабировать до 360×640. Но простым скалированием не всегда можно решить проблему. Каждый выкручивается по своему. Можно сцену размещать по центру и добавлять фоновые картинки по краям. Или по краю выравнивать, а с другого края фоновое что-то. В моих играх такой проблемы не было, просто видимая часть объектов на различных устройства различалась. На геймплей это не особо сказывалось.
Переключение между экранами
Переключение между экранами в LibGDX довольно простое. Надо вызвать метод setScreen(Screen screen);. То есть в классе игры будут ссылки на все возможные экраны:
public class MyGame extends Game { public GameScreen game; public IntroScreen intro; @Override public void create() { game = new GameScreen(this); intro = new IntroScreen(this); //при запуске игры открываем экран с меню setScreen(intro); }
По сути, создание меню не особо отличается от экрана самой игры. Так же обрабатываем нажатия, рендерим и т.д. Создадим класс IntroScreen, реализовав Screen, InputProcessor. Возьмём за основу решение, которое используется в игре Defender 2. Фон 512×1024 размером. Сама пикча на этом спрайте 480×800. Если экран больше этой картинки, то справа и снизу будет чёрный фон.
Создание меню
Размеры камеры зададим для актуальной картинки фоновой. Всё остальное схоже с игровым экраном.
float CAMERA_WIDTH = 800F; float CAMERA_HEIGHT = 480F;
Необходимо нарисовать сам фон.
public void showBG(){ spriteBatch.draw(bgTexture,0, -32, 1024 , 512); }
Поверх него вывести кнопку.
public void showMenu(){ if(downBtn) spriteBatch.draw(textures.get("cover_button_start_down"),653, 183, 256 , 128); else spriteBatch.draw(textures.get("cover_button_start_up"),653, 183, 256 , 128); }
downBtn показывает нажата ли кнопка или нет. Для красоты анимации, так сказать (: Теперь надо только обработать нажатия.
//обработка нажатия @Override public boolean touchDown(int x, int y, int pointer, int button) { //если нажали на кнопку if((height-y)/ppuY >= 213 && (height-y)/ppuY <= 283 && x/ppuX>=660 && x/ppuX<=780) //то устанвоим флаг, чтобы при рендеринге нарисовать другой спрайт downBtn = true; return true; } //убрали палец с экрана @Override public boolean touchUp(int x, int y, int pointer, int button) { if (!Gdx.app.getType().equals(ApplicationType.Android)) return false; //если до этого нажали на кнопку if(downBtn){ //чистим ресурсы dispose(); //переходим на экран игры game.setScreen(game.game); } downBtn = false; return true; }
В целом работа с меню ничем не отличается от обработки нажатий и отрисовка на игровом экране. Многие меню делают не средствами OpenGL, а не уровне Вьюх. То есть, при запуске игры открывает Activity с лейаутами. С таким меню работать в принципе проще. А уже потом при нажатии на кнопку открывать другое Activity и загружать в нём OpenGL.
Можете скачать исходники урока Libgdxtutorial-lesson8.rar.
Большое спасибо. Давайте дальше выкладывайте уроки. Можно Вам сделать допустим игру как мутировавшие организмы с других планет-демоны планеты Укоа летают вокруг Земли и похищают космических гномов-космонавтов.
А почему для этих целей не использовать Button из scene2d? или на крайний случай не сделать отдельного актера для кнопки? Плюс код из жестким указанием координат кнопки — это вообще нонсенс) Простите, но нельзя этого показывать новичкам, вы показываете им очень плохой пример
В этом примере архитектура без scene2d. В scene2d то да, удобно было б актёров юзать.
В целом да, надо работать с относительными координатами.
В идеале все настройки для кнопок и прочее надо выносить в файлы для удобства.
Этим примером показал просто, что работа с меню в принципе не особо отличается от работы с самой игрой.
Я в этой игре — Deffender II — сейчас на 250 уровне подстрял)))))
ЗЫ какой клевый мистический шаблон у блога;)
Я тож где-то в районе 200-300 остановился. Слишком однообразная игра)
Во многих играх почему то мало уделяют меню а зря. Если игра плохая то надо хоть как то смягчить удар.
Ну не знаю. Какой смысл делать красочное HD меню, если сама игра не очень? )
Можно привести в пример теже казуалки что не игра то калл, но почти у всех их есть отменное меню.
Угу)
Мне ещё вспоминается игра (вроде в Эпл сторе была), которая себя позиционирует как аналог ВоВ. Меню там супер, трейлер супер. А когда входишь в игру, то :-!
Сейчас много людей посвящают свое время играм )
Прежде всего, хочу сказать спасибо за проделанную вами роботу. Блог очень помог мне в освоении движка.
Вместе с тем хотел у меня возник вопрос:
Насколько необходимо использовать скрипты, и какой язык для этого предпочтительней ( в отношении к LibGdx)? Будут ли на эту тему уроки?
Не совсем понимаю, о чём вы)
Это не Unity, где скрипты для всего надо писать. Для работы с LibGDX используется только Java.
http://code.google.com/p/libgdx-users/wiki/LuaTutorial — наткнулся случайно, вот и стало интересно.
А так же, в демо ( https://github.com/libgdx/libgdx/tree/master/demos/pax-britannica ) есть пример, с использованием скриптов.
Удивили, честно говоря.
Если судить по статье, то там вызов Lua скриптов всё равно из Java приходится делать, как и с ndk. То есть, без Java всё равно никак. Не особо представляю, зачем оно надо. Хотя, если вы много лет работали с Lua, то может и имеет смысл.
Я вряд ли могу статьи по этому поводу написать, ибо с Lua никогда не работал =/
Добрый день. Спасибо за отличный цикл статей, очень легко и доходчиво написано.
Было б интересно почитать и про Memory Management в libgdx в вашем блоге :)
А что именно про Memory Management хотелось бы почитать? )
Например, не ясно как очистить Texture , TextureRegion ( так как именно эти моменты дают хорошую утечку памяти в моей игре). А вообще, было б интересно пообщаться с вами в скайпе,так как нету знакомых знающих libgdx. Если будет желание — добавляйтесь : alex.zaiats
У этих классов есть метод
dispose()
, который чистит ресурсы. Я его вызываю при переходах между экрана и(-или) в дестркуторе.Просто не думаю, что это на отдельную статью тянет.
А как вы об утечке памяти узнаёте?
самый простой способ — скачать программку типа Ram Booster для андроида, играть в игру и проверять потребление оперативы, если растет — утечка есть. Можно использовать MAT для еклипса,в нем чуть удобнее это прослеживается.
У меня игра достаточно простая, ( если интересно — https://play.google.com/store/apps/details?id=com.platinumgame.catchthecat), там приходится после каждой игры пересоздавать объексты поля( делать new Texture ), иначе после нескольких игр ячейки поля затираются неясными черными пикселями. Вот и получается что после каждой новой игры утекает около 2-3мб памяти за счет пересоздания ячеек поля. :)
И еще 1 вопрос : У вас не было жалоб что игра вылетает с ошибкой на девайся типа Galaxy Gio , Galaxy Duo с ошибкой :
java.lang.RuntimeException: eglSwapBuffers failed: EGL_BAD_ATTRIBUTE
Пост на эту тему на стек оверфлоу :
http://stackoverflow.com/questions/10459357/java-lang-runtimeexception-eglswapbuffers-failed-egl-success
Я проверял потребление памяти тремя способами:
1) В Eclipse чекал сколько памяти ест в общем.
2) В SDK для 4.1+ Андройдов появился Tracer for OpenGL.
3) Программа от создателя чипсета. У меня Adreno на HTC One S. Adreno Profiler показывает сколько памяти ест игра, можно посмотреть какие текстуры в конкретный момент времени в памяти и т.д.
Не, таких крашей ни в одной игре ни разу не было. Но пишут, что из-за размеров атласа проблема может быть. У меня все атласы не больше 1024×1024.
Где можно андроид прорутировать пожалуйста подскажите?
http://4pda.ru/forum/index.php?showtopic=463488&st=20
Спасибо. В своих играх часто наталкивался на ошибку исключения когда что ни будь обнулял, например когда рисуется Bitmap и в этот момент вызываешь метод Bitmap =null; то приложение вылетает с ошибкой java.lang.NullPointerException, и для того чтобы этого избежать, перед этим методом я рисовал что то другое или заполнял цветом весь экран, а потом обнулял. В вашем методе dispose(); вы не чего не рисуете, но обнуляете текстуры @Override
public void dispose() {
Gdx.input.setInputProcessor(null);
try{
spriteBatch.dispose();bgTexture.dispose();textures.clear();
}
catch(Exception e){}
}. То есть текстуры обнуляются в тот момент, когда рисуются. Почему не возникает ошибка, чем это достигается? Это не опасно?
У меня после вызова dispose сразу переключение на другой экран всегда идёт, поэтому после очистки новой отрисовки на том экране уже нет, поэтому и не выпадает NullPointerException.
Просто на Bitmap есть ограничение по разрешению http://developer.android.com/training/displaying-bitmaps/index.html и превышение этого разрешения может вызвать ошибку java.lang.OutofMemoryError: bitmap size exceeds VM budget, потому необходимо периодически обнулять и экономить память. А на текстуры папки asset такое же ограничение, не подскажите, при вызове методов bgTexture.dispose();textures.clear(); освобождается память для загрузки новых изображений ?
Именно для очистки эти методы и вызываются. В принципе, даже если вы просто null присвоите, то GC должен будет сам потом очистить ресурсы теоретически.
цитата Suvitruf,
«У меня после вызова dispose сразу переключение на другой экран всегда идёт, поэтому после очистки новой отрисовки на том экране уже нет, » а если перед переходом придется загрузить большое количество атласов и на загрузку потребуется время, то что будет отображаться на экране? Если я не ошибаюсь, переключение на другой экран не может произойти пака не загрузятся нужные для него атласы? Что будет на дисплее, пака загружается новый экран методом game.setScreen(game.game);? Стоит ли делать загрузку в фоновом потоке с помощью AsyncTask или у libGDX есть для этого свой класс?
И если не много, хотел спросить по поводу ограничения на разрешение. Загрузка суммарного разрешения Bitmap 2592×1936 из ресурсов может вызвать java.lang.OutofMemoryError (то есть при загрузке 6 Bitmap изображений разрешением 1024×1024 на шестой текстуре приложение вылетит). При загрузке текстур из asset методом bgTexture = new Texture(Gdx.files.internal(«images/bg.png»)); все произойдет точно так же, или тут другой подход к загрузке?
а можно ли сделать рандомное переключение экранов?
Это как? )
ну это типо нажал на кнопку, и открылся случайный экран
Можно.
а как?(хотябы немного намекните))
В обработчике нажатия на кнопку, используя
Random()
, устанавливать экран.Подскажите пожалуйста, как можно выйти из игры на сайт? Через активити я раньше делал с помощью интент
public boolean Exittouch(MotionEvent event){
if(event.getAction()==MotionEvent.ACTION_DOWN){
Intent inte = new Intent(android.content.Intent.ACTION_VIEW,Uri.parse(«https://play.google.com/….»));
startActivity(inte);
}
Но тут расширение не от Activity, а от com.badlogic.gdx.backends.android.AndroidApplication. Как вы думаете, через какой класс это возможно сделать?