Как написать свою первую игру для Android на Java

Автор: John Stephens
Дата создания: 1 Январь 2021
Дата обновления: 19 Май 2024
Anonim
Изучение Android Studio за час в одном видео! Создание погодного приложения с API
Видео: Изучение Android Studio за час в одном видео! Создание погодного приложения с API

Содержание


Существует множество способов создать игру для Android, и один важный способ - сделать это с нуля в Android Studio с Java. Это дает вам максимальный контроль над тем, как ваша игра будет выглядеть и вести себя, и этот процесс научит вас навыкам, которые вы можете использовать в ряде других сценариев - будь то создание заставки для приложения или вы просто хотите добавить некоторые анимации. Имея это в виду, этот урок покажет вам, как создать простую 2D-игру с использованием Android Studio и Java. Вы можете найти весь код и ресурсы на Github, если хотите следовать.

Настройка

Чтобы создать нашу игру, нам нужно разобраться с несколькими конкретными понятиями: игровые циклы, нити и холсты. Для начала запустите Android Studio. Если он не установлен, ознакомьтесь с нашим полным введением в Android Studio, в котором рассказывается о процессе установки. Теперь запустите новый проект и убедитесь, что вы выбрали шаблон «Пустое действие». Это игра, поэтому, конечно, вам не нужны такие элементы, как кнопка FAB, усложняющая ситуацию.


Первое, что вы хотите сделать, это изменить AppCompatActivity в Мероприятия, Это означает, что мы не будем использовать функции панели действий.

Точно так же мы хотим сделать нашу игру полноэкранной. Добавьте следующий код в onCreate () перед вызовом setContentView ():

getWindow (). setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); this.requestWindowFeature (Window.FEATURE_NO_TITLE);

Обратите внимание, что если вы напишите какой-то код, и он будет подчеркнут красным, это, вероятно, означает, что вам нужно импортировать класс. Другими словами, вам нужно сообщить Android Studio, что вы хотите использовать определенные операторы и сделать их доступными. Если вы просто щелкнете в любом месте подчеркнутое слово, а затем нажмете Alt + Enter, то это будет сделано автоматически!

Создание вашего игрового представления

Вы можете быть использованы для приложений, которые используют сценарий XML для определения макета представлений, таких как кнопки, изображения и метки. Это то, что линия setContentView делает для нас.


Но опять же, это игра, означающая, что ей не нужно иметь окна браузера или прокручивать представления переработчика. Вместо этого мы хотим показать холст. В Android Studio холст такой же, как в искусстве: это среда, на которой мы можем рисовать.

Поэтому измените эту строку следующим образом:

setContentView (новый GameView (это))

Вы обнаружите, что это еще раз подчеркнуто красным. Но в настоящее время если вы нажмете Alt + Enter, у вас не будет возможности импортировать класс. Вместо этого у вас есть возможность Создайте класс. Другими словами, мы собираемся создать наш собственный класс, который будет определять, что будет происходить на холсте. Это то, что позволит нам рисовать на экране, а не просто показывать готовые виды.

Поэтому щелкните правой кнопкой мыши на имени пакета в вашей иерархии слева и выберите Новый> Класс, Теперь у вас будет окно для создания вашего класса, и вы будете называть его Gameview, Под Суперклассом напишите: android.view.SurfaceView Это означает, что класс будет наследовать методы - его возможности - от SurfaceView.

В поле Interface (s) вы напишите android.view.SurfaceHolder.Callback, Как и с любым классом, теперь нам нужно создать наш конструктор. Используйте этот код:

приватная тема MainThread; public GameView (Context context) {супер (context); . GetHolder () addCallback (это); }

Каждый раз, когда наш класс вызывается для создания нового объекта (в данном случае нашей поверхности), он запускает конструктор и создает новую поверхность. Строка «super» вызывает суперкласс, а в нашем случае это SurfaceView.

Добавив Обратный звонок, мы можем перехватывать события.

Теперь переопределите некоторые методы:

@Override public void surfaceChanged (держатель SurfaceHolder, формат int, int width, int height) {} @Override public void surfaceCreated (держатель SurfaceHolder) {} @Override открытый void surfaceDestroyed (держатель SurfaceHolder) {}

Это в основном позволяет нам переопределять (отсюда и имя) методы в суперклассе (SurfaceView). Теперь в вашем коде больше не должно быть красных подчеркиваний. Ницца.

