Как написать свою первую игру для Android на Java
Есть много способов создать игру для Android, и один из важных способов – сделать это с нуля в Android Studio с Java. Это дает вам максимальный контроль над тем, как вы хотите, чтобы ваша игра выглядела и вела себя, и этот процесс научит вас навыкам, которые вы можете использовать и в ряде других сценариев – независимо от того, создаете ли вы экран-заставку для приложения или просто хотите добавить анимацию. Имея это в виду, это руководство покажет вам, как создать простую 2D-игру с помощью Android Studio и Java. Вы можете найти весь код и ресурсы на Github, если хотите продолжить.
Настройка
Чтобы создать нашу игру, нам нужно будет иметь дело с несколькими конкретными концепциями: игровые циклы, потоки и холсты. Для начала запустите Android Studio. Если он у вас не установлен, ознакомьтесь с нашим полным введением в Android Studio, в котором описывается процесс установки. Теперь начните новый проект и убедитесь, что вы выбрали шаблон «Пустое действие». Это игра, поэтому, конечно, вам не нужны такие элементы, как кнопка FAB, усложняющая дело.
Первое, что вам нужно сделать, это изменить AppCompatActivity на Activity. Это означает, что мы не будем использовать функции панели действий.
Точно так же мы хотим сделать нашу игру полноэкранной. Добавьте следующий код в onCreate() перед вызовом setContentView ():
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
Обратите внимание: если вы напишете какой-то код, и он будет подчеркнут красным, это, вероятно, означает, что вам нужно импортировать класс. Другими словами, вам нужно сообщить Android Studio, что вы хотите использовать определенные операторы и сделать их доступными. Если вы просто щелкните в любом месте подчеркнутого слова, а затем нажмите Alt + Enter, это будет сделано за вас автоматически!
Создание вашего игрового представления
Вы можете привыкнуть к приложениям, которые используют XML-скрипт для определения макета представлений, таких как кнопки, изображения и метки. Это то, что для нас делает строка setContentView .
Но опять же, это игра, означающая, что в ней не нужны окна браузера или прокручивающиеся представления ресайклера. Вместо этого мы хотим показать холст. В Android Studio холст такой же, как и в искусстве: это среда, на которой мы можем рисовать.
Так что измените эту строку, чтобы она читалась так:
setContentView(
Вы обнаружите, что это снова подчеркнуто красным. Но теперь, если вы нажмете Alt + Enter, у вас не будет возможности импортировать класс. Вместо этого у вас есть возможность создать класс. Другими словами, мы собираемся создать наш собственный класс, который будет определять, что будет происходить на холсте. Это то, что позволит нам рисовать на экране, а не просто показывать готовые виды.
Итак, щелкните правой кнопкой мыши имя пакета в иерархии слева и выберите «Создать»> «Класс». Теперь вам будет представлено окно для создания вашего класса, и вы назовете его GameView. В SuperClass напишите: android.view.SurfaceView, что означает, что класс унаследует методы – свои возможности – от SurfaceView.
В поле Interface (s) вы напишите android.view.SurfaceHolder.Callback. Как и в случае с любым другим классом, теперь нам нужно создать наш конструктор. Используйте этот код:
private
Каждый раз, когда наш класс вызывается для создания нового объекта (в данном случае нашей поверхности), он запускает конструктор и создает новую поверхность. Строка super вызывает суперкласс, и в нашем случае это SurfaceView.
Добавляя обратный вызов, мы можем перехватывать события.
Теперь переопределите некоторые методы:
@Override
Это в основном позволяет нам переопределять (отсюда и название) методы суперкласса (SurfaceView). Теперь в вашем коде больше не должно быть красных подчеркиваний. Ницца.
Вы только что создали новый класс, и каждый раз, когда мы обращаемся к нему, он будет создавать основу для рисования вашей игры. Классы создают объекты, и нам нужен еще один.
Создание тем
Наш новый класс будет называться MainThread. И его работа будет заключаться в создании потока. Поток по сути похож на параллельную ветвь кода, которая может выполняться одновременно с основной частью вашего кода. У вас может быть много потоков, работающих одновременно, что позволяет выполнять задачи одновременно, а не придерживаться строгой последовательности. Это важно для игры, потому что нам нужно следить за тем, чтобы она продолжала работать бесперебойно, даже когда много чего происходит.
Создайте новый класс так же, как и раньше, и на этот раз он расширит Thread. В конструкторе мы просто вызываем super (). Помните, что это суперкласс, Thread, который может сделать за нас всю тяжелую работу. Это похоже на создание программы для мытья посуды, которая просто вызывает washMachine () .
Когда этот класс вызывается, он создает отдельный поток, который работает как ответвление от главного. И это из здесь, что мы хотим создать нашу Gameview. Это означает, что нам также нужно ссылаться на класс GameView, и мы также используем SurfaceHolder, который содержит холст. Итак, если холст – это поверхность, SurfaceHolder – это мольберт. И GameView – это то, что объединяет все воедино.
Полная вещь должна выглядеть так:
public
Schweet. Теперь у нас есть GameView и ветка!
Создание игрового цикла
Теперь у нас есть сырье, необходимое для создания игры, но ничего не происходит. Вот здесь и появляется игровой цикл. По сути, это цикл кода, который проходит по кругу и проверяет входные данные и переменные перед отрисовкой экрана. Наша цель – сделать это как можно более последовательным, чтобы не было заиканий или сбоев в частоте кадров, которые я рассмотрю немного позже.
На данный момент мы все еще находимся в классе MainThread и собираемся переопределить метод из суперкласса. Этот запущен .
И это выглядит примерно так:
@Override
Вы увидите много подчеркивания, поэтому нам нужно добавить еще несколько переменных и ссылок. Вернитесь наверх и добавьте:
private
Не забудьте импортировать Canvas. На самом деле мы будем рисовать холст. Что касается «lockCanvas», это важно, потому что это то, что по сути замораживает холст, чтобы мы могли рисовать на нем. Это важно, потому что в противном случае у вас могло бы быть несколько потоков, пытающихся использовать его одновременно. Просто знайте, что для редактирования холста вы должны сначала заблокировать холст.
Обновление – это метод, который мы собираемся создать, и именно здесь позже произойдет самое интересное.
Попробовать и поймать то время просто требования Java, которые показывают, что мы готовы попробовать и обрабатывать исключения (ошибки), которые могут возникнуть, если полотно не готово и т.д.
Наконец, мы хотим иметь возможность запускать наш поток, когда нам это нужно. Для этого нам понадобится еще один метод, который позволит нам привести в движение вещи. Для этого и предназначена текущая переменная (обратите внимание, что логическое значение – это тип переменной, которая всегда может быть истинной или ложной). Добавьте этот метод в класс MainThread :
public void setRunning(boolean isRunning)
Но на этом этапе следует выделить одну вещь, а именно обновление. Это потому, что мы еще не создали метод обновления. Так что вернитесь в GameView и добавьте метод.
public void update()
Нам также нужно запустить ветку! Мы собираемся сделать это в нашем методе surfaceCreated :
@Override
Нам также нужно остановить поток, когда поверхность разрушена. Как вы уже догадались, мы обрабатываем это в методе surfaceDestroyed. Но поскольку на самом деле может потребоваться несколько попыток для остановки потока, мы собираемся поместить это в цикл и снова использовать try and catch. Вот так:
@Override
И, наконец, перейдите к конструктору и убедитесь, что вы создали новый экземпляр вашего потока, иначе вы получите ужасное исключение нулевого указателя! А затем мы сделаем GameView настраиваемым, то есть он может обрабатывать события.
thread =
Теперь вы, наконец, можете протестировать эту штуку! Правильно, нажмите «Выполнить», и он действительно должен работать без ошибок. Приготовьтесь к тому, чтобы вас сдул!
Это … это … пустой экран! Весь этот код. Для пустого экрана. Но это пустой экран возможностей. Ваша поверхность настроена и работает с игровым циклом для обработки событий. Теперь все, что осталось, – это заставить все происходить. Даже не имеет значения, если вы не выполнили все в руководстве до этого момента. Дело в том, что вы можете просто переработать этот код, чтобы начать создавать великолепные игры!
Делаем графику
Итак, теперь у нас есть пустой экран, на котором можно рисовать, все, что нам нужно сделать, это нарисовать на нем. К счастью, это простая часть. Все, что вам нужно сделать, это переопределить метод рисования в нашем классе GameView, а затем добавить несколько красивых картинок:
@Override
Запустите это, и у вас должен появиться красивый красный квадрат в верхнем левом углу белого экрана. Это, безусловно, улучшение.
Теоретически вы можете создать практически всю свою игру, вставив ее в этот метод (и переопределив onTouchEvent для обработки ввода), но это не очень хороший способ действовать. Размещение нового Paint внутри нашего цикла значительно замедлит работу, и даже если мы разместим это в другом месте, добавление слишком большого количества кода к методу рисования будет некрасивым и трудным для понимания.
Вместо этого имеет смысл обрабатывать игровые объекты с их собственными классами. Мы начнем с того, который показывает персонажа, и этот класс будет называться CharacterSprite. Давай, сделай это.
Этот класс будет рисовать спрайт на холсте и будет выглядеть так:
public
Теперь, чтобы использовать это, вам нужно сначала загрузить растровое изображение, а затем вызвать класс из GameView. Добавьте ссылку на частный CharacterSprite characterSprite, а затем в методе surfaceCreated добавьте строку:
characterSprite =
Как видите, загружаемое растровое изображение хранится в ресурсах и называется avdgreen (оно было из предыдущей игры). Теперь все, что вам нужно сделать, это передать это растровое изображение новому классу в методе рисования с помощью:
characterSprite.draw(canvas);
Теперь нажмите «Выполнить», и на экране должно появиться изображение! Это Бибу. Я рисовал его в школьных учебниках.
Что, если бы мы хотели заставить этого человечка двигаться? Все просто: мы просто создаем переменные x и y для его позиций, а затем изменяем эти значения в методе обновления .
Поэтому добавьте ссылки в свой CharacterSprite, а затем нарисуйте растровое изображение в точках x, y. Создайте здесь метод обновления, а пока мы просто попробуем:
y++;
При каждом запуске игрового цикла мы перемещаем персонажа вниз по экрану. Помните, что координаты y отсчитываются сверху, поэтому 0 – это верх экрана. Конечно, нам нужно вызвать метод обновления в CharacterSprite из метода обновления в GameView .
Снова нажмите кнопку воспроизведения, и теперь вы увидите, что ваше изображение медленно перемещается по экрану. Мы еще не выиграли никаких игровых наград, но это только начало!
Хорошо, чтобы сделать вещи немного интереснее, я просто опущу здесь код «надувного мяча». Это заставит нашу графику отскакивать от краев экрана, как в старых заставках Windows. Вы знаете, странно гипнотические.
public void update()
Вам также нужно будет определить эти переменные:
private
оптимизация
Здесь есть еще много чего, от обработки ввода игрока до масштабирования изображений и управления одновременным перемещением множества персонажей по экрану. Прямо сейчас персонаж подпрыгивает, но если вы присмотритесь, то заметите легкое заикание. Это не страшно, но то, что вы можете увидеть это невооруженным глазом, является своего рода предупреждающим знаком. Скорость эмулятора также сильно различается по сравнению с физическим устройством. Теперь представьте, что происходит, когда у вас есть тонны происходит на экране сразу!
Есть несколько решений этой проблемы. Для начала я хочу создать частное целое число в MainThread и вызвать его targetFPS. Он будет иметь значение 60. Я собираюсь попытаться заставить мою игру работать на этой скорости, а пока я буду проверять, есть ли это. Для этого мне также нужен частный двойник под названием averageFPS .
Я также собираюсь обновить метод запуска, чтобы измерить, сколько времени занимает каждый игровой цикл, а затем временно приостановить этот игровой цикл, если он опережает targetFPS. Мы тогда будем рассчитать, сколько времени в настоящее время взял, а затем распечатать, что таким образом мы можем увидеть его в журнале.
@Override
Теперь наша игра пытается заблокировать частоту кадров до 60, и вы должны обнаружить, что она обычно показывает довольно стабильные 58-62 кадра в секунду на современном устройстве. На эмуляторе вы можете получить другой результат.
Попробуйте изменить это 60 на 30 и посмотрите, что произойдет. Игра замедляется, и он должен теперь прочитать 30 в вашем LogCat.
Заключительные мысли
Есть еще кое-что, что мы можем сделать для оптимизации производительности. Там отличный блог на эту тему здесь. Постарайтесь воздержаться от создания новых экземпляров Paint или растровых изображений внутри цикла и выполнить всю инициализацию снаружи до начала игры.
Если вы планируете создать следующую популярную игру для Android, в наши дни, безусловно, есть более простые и эффективные способы сделать это. Но определенно существуют сценарии использования для рисования на холсте, и это очень полезный навык, который можно добавить в свой репертуар. Я надеюсь, что это руководство мне немного помогло, и желаю удачи в ваших будущих начинаниях по кодированию!
Далее – Руководство по Java для новичков
Источник записи: https://www.androidauthority.com