[Разработка игр, Разработка под Android, Хакатоны] Делаем игру с управлением улыбкой
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Привет! Меня зовут Иван Шафран, недавно я присоединился к команде видео ВКонтакте в роли программиста-разработчика для Android. Участвую в создании как продуктовых приложений, так и SDK. Время от времени я посещаю хакатоны, где можно реализовывать любые безумные идеи. Сегодня расскажу, как за пару часов сделать прототип мобильной игры с необычным управлением: персонаж будет реагировать на улыбку и подмигивание.
оригинал
Как возникла идея
Мысль создать такую игру пришла как раз во время хакатона. Формат предполагал, что на разработку есть один рабочий день, то есть 8 часов. Чтобы успеть сделать прототип, я выбрал Android SDK. Возможно, лучше подошли бы игровые движки, но в них я не разбираюсь.
Концепцию управления с помощью эмоций подсказала другая игра: там движения персонажа можно было задавать, меняя громкость своего голоса. Может, и эмоции уже кто-то использовал в игровом управлении. Но я знаю мало таких примеров, поэтому остановился на этом формате.
Осторожно громкое видео!
Извините, данный ресурс не поддреживается. :(
Настраиваем окружение для разработки
Нам понадобится только Android Studio на компьютере. Если нет реального устройства на Android для запуска, можно воспользоваться эмулятором с включённой веб-камерой.
Создаём проект с ML Kit
ML Kit — отличный инструмент, который поможет впечатлить жюри хакатона: ведь вы используете AI в прототипе! А вообще он помогает встраивать в проекты решения на основе машинного обучения, например функциональность для определения объектов в кадре, перевода и распознавания текста.
Для нас важно, что у ML Kit есть бесплатный offline API для распознавания улыбки и открытых или закрытых глаз.
Раньше, чтобы создать любой проект с ML Kit, нужно было сначала зарегистрироваться в консоли Firebase. Теперь этот шаг можно пропустить для офлайн-функциональности.
Android-приложение
Удаляем лишнее
Чтобы не писать логику по работе с камерой с нуля, возьмём официальный семпл и уберём из него то, что нам не нужно.
Для начала скачайте пример и попробуйте запустить. Исследуйте режим Face detection: выглядеть это будет, как на превью статьи.
Манифест
Начнём правки с AndroidManifest.xml. Удалим все теги activity, кроме первого. А на его место выставим CameraXLivePreviewActivity, чтобы сразу запускаться с камеры. В значении атрибута android:value оставляем только face, чтобы исключить из APK ненужные нам ресурсы.
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="face"/>
<activity
android:name=".CameraXLivePreviewActivity"
android:exported="true"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
Полный diff шага.
Камера
Сэкономим время — не будем удалять лишние файлы, вместо этого сконцентрируемся на элементах экрана CameraXLivePreviewActivity.
- На строке 117 установим режим face detection:
private String selectedModel = FACE_DETECTION;
- На строке 118 включим фронтальную камеру:
private int lensFacing = CameraSelector.LENS_FACING_FRONT;
- В конце метода onCreate на строках 198–199 скроем настройки
findViewById( R.id.settings_button ).setVisibility( View.GONE );
findViewById( R.id.control ).setVisibility( View.GONE );
На этом можно остановиться. Но если отрисовка FPS и сетка лица визуально отвлекают, то выключить их можно так:
- В файле VisionProcessorBase.java удаляем строки 213–215, чтобы скрыть FPS:
graphicOverlay.add(
new InferenceInfoGraphic(
graphicOverlay, currentLatencyMs, shouldShowFps ? framesPerSecond : null));
- В файле FaceDetectorProcessor.java удаляем строки 75–78, чтобы скрыть сетку лица:
for (Face face : faces) {
graphicOverlay.add(new FaceGraphic(graphicOverlay, face));
logExtrasForTesting(face);
}
Полный diff шага.
Распознаём эмоции
Распознавание улыбки по умолчанию выключено, но запустить его очень просто. Не зря же мы брали пример кода за основу! Выделим необходимые нам параметры в отдельный класс и объявим интерфейс слушателя:
FaceDetectorProcessor.java
SPL
// В классе FaceDetectorProcessor.java
public class FaceDetectorProcessor extends VisionProcessorBase<List<Face>> {
public static class Emotion {
public final float smileProbability;
public final float leftEyeOpenProbability;
public final float rightEyeOpenProbability;
public Emotion(float smileProbability, float leftEyeOpenProbability, float rightEyeOpenProbability) {
this.smileProbability = smileProbability;
this.leftEyeOpenProbability = leftEyeOpenProbability;
this.rightEyeOpenProbability = rightEyeOpenProbability;
}
}
public interface EmotionListener {
void onEmotion(Emotion emotion);
}
private EmotionListener listener;
public void setListener(EmotionListener listener) {
this.listener = listener;
}
@Override
protected void onSuccess(@NonNull List<Face> faces, @NonNull GraphicOverlay graphicOverlay) {
if (!faces.isEmpty() && listener != null) {
Face face = faces.get(0);
if (face.getSmilingProbability() != null &&
face.getLeftEyeOpenProbability() != null && face.getRightEyeOpenProbability() != null) {
listener.onEmotion(new Emotion(
face.getSmilingProbability(),
face.getLeftEyeOpenProbability(),
face.getRightEyeOpenProbability()
));
}
}
}
}
Чтобы включить классификацию эмоций, настроим FaceDetectorProcessor в классе CameraXLivePreviewActivity и подпишемся на получение состояния эмоций. Затем вероятности преобразуем в булевы флаги. Для тестирования в вёрстку добавим TextView, в котором покажем эмоции через смайлы.
Полный diff шага.
Разделяй и играй
Раз мы делаем игру, нужно место для рисования элементов. Будем считать, что она запускается на телефоне в портретном режиме. Значит, разделим экран на две части: камера сверху и игра снизу.
Контролировать персонажа с помощью улыбки сложно, к тому же на хакатоне мало времени для реализации продвинутой механики. Поэтому наш персонаж будет собирать ништяки по дороге, находясь либо в верхней части игрового поля, либо в нижней. Действия с закрытыми или открытыми глазами добавим как усложнение игры: поймали ништяк с закрытым глазом — очки удваиваются (либо пол-экрана не видно и можно грабить корованы).
Если хотите реализовать другой игровой процесс, то могу подсказать несколько занятных вариантов:
- Guitar Hero / Just Dance — аналог, где под музыку нужно показывать определённую эмоцию;
- гонка с преодолением препятствий, где нужно доехать до финиша за определённое время или не разбившись;
- шутер, где подмигиванием игрок делает выстрел в противника.
Отображать игру будем в кастомном Android View — там в методе onDraw нарисуем персонажа на Canvas. В первом прототипе ограничимся геометрическими примитивами.
Игрок
Наш персонаж — это квадрат. При инициализации зададим его размеры и установим положение слева, так как он будет находиться на месте. Позиция по оси Y будет зависеть от улыбки игрока. Все абсолютные значения будем высчитывать относительно размеров области игры. Это проще, чем подбирать конкретные размеры, — да и на новых устройствах получим приемлемый вид.
private var playerSize = 0
private var playerRect = RectF()
// Инициализируем размеры в зависимости от высоты View
private fun initializePlayer() {
playerSize = height / 4
playerRect.left = playerSize / 2f
playerRect.right = playerRect.left + playerSize
}
// Имеем в полях класса флаги эмоций
private var flags: EmotionFlags
// Устанавливаем положение в зависимости от улыбки
private fun movePlayer() {
playerRect.top = getObjectYTopForLine(playerSize, isTopLine = flags.isSmile).toFloat()
playerRect.bottom = playerRect.top + playerSize
}
// Получаем позицию top для объекта с высотой size,
// чтобы он был посередине верхней или нижней дорожки
private fun getObjectYTopForLine(size: Int, isTopLine: Boolean): Int {
return if (isTopLine) {
width / 2 - width / 4 - size / 2
} else {
width / 2 + width / 4 - size / 2
}
}
// Храним paint в полях класса, так как создавать его на каждый кадр накладно
private val playerPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
color = Color.BLUE
}
// Рисуем наш квадрат на Canvas
private fun drawPlayer(canvas: Canvas) {
canvas.drawRect(playerRect, playerPaint)
}
Тортик
Наш персонаж «бежит» и пытается ловить тортики, чтобы набрать как можно больше очков. Мы используем стандартный приём с переходом в систему отсчёта относительно игрока: он будет стоять на месте, а тортики — лететь к нему. Если квадрат тортика пересекается с квадратом игрока, то засчитываем балл. А если при этом хотя бы один глаз у пользователя закрыт — два балла ¯ \ _ ( ツ ) _ / ¯
Также в нашей вселенной будет всего один электрон тортик. Как только персонаж его съедает, он перемещается за экран на случайную полосу со случайной координатой. Так улыбка игрока не войдёт в резонанс при предсказуемом появлении тортика.
// При инициализации тортика сразу перемещаем его за экран
private fun initializeCake() {
cakeSize = height / 8
moveCakeToStartPoint()
}
private fun moveCakeToStartPoint() {
// Выбираем случайную позицию справа за экраном
cakeRect.left = width + width * Random.nextFloat()
cakeRect.right = cakeRect.left + cakeSize
// Случайно выбираем полосу сверху или снизу
val isTopLine = Random.nextBoolean()
cakeRect.top = getObjectYTopForLine(cakeSize, isTopLine).toFloat()
cakeRect.bottom = cakeRect.top + cakeSize
}
// Двигаем тортик относительно прошедшего времени от прошлого кадра
private fun moveCake() {
val currentTime = System.currentTimeMillis()
val deltaTime = currentTime - previousTimestamp
val deltaX = cakeSpeed * width * deltaTime
cakeRect.left -= deltaX
cakeRect.right = cakeRect.left + cakeSize
previousTimestamp = currentTime
}
// Если тортик и игрок пересекаются, то прибавляем очки
private fun checkPlayerCaughtCake() {
if (RectF.intersects(playerRect, cakeRect)) {
score += if (flags.isLeftEyeOpen && flags.isRightEyeOpen) 1 else 2
moveCakeToStartPoint()
}
}
// Если игрок пропустил тортик, то возвращаем тортик на стартовую позицию
private fun checkCakeIsOutOfScreenStart() {
if (cakeRect.right < 0) {
moveCakeToStartPoint()
}
}
Что получилось
Показ баллов сделаем очень простым. Будем выводить число в центре экрана. Нужно только учесть высоту текста и сделать отступ сверху для красоты.
private val scorePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.GREEN
textSize = context.resources.getDimension(R.dimen.score_size)
}
private var score: Int = 0
private var scorePoint = PointF()
private fun initializeScore() {
val bounds = Rect()
scorePaint.getTextBounds("0", 0, 1, bounds)
val scoreMargin = resources.getDimension(R.dimen.score_margin)
scorePoint = PointF(width / 2f, scoreMargin + bounds.height())
score = 0
}
Смотрим, какую игрушку мы сделали:
Извините, данный ресурс не поддреживается. :(
Полный diff шага.
Графоний
Чтобы игру было не стыдно показывать на презентации хакатона, добавим немного графония!
Картинки
Исходим из того, что рисовать впечатляющую графику мы не умеем. К счастью, есть сайты с бесплатными ассетами для игр. Мне понравился вот этот, хотя сейчас он недоступен напрямую по неизвестной мне причине.
Анимация
Мы рисуем на Canvas, а значит, анимацию нужно реализовывать самим. Если есть картинки с анимацией, запрограммировать это будет легко. Вводим класс для объекта со сменяющимися изображениями.
class AnimatedGameObject(
private val bitmaps: List<Bitmap>,
private val duration: Long
) {
fun getBitmap(timeInMillis: Long): Bitmap {
val mod = timeInMillis % duration
val index = (mod / duration.toFloat()) * bitmaps.size
return bitmaps[index.toInt()]
}
}
Чтобы получился эффект движения, фон тоже должен быть анимированным. Иметь серию кадров фона в памяти — накладная история. Поэтому поступим хитрее: одно изображение будем рисовать со сдвигом по времени. Схема идеи:
Полный diff шага.
Финальный результат
Сложно назвать это шедевром, но для прототипа за вечер сойдёт. Код можно найти тут. Запускается локально без дополнительных махинаций.
Извините, данный ресурс не поддреживается. :(
В заключение добавлю, что ML Kit Face Detection может пригодиться и для других сценариев.
Например, чтобы делать идеальные селфи с друзьями: можно анализировать всех людей в кадре и убеждаться, что все улыбнулись и открыли глаза. Определение нескольких лиц в видеопотоке работает из коробки, поэтому задача несложная.
Используя распознавание контуров лица из модуля Face Detection, реально повторить маски, которые сейчас популярны почти во всех приложениях с камерой. А если добавить интерактив — через определение улыбки и подмигивания, — то пользоваться ими будет вдвойне весело.
Эту функциональность — определение контуров лица — можно применять не только для развлечений. Те, кто сами пытались вырезать фото на документы, оценят. Берём контур лица, автоматически вырезаем фото с нужным соотношением сторон и правильным положением головы. Определить правильный угол съёмки поможет датчик гироскопа.
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка игр, Логические игры] Как мы турнир провели
- [Разработка игр, Локализация продуктов, Монетизация игр, Продвижение игр] 5 ключиков к игровому рынку Бразилии
- [Хакатоны, Конференции] Digital-мероприятия в Москве c 10 по 16 августа
- [Разработка игр, C#, Unity] Управление сценами в Unity без боли и страданий
- [Разработка мобильных приложений, Разработка под Android] Top 5 Android App Development Companies To Seek in 2020
- [Программирование, Разработка мобильных приложений, Проектирование и рефакторинг, Управление разработкой] Какие навыки можно прокачать на проекте c большой кодовой базой
- [Разработка под iOS, Разработка под Android, Смартфоны, Накопители] Почему iPhone хватает 4 ГБ ОЗУ, а Android — нет?
- [Программирование, Беспроводные технологии, Хакатоны, Интервью, IT-компании] Победитель хакатона: права на цифровое решение остались за нами
- [Разработка под iOS, Разработка под Android, Swift, Дизайн мобильных приложений] FigmaExport: как автоматизировать экспорт UI-Kit из Figma в Xcode и Android Studio проекты
- [Java, Разработка под Android, Kotlin, Системы сборки] Композитная сборка как альтернатива buildSrc в Gradle
Теги для поиска: #_razrabotka_igr (Разработка игр), #_razrabotka_pod_android (Разработка под Android), #_hakatony (Хакатоны), #_android, #_hakaton (хакатон), #_ml_kit, #_blog_kompanii_vkontakte (
Блог компании ВКонтакте
), #_razrabotka_igr (
Разработка игр
), #_razrabotka_pod_android (
Разработка под Android
), #_hakatony (
Хакатоны
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 22:19
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Привет! Меня зовут Иван Шафран, недавно я присоединился к команде видео ВКонтакте в роли программиста-разработчика для Android. Участвую в создании как продуктовых приложений, так и SDK. Время от времени я посещаю хакатоны, где можно реализовывать любые безумные идеи. Сегодня расскажу, как за пару часов сделать прототип мобильной игры с необычным управлением: персонаж будет реагировать на улыбку и подмигивание. оригинал Как возникла идея Мысль создать такую игру пришла как раз во время хакатона. Формат предполагал, что на разработку есть один рабочий день, то есть 8 часов. Чтобы успеть сделать прототип, я выбрал Android SDK. Возможно, лучше подошли бы игровые движки, но в них я не разбираюсь. Концепцию управления с помощью эмоций подсказала другая игра: там движения персонажа можно было задавать, меняя громкость своего голоса. Может, и эмоции уже кто-то использовал в игровом управлении. Но я знаю мало таких примеров, поэтому остановился на этом формате. Осторожно громкое видео!
Извините, данный ресурс не поддреживается. :( Настраиваем окружение для разработки Нам понадобится только Android Studio на компьютере. Если нет реального устройства на Android для запуска, можно воспользоваться эмулятором с включённой веб-камерой. Создаём проект с ML Kit ML Kit — отличный инструмент, который поможет впечатлить жюри хакатона: ведь вы используете AI в прототипе! А вообще он помогает встраивать в проекты решения на основе машинного обучения, например функциональность для определения объектов в кадре, перевода и распознавания текста. Для нас важно, что у ML Kit есть бесплатный offline API для распознавания улыбки и открытых или закрытых глаз. Раньше, чтобы создать любой проект с ML Kit, нужно было сначала зарегистрироваться в консоли Firebase. Теперь этот шаг можно пропустить для офлайн-функциональности. Android-приложение Удаляем лишнее Чтобы не писать логику по работе с камерой с нуля, возьмём официальный семпл и уберём из него то, что нам не нужно. Для начала скачайте пример и попробуйте запустить. Исследуйте режим Face detection: выглядеть это будет, как на превью статьи. Манифест Начнём правки с AndroidManifest.xml. Удалим все теги activity, кроме первого. А на его место выставим CameraXLivePreviewActivity, чтобы сразу запускаться с камеры. В значении атрибута android:value оставляем только face, чтобы исключить из APK ненужные нам ресурсы. <meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="face"/> <activity android:name=".CameraXLivePreviewActivity" android:exported="true" android:theme="@style/AppTheme"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> Полный diff шага. Камера Сэкономим время — не будем удалять лишние файлы, вместо этого сконцентрируемся на элементах экрана CameraXLivePreviewActivity.
На этом можно остановиться. Но если отрисовка FPS и сетка лица визуально отвлекают, то выключить их можно так:
Полный diff шага. Распознаём эмоции Распознавание улыбки по умолчанию выключено, но запустить его очень просто. Не зря же мы брали пример кода за основу! Выделим необходимые нам параметры в отдельный класс и объявим интерфейс слушателя: FaceDetectorProcessor.javaSPL// В классе FaceDetectorProcessor.java
public class FaceDetectorProcessor extends VisionProcessorBase<List<Face>> { public static class Emotion { public final float smileProbability; public final float leftEyeOpenProbability; public final float rightEyeOpenProbability; public Emotion(float smileProbability, float leftEyeOpenProbability, float rightEyeOpenProbability) { this.smileProbability = smileProbability; this.leftEyeOpenProbability = leftEyeOpenProbability; this.rightEyeOpenProbability = rightEyeOpenProbability; } } public interface EmotionListener { void onEmotion(Emotion emotion); } private EmotionListener listener; public void setListener(EmotionListener listener) { this.listener = listener; } @Override protected void onSuccess(@NonNull List<Face> faces, @NonNull GraphicOverlay graphicOverlay) { if (!faces.isEmpty() && listener != null) { Face face = faces.get(0); if (face.getSmilingProbability() != null && face.getLeftEyeOpenProbability() != null && face.getRightEyeOpenProbability() != null) { listener.onEmotion(new Emotion( face.getSmilingProbability(), face.getLeftEyeOpenProbability(), face.getRightEyeOpenProbability() )); } } } } Чтобы включить классификацию эмоций, настроим FaceDetectorProcessor в классе CameraXLivePreviewActivity и подпишемся на получение состояния эмоций. Затем вероятности преобразуем в булевы флаги. Для тестирования в вёрстку добавим TextView, в котором покажем эмоции через смайлы. Полный diff шага. Разделяй и играй Раз мы делаем игру, нужно место для рисования элементов. Будем считать, что она запускается на телефоне в портретном режиме. Значит, разделим экран на две части: камера сверху и игра снизу. Контролировать персонажа с помощью улыбки сложно, к тому же на хакатоне мало времени для реализации продвинутой механики. Поэтому наш персонаж будет собирать ништяки по дороге, находясь либо в верхней части игрового поля, либо в нижней. Действия с закрытыми или открытыми глазами добавим как усложнение игры: поймали ништяк с закрытым глазом — очки удваиваются (либо пол-экрана не видно и можно грабить корованы). Если хотите реализовать другой игровой процесс, то могу подсказать несколько занятных вариантов:
Отображать игру будем в кастомном Android View — там в методе onDraw нарисуем персонажа на Canvas. В первом прототипе ограничимся геометрическими примитивами. Игрок Наш персонаж — это квадрат. При инициализации зададим его размеры и установим положение слева, так как он будет находиться на месте. Позиция по оси Y будет зависеть от улыбки игрока. Все абсолютные значения будем высчитывать относительно размеров области игры. Это проще, чем подбирать конкретные размеры, — да и на новых устройствах получим приемлемый вид. private var playerSize = 0
private var playerRect = RectF() // Инициализируем размеры в зависимости от высоты View private fun initializePlayer() { playerSize = height / 4 playerRect.left = playerSize / 2f playerRect.right = playerRect.left + playerSize } // Имеем в полях класса флаги эмоций private var flags: EmotionFlags // Устанавливаем положение в зависимости от улыбки private fun movePlayer() { playerRect.top = getObjectYTopForLine(playerSize, isTopLine = flags.isSmile).toFloat() playerRect.bottom = playerRect.top + playerSize } // Получаем позицию top для объекта с высотой size, // чтобы он был посередине верхней или нижней дорожки private fun getObjectYTopForLine(size: Int, isTopLine: Boolean): Int { return if (isTopLine) { width / 2 - width / 4 - size / 2 } else { width / 2 + width / 4 - size / 2 } } // Храним paint в полях класса, так как создавать его на каждый кадр накладно private val playerPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL color = Color.BLUE } // Рисуем наш квадрат на Canvas private fun drawPlayer(canvas: Canvas) { canvas.drawRect(playerRect, playerPaint) } Тортик Наш персонаж «бежит» и пытается ловить тортики, чтобы набрать как можно больше очков. Мы используем стандартный приём с переходом в систему отсчёта относительно игрока: он будет стоять на месте, а тортики — лететь к нему. Если квадрат тортика пересекается с квадратом игрока, то засчитываем балл. А если при этом хотя бы один глаз у пользователя закрыт — два балла ¯ \ _ ( ツ ) _ / ¯ Также в нашей вселенной будет всего один электрон тортик. Как только персонаж его съедает, он перемещается за экран на случайную полосу со случайной координатой. Так улыбка игрока не войдёт в резонанс при предсказуемом появлении тортика. // При инициализации тортика сразу перемещаем его за экран
private fun initializeCake() { cakeSize = height / 8 moveCakeToStartPoint() } private fun moveCakeToStartPoint() { // Выбираем случайную позицию справа за экраном cakeRect.left = width + width * Random.nextFloat() cakeRect.right = cakeRect.left + cakeSize // Случайно выбираем полосу сверху или снизу val isTopLine = Random.nextBoolean() cakeRect.top = getObjectYTopForLine(cakeSize, isTopLine).toFloat() cakeRect.bottom = cakeRect.top + cakeSize } // Двигаем тортик относительно прошедшего времени от прошлого кадра private fun moveCake() { val currentTime = System.currentTimeMillis() val deltaTime = currentTime - previousTimestamp val deltaX = cakeSpeed * width * deltaTime cakeRect.left -= deltaX cakeRect.right = cakeRect.left + cakeSize previousTimestamp = currentTime } // Если тортик и игрок пересекаются, то прибавляем очки private fun checkPlayerCaughtCake() { if (RectF.intersects(playerRect, cakeRect)) { score += if (flags.isLeftEyeOpen && flags.isRightEyeOpen) 1 else 2 moveCakeToStartPoint() } } // Если игрок пропустил тортик, то возвращаем тортик на стартовую позицию private fun checkCakeIsOutOfScreenStart() { if (cakeRect.right < 0) { moveCakeToStartPoint() } } Что получилось Показ баллов сделаем очень простым. Будем выводить число в центре экрана. Нужно только учесть высоту текста и сделать отступ сверху для красоты. private val scorePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.GREEN textSize = context.resources.getDimension(R.dimen.score_size) } private var score: Int = 0 private var scorePoint = PointF() private fun initializeScore() { val bounds = Rect() scorePaint.getTextBounds("0", 0, 1, bounds) val scoreMargin = resources.getDimension(R.dimen.score_margin) scorePoint = PointF(width / 2f, scoreMargin + bounds.height()) score = 0 } Смотрим, какую игрушку мы сделали: Извините, данный ресурс не поддреживается. :( Полный diff шага. Графоний Чтобы игру было не стыдно показывать на презентации хакатона, добавим немного графония! Картинки Исходим из того, что рисовать впечатляющую графику мы не умеем. К счастью, есть сайты с бесплатными ассетами для игр. Мне понравился вот этот, хотя сейчас он недоступен напрямую по неизвестной мне причине. Анимация Мы рисуем на Canvas, а значит, анимацию нужно реализовывать самим. Если есть картинки с анимацией, запрограммировать это будет легко. Вводим класс для объекта со сменяющимися изображениями. class AnimatedGameObject(
private val bitmaps: List<Bitmap>, private val duration: Long ) { fun getBitmap(timeInMillis: Long): Bitmap { val mod = timeInMillis % duration val index = (mod / duration.toFloat()) * bitmaps.size return bitmaps[index.toInt()] } } Чтобы получился эффект движения, фон тоже должен быть анимированным. Иметь серию кадров фона в памяти — накладная история. Поэтому поступим хитрее: одно изображение будем рисовать со сдвигом по времени. Схема идеи: Полный diff шага. Финальный результат Сложно назвать это шедевром, но для прототипа за вечер сойдёт. Код можно найти тут. Запускается локально без дополнительных махинаций. Извините, данный ресурс не поддреживается. :( В заключение добавлю, что ML Kit Face Detection может пригодиться и для других сценариев. Например, чтобы делать идеальные селфи с друзьями: можно анализировать всех людей в кадре и убеждаться, что все улыбнулись и открыли глаза. Определение нескольких лиц в видеопотоке работает из коробки, поэтому задача несложная. Используя распознавание контуров лица из модуля Face Detection, реально повторить маски, которые сейчас популярны почти во всех приложениях с камерой. А если добавить интерактив — через определение улыбки и подмигивания, — то пользоваться ими будет вдвойне весело. Эту функциональность — определение контуров лица — можно применять не только для развлечений. Те, кто сами пытались вырезать фото на документы, оценят. Берём контур лица, автоматически вырезаем фото с нужным соотношением сторон и правильным положением головы. Определить правильный угол съёмки поможет датчик гироскопа. =========== Источник: habr.com =========== Похожие новости:
Блог компании ВКонтакте ), #_razrabotka_igr ( Разработка игр ), #_razrabotka_pod_android ( Разработка под Android ), #_hakatony ( Хакатоны ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 22:19
Часовой пояс: UTC + 5