Вы только что создали новый класс, и каждый раз, когда мы ссылаемся на него, он создает холст для вашей игры. Классы Создайте объекты и нам нужен еще один.

Создание тем

Наш новый класс будет называться MainThread, И его работа будет заключаться в создании темы. Поток, по сути, похож на параллельный форк кода, который может выполняться одновременно с главный часть вашего кода. Вы можете запускать множество потоков одновременно, что позволяет происходить одновременно, а не придерживаться строгой последовательности. Это важно для игры, потому что нам нужно следить за тем, чтобы она продолжала работать гладко, даже когда многое происходит.

Создайте свой новый класс так же, как вы делали это раньше, и на этот раз он будет расширяться Нить, В конструкторе мы просто собираемся позвонить супер(), Помните, что это супер класс, который является Thread, и который может сделать всю тяжелую работу для нас. Это похоже на создание программы для мытья посуды, которая просто вызывает стиральная машина().

Когда этот класс вызывается, он собирается создать отдельный поток, который работает как ответвление от основного. И это из Вот что мы хотим создать наш GameView. Это означает, что мы также должны ссылаться на класс GameView, и мы также используем SurfaceHolder, который содержит canvas. Так что, если холст - это поверхность, SurfaceHolder - это мольберт. А GameView - это то, что объединяет все это.

Полная вещь должна выглядеть так:

открытый класс MainThread extends Thread {private SurfaceHolder surfaceHolder; приватный GameView gameView; public MainThread (SurfaceHolder surfaceHolder, GameView gameView) {super (); this.surfaceHolder = surfaceHolder; this.gameView = gameView; }}

Schweet. Теперь у нас есть GameView и поток!

Создание игрового цикла

Теперь у нас есть сырье, необходимое для нашей игры, но ничего не происходит. Вот где начинается игровой цикл. По сути, это цикл кода, который вращается и проверяет входные данные и переменные перед рисованием экрана. Наша цель состоит в том, чтобы сделать это как можно более последовательным, чтобы в кадре не было заиканий или сбоев, о которых я расскажу чуть позже.

На данный момент мы все еще в MainThread класс, и мы собираемся переопределить метод из суперкласса. Этот бег.

И это выглядит примерно так:

@Override public void run () {while (running) {canvas = null; try {canvas = this.surfaceHolder.lockCanvas (); синхронизированный (surfaceHolder) {this.gameView.update (); this.gameView.draw (холст); }} catch (Exception e) {} finally {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Exception e) {e.printStackTrace (); }}}}}

Вы увидите много подчеркиваний, поэтому нам нужно добавить еще несколько переменных и ссылок. Вернитесь наверх и добавьте:

частный SurfaceHolder surfaceHolder; приватный GameView gameView; частный логический запуск; общедоступная статическая канва Canvas;

Не забудьте импортировать Canvas. Холст - это то, над чем мы будем рисовать. Что касается «lockCanvas», это важно, потому что это то, что по существу замораживает холст, чтобы позволить нам рисовать на нем. Это важно, потому что в противном случае вы могли бы одновременно пытаться рисовать несколько потоков. Просто знайте, что для того, чтобы отредактировать холст, необходимо сначала замок холст.

Обновление - это метод, который мы собираемся создать, и именно здесь самое интересное произойдет позже.

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

Наконец, мы хотим иметь возможность начать наш поток, когда нам это нужно. Для этого нам понадобится еще один метод, который позволит нам привести вещи в движение. Вот что Бег переменная для (обратите внимание, что логический тип является типом переменной, которая всегда имеет значение true или false). Добавьте этот метод к MainThread учебный класс:

public void setRunning (логическое isRunning) {running = isRunning; }

Но на данный момент, одна вещь все еще должна быть выделена, и это Обновить, Это потому, что мы еще не создали метод обновления. Так что возвращайтесь в Gameview а теперь добавьте метод.

public void update () {}

Нам также нужно Начало нить! Мы собираемся сделать это в нашем surfaceCreated метод:

@Override public void surfaceCreated (держатель SurfaceHolder) {thread.setRunning (true); Thread.start (); }

Нам также нужно остановить поток, когда поверхность разрушена. Как вы уже догадались, мы обращаемся с этим в surfaceDestroyed метод. Но, поскольку на самом деле может потребоваться несколько попыток остановить поток, мы собираемся поместить это в цикл и использовать пытаться а также ловить опять таки. Вот так:

@Override public void surfaceDestroyed (держатель SurfaceHolder) {логическое повторение = истина; while (retry) {try {thread.setRunning (false); Thread.join (); } catch (InterruptedException e) {e.printStackTrace (); } retry = false; }}

И наконец, зайдите в конструктор и обязательно создайте новый экземпляр вашего потока, иначе вы получите исключение страшного нулевого указателя! И затем мы собираемся сделать GameView фокусируемым, то есть он может обрабатывать события.

thread = new MainThread (getHolder (), this); setFocusable (истина);

Теперь вы можете Ну наконец то на самом деле проверить эту вещь! Это верно, нажмите Run и должен на самом деле работает без каких-либо ошибок. Приготовьтесь сдуться!

Это ... это ... пустой экран! Весь этот код. Для пустого экрана. Но это пустой экран возможность, Вы получили доступ к игровому циклу для обработки событий. Теперь все, что осталось, это сделать вещи. Это даже не имеет значения, если вы до этого момента не следовали всему учебнику. Дело в том, что вы можете просто переработать этот код, чтобы начать создавать великолепные игры!

Делать графику

Хорошо, теперь у нас есть пустой экран для рисования, все что нам нужно сделать, это нарисовать на нем. К счастью, это простая часть. Все, что вам нужно сделать, это переопределить метод рисования в нашем Gameview класс, а затем добавить несколько красивых картинок:

@Override public void draw (Canvas canvas) {super.draw (canvas); if (canvas! = null) {canvas.drawColor (Color.WHITE); Paint paint = new Paint (); paint.setColor (Color.rgb (250, 0, 0)); canvas.drawRect (100, 100, 200, 200, краска); }}

Запустите это, и у вас должен получиться красивый красный квадрат в левом верхнем углу белого экрана. Это, безусловно, улучшение.

Вы можете теоретически создать почти всю свою игру, вставив ее в этот метод (и переопределив onTouchEvent обрабатывать ввод) но это было бы не очень хорошим способом идти о вещах. Размещение нового Paint внутри нашего цикла значительно замедлит процесс, и даже если мы добавим это в другое место, добавим слишком много кода в привлечь метод станет уродливым и трудным для подражания.

Вместо этого имеет больше смысла обрабатывать игровые объекты с их собственными классами. Мы собираемся начать с того, который показывает характер, и этот класс будет называться CharacterSprite, Давай и сделай это.

Этот класс будет рисовать спрайт на холсте и будет выглядеть так

открытый класс CharacterSprite {личное растровое изображение; public CharacterSprite (Bitmap bmp) {image = bmp; } public void draw (Canvas canvas) {canvas.drawBitmap (image, 100, 100, null); }}

Теперь, чтобы использовать это, вам нужно сначала загрузить растровое изображение, а затем вызвать класс из Gameview, Добавить ссылку на частный персонажSprite персонажSprite а затем в surfaceCreated метод, добавьте строку:

characterSprite = new CharacterSprite (BitmapFactory.decodeResource (getResources (), R.drawable.avdgreen));

Как видите, загружаемое растровое изображение хранится в ресурсах и называется avdgreen (оно было из предыдущей игры). Теперь все, что вам нужно сделать, это передать это растровое изображение в новый класс в привлечь метод с:

characterSprite.draw (холст);

Теперь нажмите Run, и вы должны увидеть вашу графику на вашем экране! Это БиБу. Я рисовал его в своих школьных учебниках.

Что если мы хотим заставить этого маленького парня двигаться? Просто: мы просто создаем переменные x и y для его позиций, а затем меняем эти значения в Обновить метод.

Так что добавьте ссылки на ваш CharacterSprite а затем нарисуйте свое растровое изображение в х, у, Создайте здесь метод обновления, и сейчас мы просто попробуем:

у ++;

Каждый раз, когда запускается игровой цикл, мы перемещаем персонажа вниз по экрану. Помнить, Y координаты измеряются сверху, так 0 верхняя часть экрана. Конечно, нам нужно позвонить Обновить метод в CharacterSprite от Обновить метод в Gameview.

Нажмите кнопку воспроизведения еще раз, и теперь вы увидите, что ваше изображение медленно прослеживает экран. Мы пока не выигрываем игровые награды, но это только начало!

Хорошо, чтобы сделать вещи немного что еще интереснее, я просто добавлю сюда код "бодрый мяч". Это заставит нашу графику отскочить по экрану от краев, как те старые заставки Windows. Вы знаете, странно гипнотические.

public void update () {x + = xVelocity; y + = yVelocity; if ((x & gt; screenWidth - image.getWidth ()) || (x & lt; 0)) {xVelocity = xVelocity * -1; } if ((y & gt; screenHeight - image.getHeight ()) || (y & lt; 0)) {yVelocity = yVelocity * -1; }}

Вам также необходимо определить эти переменные:

private int xVelocity = 10; private int yVelocity = 5; private int screenWidth = Resources.getSystem (). getDisplayMetrics (). widthPixels; private int screenHeight = Resources.getSystem (). getDisplayMetrics (). heightPixels;

оптимизация

Есть много здесь можно больше углубиться, от обработки ввода игрока до масштабирования изображений, до управления множеством персонажей, одновременно перемещающихся по экрану. Прямо сейчас персонаж подпрыгивает, но если вы присмотритесь, то слегка заикается. Это не страшно, но тот факт, что вы можете увидеть это невооруженным глазом, является чем-то вроде предупреждающего знака. Скорость также сильно варьируется на эмуляторе по сравнению с физическим устройством. Теперь представьте, что происходит, когда у вас есть тонны происходит на экране сразу!

Есть несколько решений этой проблемы. Для начала я хочу создать личное целое число в MainThread и называть это targetFPS, Это будет иметь значение 60.Я постараюсь, чтобы моя игра работала с такой скоростью, а между тем я буду проверять, есть ли она. Для этого я также хочу частный двойник под названием averageFPS.

Я также собираюсь обновить бег метод, чтобы измерить, сколько времени занимает каждый игровой цикл, а затем Пауза этот игровой цикл временно, если он опережает targetFPS. Затем мы рассчитаем, как долго в настоящее время взял, а затем распечатать, чтобы мы могли видеть это в журнале.

@Override public void run () {long startTime; долгое время Миллис; long waitTime; long totalTime = 0; int frameCount = 0; long targetTime = 1000 / targetFPS; while (running) {startTime = System.nanoTime (); холст = ноль; try {canvas = this.surfaceHolder.lockCanvas (); синхронизированный (surfaceHolder) {this.gameView.update (); this.gameView.draw (холст); }} catch (Exception e) {} finally {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Exception e) {e.printStackTrace (); }}} timeMillis = (System.nanoTime () - startTime) / 1000000; waitTime = targetTime - timeMillis; try {this.sleep (waitTime); } catch (Exception e) {} totalTime + = System.nanoTime () - startTime; FrameCount ++; if (frameCount == targetFPS) {averageFPS = 1000 / ((totalTime / frameCount) / 1000000); frameCount = 0; totalTime = 0; System.out.println (averageFPS); }}}

Теперь наша игра пытается зафиксировать частоту кадров в секунду до 60, и вы должны обнаружить, что она обычно измеряет довольно устойчивые 58-62 кадров в секунду на современном устройстве. На эмуляторе вы можете получить другой результат.

Попробуйте изменить это значение на 60 и посмотреть, что произойдет. Игра тормозит и это должен Теперь прочитайте 30 в вашем logcat.

Заключительные мысли

Есть и другие вещи, которые мы можем сделать, чтобы оптимизировать производительность. На эту тему есть отличный пост в блоге. Старайтесь воздерживаться от создания новых экземпляров Paint или растровых изображений внутри цикла и выполняйте всю инициализацию. за пределами до начала игры.

Если вы планируете создать следующую популярную игру для Android, то есть безусловно более простые и эффективные способы сделать это в наши дни. Но, безусловно, есть сценарии использования для рисования на холсте, и это очень полезный навык для добавления в свой репертуар. Я надеюсь, что это руководство несколько помогло, и желаю вам удачи в ваших будущих начинаниях по программированию!

следующийРуководство для начинающих по Java

ИИ здесь и сейчас. Возможно, мы еще не совсем на стадии «Я, робот», но автомобили с самостоятельным вождением - это вещь, и искусственный интеллект все больше движет тем, как работает мир во...

Если вы относитесь к тому типу детей, у которых есть основания разбирать стерео у своих родителей, у вас могут быть способности к электронике. С Arduinoтвой подарок за мастеринг может быть перешел на ...

Рекомендуем вам