Как написать кликер для андроид

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

Разработка кликера

Сегодня мы с вами пошагово разработаем кликер, суть которого заключается в создании тортиков. Это будет не просто счетчик кликов, а полноценный кликер с выпеканием тортов, магазином и всеми вытекающими.

Спрайты для урока:

Создаем торт

Начнем мы с вами с главного, с торта. Торт у нас будет собираться послойно, у каждого слоя будет цена — сколько кликов нужно сделать, чтобы собрать определенный слой.

Торты у нас будут заранее созданы и храниться в префабах. Чтобы сделать первый торт — создайте на сцене пустой объект и назовите его Cake. После этого создайте папку Prefabs в окне Asset, а в ней Cakes. Перетащите с окна Hierarchy созданный пустой объект торта в папку Cakes.

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

Размещаем слой торта, и устанавливаем размер по Y 1.5

После этого дочерним к слою располагаем крем и выставляем ему Scale 1 по всем осям. После установки, раскрашиваем слой и крем.

Первый уровень торта готов, но хотелось бы сделать его минимум из 3 уровней. Выделяем слой, и нажимаем ctrl+d, мы получили дубликат слоя, сдвигаем его повыше, и видим следующую картину:

Тут проблема в слоях. Вам нужно вручную проставить Order in layer для каждого спрайта в торте. Получается, что у нашего 1 уровня будет слой 0, а у крема 1. Далее всё зависит от того, сколько вам нужно будет уровней для торта, но каждый последующий слой будет больше.

В итоге у нас получится вот такой торт. Хотел бы обратить внимание на пару вещей.

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

Также для упрощения понимания, приведу структуру полученного торта и какие слои у каждого элемента.

Структура торта

Как вы видите, у нас есть 3 коржа. У каждого коржа есть крем, а у последнего ещё и конфети. Распределение слоев идет следующим образом:

  • корж 1 — слой 0;
  • крем 1 — слой 1;
  • корж 2 — слой 2;
  • крем 2 — слой 3;
  • корж 3 — слой 4;
  • крем 3 — слой 5;
  • все конфети — слой 6.

Теперь наш торт окончательно готов, и т.к. пустой GameObject торта находился на сцене, то сейчас там появится целый торт.

Добавляем фон

Фон мы с вами расположим на Canvas, для этого нажимаем правой кнопкой мыши по окну иерархии и создаем Canvas.

Canvas нужен для отрисовки графического интерфейса, но также на нем можно расположить и фон. Перед работой нам нужно его настроить.
Первым делом переключаем Render Mode в Camera и указываем камеру, к которой будет привязан канвас, т.к. у нас она одна, то её и указываем.
После этого нам нужно указать настройки для адаптивности нашего интерфейса, для этого в Scale Mode выбираем Scale With Screen Size. Этот режим означает, что мы разработаем весь интерфейс для определенного эталонного разрешения, а дальше unity будет его растягивать отталкиваясь от этого. Указываем Reference Resolution 1080 на 1920 и Match 0.5, mathc — это то, как будет тянуться интерфейс, по ширине или по высота, в случае с 0.5, это по ширине и высоте одновременно.

Канвас настроен, теперь нажимаем пкм по канвасу в окне иерархии, и создаем объект Panel, он будет являться нашим фоном, указываем в Source Image картинку нашего заднего фона. Теперь фон указан, но он какой-то темный, чтобы убрать это затемнение, вам следует нажать лкм на color и параметр A(alpha) установить в 255. Теперь ваш фон смотрится адекватно.

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

Создаем место для торта

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

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

Теперь, наконец-то, перейдем к скриптам)

Создаем папку для скриптов, и в неё уже создаем скрипт CakePlace.

Пока наш скрипт будет выглядеть так, мы будем постепенно его дописывать. Давайте разберем, что он делает сейчас. У нас есть поле торт, в котором будет храниться тот торт, который мы хотим заспаунить. В старте мы вызываем метод SetCake, который просто создает торт дочерним к нам. Также уже готов метод RemoveCake, который будет удалять торт.

Этот компонент мы добавляем к объекту CakePlace. Удаляем со сцены сам торт и в поле Cake указываем префаб нашего торта.

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

Скрипты торта

В папке скрипты создаем папку Cake, а в ней 2 скрипта: Cake и CakeLayer.

CakeLayer

У нас есть следующие поля:

  • _clicksBeforeCooking — сколько кликов нужно, чтобы слой был готов;
  • _spriteRenderer — хранит компонент отрисовки этого слоя;
  • _layerColor — цвет нашего слоя поле выпекания;
  • CookingProgress — свойство, которое обозначает прогресс, сколько кликов уже сделано.

Зачем нам хранить цвет слоя? Дело в том, что нам надо как-то помечать то, что слой не выпечен, и пользователь видел, что он сейчас выпекает. Для этого мы каждый слой в старте превращаем в призрачный — белый слой с небольшой прозрачностью.

Как торт выпекается?

Каждый раз при клике, прогресс будет увеличиваться. Когда прогресс достигнет нужного количества кликов для выпекания, тогда цвет заменится на нормальный, а метод вернет true.

Этот компонент нам нужно добавить на каждый элемент торта.

Cake

Начнем разбор с полей:

  • _profit — прибыль которую мы получим за выпекание торта;
  • _layers — все наши слои;
  • _createdLayers — сколько слоев уже создано;
  • Done — свойство, которое возвращает результат сравнения выпеченных слоев и общего количества.

На старте мы получаем с наших детей все компоненты CakeLayer, чтобы мы могли контролировать послойное выпекание.

OnClick будет вызываться каждый раз при кликах по экрану.
Если торт не готов, то мы вызываем метод TryBakeLayer, который пытается испечь слой. В нем мы получаем текущий слой и вызываем у него увеличение прогресса, после чего пытаемся его завершить (этот if можно было заменить на return cakeLayer.TryCookLayer();).

После чего снова происходит проверка на то, готов ли торт.

Также после 42 строки добавьте следующую строку:

_createdLayers++;

Это нужно, чтобы мы перешли к приготовлению следующего слоя

Настройка торта

Теперь нужно закинуть эти компоненты на торт.
На основной объект помещаем компонент Cake, а на все слои помещаем компонент CakeLayer

Покликовое создание торта

Основной частью кликера является считывание кликов. Считывать клики мы с вами будем по панельке канваса.
Нажимаем пкм по канвасу и создаем Panel. Он сразу будет серым, нажимаем Color и A уводим в 0.

Также сейчас наша зона кликов находится под фоном, а значит клики до неё не дойдут, тут вам следует убрать галочку Raycast Target с фона

Скрипт Clicker Zone

Мы наследуем наш компонент от интерфейса IPointerClickHandler, который позволяет нам определять клики по нашей панели. Также у нас есть событие клик, которое мы пробуждаем во время клика по нашей панели.

Получается такая схема: клик по панели -> пробуждение события -> все, кто подписаны, выполняют определенное действие.

Этот компонент мы добавляем на наш пустой объект ClickerZone.

Модифицируем скрипт CakePlace

Мы добавили поле ClickerZone, чтобы иметь возможность подписать наш торт на событие о клике, а также добавили подписку торта на клики при создании, и отписку при удалении.

Мы добавили поле, не забудьте указать в него ClickerZone.

Теперь мы можем покликово собирать тор!

Сбор тортов и зачисление очков на баланс

Создаем в папке скриптов папку Player, а в ней скрипты Player и PlayerWallet

Тут в целом все просто, у нас методы на пополнение и списание, а также приватное поле и свойство которое его раскрывает

А игрок просто содержит кошель.

Эти компоненты можно разместить на камере или создать пустой объект Player на сцене.

Теперь нужно собрать панельку, на которую будет кликать игрок для сбора торта. Для этого кликаем пкм по канвасу и создаем панель. Эту панель сужаем до небольшого окна, добавляем на неё текст и кнопку, чтобы итоговый результат выглядел следующим образом:

Также чтобы мы могли скрывать и показывать сразу всю группу элементов(панель, текст и кнопку) на родительский объект CakeCollector следует добавить CanvasGroup, он позволяет контролировать сразу всех детей.

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

Обновляем скрипт Cake

Тут мы просто добавляем событие, которое пробуждаем в момент когда торт готов.

Обновляем скрипт CakePlace

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

Cкрипт CakeCollector

Теперь создаем папку UI в скриптах и добавляем скрипт CakeCollector.

Получаем торт по событию и сразу открываем панель, после чего ожидаем сбора, а потом закрываем.

Дополняем скрипт Player

Просто подписываемся на событие о сборе и добавляем столько денег, сколько приносит торт.

Теперь у нас готов сбор тортиков

Отображение баланса

Первым делом создаем панель на канвасе, указываем показанное выше изображение и добавляем текст.

Далее мы создаем скрипт WalletDisplay и добавляем событие в PlayerWallet.

Обновляем PlayerWallet

Просто добавляем событие о том, что деньги обновились

WalletDisplay

Тут мы подписываемся на обновление кошелька и обновляем данные при добавлении денег.

Теперь за торт с ценой 5 мы получили 5 валюты.

Диспенсер тортов

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

У нашего компонента есть поля:

  • _cakeCollector — нужен нам для того, чтобы знать, что старый торт собран;
  • _cakePlace — нужен, чтобы выдавать ему торты;
  • _cakeTemplates — все доступные варианты тортов.

Мы получаем от сборщика информацию о том, что торт собран, удаляем этот торт с поля, а потом берем рандомное число от 0 до количества шаблонов и выдаем его на установку в CakePlace.

Теперь каждый раз после нажатия нам будет выдаваться новый торт.

Создание внутриигрового магазина

Первым делом создаем кнопку для магазина и размещаем в правом верхнем углу. Далее создаем скрипт ShopButton и Shop.

По нажатию на кнопку открытия (которую разместили в правом верхнем углу), мы включаем объект магазина, а при нажатии на кнопку закрытия (разместим внутри магазина) — выключаем объект.

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

После этого добавляем ScrollView, он нужен для расположения множества элементов.

Далее растягиваем его и создаем шаблон для товара.

Добавляем в Content шаблон Item, дублируем пару раз и добавляем компонент Horizontal Layout Group на content, это позволит ровно расположить множество элементов.

Мы разработали с вами шаблон для магазина, осталось создать товары.
Все варианты товаров мы будем хранить как SO.

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

После этого создаем сам объект торта, который уже в свою очередь ссылается на определенный SO

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

А также событие о том, что куплен новый торт, оно будет оповещать раздатчик о том, что появился новый шаблон.

Теперь приступаем к магазину

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

Шкала прогресса выпекания

Шкала прогресса слоя будет создана из простого слайдера. Нажимаем пкм по иерархии и создаем слайдер, после чего удаляем ручку из него и растягиваем части, которые не полностью заполняют его (чтобы полностью заполнить слайдер, выделите объект и слайдер value перетяните в правый край).

Также нам понадобится интересный скрипт

При обновлении прогресса, мы устанавливаем новый targetProgress и targetColor. Как вы можете заметить, Lerp применяется не для плавного перехода, а для получения промежуточного цвета.

В Update же мы просто стремимся к target значениям.

Этот прогресс осталось как-то передать, чем мы с вами и займемся.

В Cake мы создадим событие, которое будет сообщать нам о том, каков прогресс у нашего торта.

Мы добавляем ссылку на прогресс бар в CakePlace, и добавляем подписку и отписку на торт для нашего слайдера.

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

Надеюсь вам понравилась эта статья, пишите в комментариях, что ещё стоит разобрать!

Часто мобильные разработчики не хотят браться за создание игр, потому что это кажется долгим и трудоёмким процессом. Это работа с памятью и спрайтами, сложные UI/UX и много оптимизации. И если вы уже разрабатывали приложения под Android, то наверняка представляете, как было бы трудно создавать игры на нативном коде. Именно по этой причине стоить задуматься о разработке на Flutter — SDK от Google для создания кроссплатформенных приложений.

На GitHub есть пример игры Spaceblast, созданной на этом SDK.

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

Разработка игры

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

GestureDetector(
 onTapDown: (TapDownDetails details) => damage(details),
)
var _bossDamage = 980;

void damage(TapDownDetails details) {
 setState(() {
   _bossDamage = _bossDamage - 30 <= 0 ? 980 : _bossDamage - 30;
 });
}

Вот что должно получиться после добавления визуальных элементов:

Следующим шагом будет добавление UI-элементов, которые сделают игру более приятной. Потребуются спрайты персонажа, боссов (которым будет наноситься урон), бонусов и монет.

Для анимации персонажа используется всего 2 изображения. Первый кадр — для состояния покоя, второй — для атаки. При нажатии на экран эти изображения будут переключаться между собой. Это создаст визуальный эффект удара.

String hero() {
 return tap ? "assets/character/attack.png" : "assets/character/idle.png";
}

Для боссов же понадобится больше изображений. Это сделает игру более разнообразной и интересной.

static List getBosses() {
 var list = List();
 list.add(Bosses("Lunabi", 450, "assets/boss/boss_one.png"));
 list.add(Bosses("ivygrass", 880, "assets/boss/boss_two.png"));
 list.add(Bosses("Tombster", 1120, "assets/boss/boss_three.png"));
 list.add(Bosses("Glidestone", 2260, "assets/boss/boss_four.png"));
 list.add(Bosses("Smocka", 2900, "assets/boss/boss_five.png"));
 list.add(Bosses("Clowntorch", 4100, "assets/boss/boss_six.png"));
 list.add(Bosses("Marsattack", 5380, "assets/boss/boss_seven.png"));
 list.add(Bosses("Unknown", 7000, "assets/boss/boss_eight.png"));
 list.add(Bosses("ExArthur", 10000, "assets/boss/boss_nine.png"));
 return list;
}

У каждого босса есть имя, показатель жизни и спрайт.

Для бонусов код аналогичный:

static List getPowerUps() {
 var list = List();
 list.add(PowerUps("Master Sword", 2.15, false, 50));
 list.add(PowerUps("Lengendary Sword", 2.45, false, 180));
 list.add(PowerUps("Keyblade", 3.75, false, 300));
 list.add(PowerUps("Lightsaber", 4.95, false, 520));
 list.add(PowerUps("Buster Sword", 6.15, false, 1700));
 list.add(PowerUps("Soul Edge", 8.65, false, 2400));
 return list;
}

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

После этого можно добавить визуальный «хитбокс», который будет отображаться при каждом нажатии на экран:

Widget hitBox() {
 if (tap) {
   return Positioned(
     top: yAxis,
     left: xAxis,
     child: Column(
       children: [
         Padding(
           padding: const EdgeInsets.only(bottom: 20.0),
           child: Material(
             color: Colors.transparent,
             child: StrokeText(
               "-${damageUser.toInt().toString()}",
               fontSize: 14.0,
               fontFamily: "Gameplay",
               color: Colors.red,
               strokeColor: Colors.black,
               strokeWidth: 1.0,
             ),
           ),
         ),
         Image.asset(
           "assets/elements/hit.png",
           fit: BoxFit.fill,
           height: 80.0,
           width: 80.0,
         ),
       ],
     ),
   );
 } else {
   return Container();
 }
}

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

При прикосновении используется параметр TapDownDetails для получения X и Y координат пальца.

Ещё нужно добавить логику скрытия этих элементов после того, как игрок отведёт палец от экрана.

GestureDetector(
 onTapDown: (TapDownDetails details) => damage(details),
 onTapUp: (TapUpDetails details) => hide(null),
 onTapCancel: () => hide(null),
),

В добавлении списка бонусов тоже нет ничего сложного:

ListView.builder(
 padding: EdgeInsets.only(bottom: 20.0, left: 10.0, right: 10.0),
 itemCount: list.length,
 itemBuilder: (context, position) {
   PowerUps powerUp = list[position];
   int bgColor = !powerUp.bought && coins >= powerUp.coins
       ? 0xFF808080
       : !powerUp.bought ? 0xFF505050 : 0xFF202020;

   return swordElement(bgColor, powerUp, position);
 },
)

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

Widget swordElement(int bgColor, PowerUps powerUp, int position) {
 return Padding(
   padding: const EdgeInsets.symmetric(
     vertical: 5.0,
   ),
   child: Container(
     height: 70,
     child: Card(
       color: Color(bgColor),
       child: Row(
         children: [
           Expanded(
             child: Padding(
               padding: const EdgeInsets.symmetric(horizontal: 20.0),
               child: Text(
                 powerUp.name,
                 style: Utils.textStyle(11.0),
               ),
             ),
           ),
           Padding(
             padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 20.0),
             child: FancyButton(
               size: 20,
               child: Row(
                 children: [
                   Padding(
                     padding: const EdgeInsets.only(left: 10.0, bottom: 2, top: 2),
                     child: Text(
                       !powerUp.bought ? "BUY" : "BOUGHT",
                       style:
                       Utils.textStyle(13.0, color: !powerUp.bought ? Colors.white : Colors.grey),
                     ),
                   ),
                   Padding(
                     padding: EdgeInsets.only(left: 8.0, right: !powerUp.bought ? 2.0 : 0.0),
                     child: Text(
                       !powerUp.bought ? powerUp.coins.toString() : "",
                       style: Utils.textStyle(13.0),
                     ),
                   ),
                   coinVisibility(powerUp.bought),
                 ],
               ),
               color: !powerUp.bought && coins >= powerUp.coins
                   ? Colors.deepPurpleAccent
                   : Colors.deepPurple,
               onPressed: !powerUp.bought && coins >= powerUp.coins ? () => buyPowerUp(position) : null,
             ),
           )
         ],
       ),
     ),
   ),
 );
}

На текущем моменте игра выглядит так:

Звуки и музыка

Звуки играют очень важную роль в играх. Добавим их:

AudioPlayer hitPlayer;
AudioCache hitCache;
hitPlayer = AudioPlayer();
hitCache = AudioCache(fixedPlayer: hitPlayer);
// Если аудио ещё проигрывается, то его нужно остановить и проиграть заново
hitPlayer.pause();
hitCache.play('audio/sword.mp3');

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

AudioCache musicCache;
AudioPlayer instance;
void playMusic() async {
 musicCache = AudioCache(prefix: "audio/");
 instance = await musicCache.loop("bgmusic.mp3");
}

Звук будет проигрываться из InitState и будет остановлен из метода Dispose. Если присмотреться, то в игре можно увидеть кастомные шрифты — они не обязательны, но делают игру более ламповой.

Экран приветствия

В каждой игре должен быть экран приветствия. Он даёт игроку некую подсказку о том, что ждёт его в игре.

Тут экран приветствия был создан из спрайта босса, игрока и фонового изображения с логотипом.

Ещё в экран приветствия был добавлен эффект Blur (размытие) и немного частиц огня и пепла. Код эффекта выглядит так:

BackdropFilter(
 filter: ImageFilter.blur(
   sigmaX: 4.0,
   sigmaY: 4.0,
 ),
 child: Align(
   alignment: Alignment.bottomCenter,
   child: Align(
     alignment: Alignment.topCenter,
     heightFactor: heroYAxis,
     child: Image.asset(
       heroAsset(),
       width: size / 1.5,
       height: size / 1.5,
       fit: BoxFit.cover,
     ),
   ),
 ),
)

Код частиц можно найти в этом репозитории.

Вот так выглядит итоговый результат:

Добавление механик в игру

По желанию можно усложнить игру, сделав её разнообразнее и интереснее. Например, можно добавить ограничение по времени. Оставшееся время будет увеличиваться при каждом убийстве босса. Ещё можно дать возможность поделиться результатом в соц. сетях.

Для отсчёта времени используется обычный AnimationController. Он отсчитывает прошедшее время в миллисекундах, пока не достигнет 0. Если вам нужно форматировать время для строки, то используйте этот фрагмент кода:

String get timerString {
 Duration duration = controller.duration * controller.value;
 return '$ {(duration.inMinutes) .toString (). padLeft (2,' 0 ')}: $ {(duration.inSeconds% 60) .toString (). padLeft (2,' 0 ')}';
}

Если счётчик достигнет 0, то будет активироваться «Game over»:

controller.addStatusListener ((status) {
 if (status == AnimationStatus.dismissed) {
   setState (() {
     gameOver = true ;
   });
 }
});

Поддержка геймпада

Это экспериментальная функция, поэтому с ней может происходить всё, что угодно.

Пока можно попробовать варианты Android + Switch Pro Controller (PS4 и Xbox Controller остаются в стороне).

Для работы с геймпадом нужно использовать каналы. Вначале код для Android Kotlin:

var channel =  MethodChannel(flutterView, "gamepad")
channel.setMethodCallHandler { call, result ->
   when {
       call.method == "isGamepadConnected" -> {
         val ids = InputDevice.getDeviceIds()
         for (id in ids) {
           val device = InputDevice.getDevice(id)
           val sources = device.sources

           if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {
             result.success(true)
           }
         }
         result.success(false)
       }
       call.method == "getGamePadName" -> {
         val gamepadIds = InputDevice.getDeviceIds()
         for (id in gamepadIds) {
           val device = InputDevice.getDevice(id)
           val sources = device.sources

           if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {
             result.success(device.name)
           }
         }
       }
       else -> result.notImplemented()
   }
 }
}

Его можно разместить внутри класса MainActivity. Ещё вы можете реализовать интерфейс InputManager.InputDeviceListner для обнаружения подключения/отключения геймпада:

var inputManager = getSystemService(Context.INPUT_SERVICE) as InputManager
override fun onResume() {
 super.onResume()
 inputManager.registerInputDeviceListener(this, null)
}

override fun onPause() {
 super.onPause()
 inputManager.unregisterInputDeviceListener(this)
}

override fun onInputDeviceRemoved(deviceId: Int) {
 channel.invokeMethod("gamepadRemoved", true)
}

override fun onInputDeviceAdded(deviceId: Int) {
 channel.invokeMethod("gamepadName", InputDevice.getDevice(deviceId).name)
}

override fun onInputDeviceChanged(deviceId: Int) {
 channel.invokeMethod("gamepadName", InputDevice.getDevice(deviceId).name)
}

При каждом из этих событий Android будет отправлять данные в канал:

static const MethodChannel _channel = const MethodChannel('gamepad');

static Future get isGamePadConnected async {
 final bool isConnected = await _channel.invokeMethod('isGamepadConnected');
 return isConnected;
}

static Future get gamepadName async {
 final String name = await _channel.invokeMethod("getGamePadName");
 return name;
}

Этот код будет обрабатывать запрос на получение названия устройства и его статус подключения. Если хотите прослушивать события с Android, то внутри метода initState вам нужно вставить следующий код:

_channel.setMethodCallHandler((call) async {
 switch (call.method) {
   case "gamepadName":
     setState(() {
       _gamepadName = call.arguments;
     });
     break;
   case "gamepadRemoved":
     setState(() {
       _gamepadName = "Undefined";
     });
     break;
 }
});

Аргументы могут быть следующими:

Результат:

Запуск на других платформах

Наверное, главным преимущество Flutter является его кроссплатформенность. Игры на нём можно запускать на Android, iOS, ПК и даже в вебе.

iOS

Первым делом вам потребуется Mac с установленным на нём XCode. Только так вы сможете собрать билд игры.

Прим. перев. Есть также вариант использовать codemagic.io.

Если вы используете Android Studio, то после нажатия на RUN выберете iOS устройство. Для этого у вас должна быть включена опция «Open iOS Simulator».

Ещё вы можете запустить игру на определённом устройстве по его ID. Идентификатор устройства можно посмотреть во «flutter devices», а после выполнить следующее:

flutter run -d {device id}
// ID указывать без фигурных скобок

ПК

Для компьютеров сборка игры чуть сложнее из-за дополнительных шагов. Её можно проводить не только на Mac, но и на Windows или Linux.

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

export ENABLE_FLUTTER_DESKTOP=true

После этого вы сможете клонировать официальный репозиторий Flutter для ПК:

git clone https://github.com/google/flutter-desktop-embedding.git

cd example

Теперь вы должны скопировать папки «Mac», «Windows» и «Linux» в папку вашего проекта.

После этого нужно внести небольшие изменения в код:

void _setTargetPlatformForDesktop() {
 TargetPlatform targetPlatform;
 if (Platform.isMacOS) {
   targetPlatform = TargetPlatform.iOS;
 } else if (Platform.isLinux || Platform.isWindows) {
   targetPlatform = TargetPlatform.android;
 }
 if (targetPlatform != null) {
   debugDefaultTargetPlatformOverride = targetPlatform;
 }
}

Этот метод нужно вызвать в main().

void main() {
 _setTargetPlatformForDesktop();
 runApp(TapHero());
}

Убедитесь, что вы импортировали эти два класса:

import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show debugDefaultTargetPlatformOverride;

Примечание Если вам нужно заблокировать поворот экрана на телефоне, то добавьте этот код перед возвратом в методе сборки:

SystemChrome.setPreferredOrientations([
 DeviceOrientation.portraitUp,
]);

import 'package:flutter/services.dart';

И наконец игру можно запускать на ПК. Целевая платформа должна появится в списке устройств.

Веб

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

Процесс идентичен сборке под ПК. Сначала вам нужно клонировать репозиторий (там есть пример «hello_world»):

git clone git@github.com:flutter/flutter_web.git

cd examples/hello_world

Убедитесь, что вы используете последнюю версию Flutter:

flutter upgrade

И ещё вам потребуется пакет для веб-разработки:

flutter packages pub global activate webdev

flutter packages upgrade

Именно благодаря ему вы сможете собирать игры и запускать их в браузере. После этого нужно указать путь к Dart и Webdev и изменить импортируемые библиотеки для flutter_web, например:

import 'package:flutter_web/material.dart';

После этого нужно перенести все ассеты игры в папку /web/assets. Под конец остаётся нажать на строку webdev serve над терминалом.

Вот и всё. Теперь открыть игру в браузере можно по ссылке http://localhost:8080/.

Получение фактического URL

Знаете ли вы что-то о хостинге на Firebase? С его помощью вы сможете разместить ваше приложение и получить его URL. Вам нужна услуга «хостинг» и подробный гайд по ней:

https://firebase.google.com/docs/hosting/quickstart

После этого вам нужно будет создать своё Flutter Web App:

webdev build

В вашем проекте вы увидите новые папки: «build» и «public». Скопируйте всё содержимое первой во вторую. Последним шагом будет деплой:

firebase deploy

Вот и всё! Теперь ваше приложение должно быть доступно по адресу .web.app вашего домена. В игру из примера можно поиграть тут: https://gametaphero.web.app/#/

Примечание В папке public есть файл index.html. Он следует той же логике, что и любая веб-страница: для сайта можно установить иконку, цвет строки поиска (для Chrome mobile), название приложения и т. д.

Перевод статьи «From Zero to a Multiplatform Flutter Game in a week»

I have seen so many apps that provide auto tapping feature. But they don’t provide some specific customization so I decided to create a one. I have seen many tutorials that offers auto tapping, but they are supposed to be used in the same app. But I want to create an auto tapping app that can click on other app’s View. I am an intermediate java developer but new to Android studio, so I don’t know much about Android APIs. Is there any class or package which can provide this feature. For more clearance, I want to do auto tap, when the color at the specific location on screen, (213, 120) for instance, becomes green.
Thanks in advance!

asked Apr 11, 2020 at 16:15

MichaelJohn's user avatar

3

I am not an expert on the subject, I have experience as a BackEnd developer, I am new to the Android world, just like you I am interested in building an app to automate some farming mechanics in Android games with specific behaviors, so I investigated on the subject and the solution you are looking for is the AccessibilityService API, from Android 8.0 (API level 26) it includes several functionalities to make touches without the need for Root, I leave you links with examples of some open source repos that can guide you.

  • https://developer.android.com/guide/topics/ui/accessibility/service#continued-gestures (Doc)
  • https://github.com/nestorm001/AutoClicker (Old and abandoned project, but has the functionality of touches)
  • https://github.com/pylapp/SmoothClicker (A more mature App but need root, it can be helpful to guide you in how to build the UI)

For the detection in the change of the pixel I still do not have a concrete answer, I keep looking, maybe obtaining a dump of the screen every X time and validate the pixels on that is the way but it is still uncertain if it is the way to go.
Something like How to record screen and take screenshots, using Android API?.
I know that you can get a bitmap dump of the screen if you have the activity, but it’s not the case, however some class in the following package may be helpful https://developer.android.com/reference/kotlin/android/graphics/package-summary.

I’m currently working in my own implementation, when I have something to show I will gladly share it.

Batta's user avatar

Batta

4555 silver badges13 bronze badges

answered Oct 19, 2020 at 6:58

Sergio Rodriguez's user avatar

2

Кликер на андроиде

16.02.2018, 14:40. Показов 5222. Ответов 1


Собираюсь написать обычный кликер на андроиде.

Сразу хочу уточнить.
1) Возможно ли имитировать клик по координатам, так-же как на Windows?
Например, в c++ visual studio, есть такая функции (стандартная в windows.h)

C++
1
2
3
SetCursorPos(x, y);
mouse_event(MOUSEEVENTF_LEFTDOWN, x, y, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP,x,y,0,0);

2) Возможна ли работа с буфером обмена? Копирование, вставка обычного текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void win32_copy_to_clipboard(char* str,int n) //внести текст в буфер обмена
{
    size_t len,i;
    HGLOBAL hMem;
    len = n + 1;
    hMem =  GlobalAlloc(GMEM_MOVEABLE, len);
    memcpy(GlobalLock(hMem), str, len);
    GlobalUnlock(hMem);
    OpenClipboard(0);
    EmptyClipboard();
    SetClipboardData(CF_TEXT, hMem);
    CloseClipboard();
}
 
char* GetClipboardData() { //получить текст из буфера обмена
    HANDLE h;
    if (!OpenClipboard(NULL)) return 0;
    h = GetClipboardData(CF_TEXT);
    CloseClipboard();
    return (char *)h;
}

3) Возможна ли имитация нажатия клавиши? Ну т.е. у меня допустим есть поле ввода (как в html — input type=’text’)
И мне туда нужно автоматически ввести текст?

C++
1
2
3
4
    keybd_event(VK_CONTROL, 0,0,0);
    keybd_event(0x56,       0,0,0);
    keybd_event(VK_CONTROL, 0,KEYEVENTF_KEYUP,0);
    keybd_event(0x56,       0,KEYEVENTF_KEYUP,0); //ctrl+v

4) Возможно ли получить RGB цвет определенного пикселя?

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int GetPixelColor_Ex(int x, int y, UINT8 colornum=COLOR_SUM) {
    if(_hGDI) {
        pGetPixel = GetProcAddress(_hGDI, "GetPixel");
        _hdc = GetDC(NULL);
        if(_hdc) {
            _color = GetPixel(_hdc, x, y);
            int _red = GetRValue(_color);
            int _green = GetGValue(_color);
            int _blue = GetBValue(_color);
            switch(colornum) {
                case COLOR_SUM: return (_red + _green + _blue);
                case COLOR_RED: return _red;
                case COLOR_GREEN: return _green;
                case COLOR_BLUE: return _blue;
                default: return 0;
            }
        }
    }
    return -1;
}

И да, самое главное, это вообще возможно, делать из своего приложения, в другом приложении?
И еще, есть работа с оперативной памятью?

Я новичок, андроидом пользовался за все время месяца 2.

До этого кликер был написан на cpp. Т.е. я через эмулятор андроида(Bluestacks) под виндоус. Делал свои определенные действия). Теперь хочу сделать это под сам андроид, т.к. мобильность отсутствует. При каких-либо залагиваниях, приходится ехать домой и заного все запускать.

__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь



0



Приветствую! Многие начинающие разработчики Unity всегда начинают своё обучение с каких-либо простых вещей. И поэтому я решил показать пример создания максимально простого кликера.

Делать кликер мы будем всего с помощью двух UI элементов.

  • Кнопка, при нажатии на которую, будет увеличиваться наш счётчик.
  • Текст, в котором будет отображаться наш счётчик, то-есть суммарное количество наших кликов по кнопке.

Давайте их создадим. Но перед этим создадим игровой объект canvas, ведь ни один элемент UI не может существовать без канваса. Для этого ПКМ жмём на любое ПУСТОЕ место в окне Hierarchy. Дале выбираем UICanvas.

После созданного канваса, по такому же принципу создаём ещё 2 объекта, которые нам непосредственно понадобятся для нашего кликера:

  • Жмём ПМК —> UI — Legacy — Button
  • Жмём ПМК —> UI — Legacy — Text

Далее с помощью окна Inspector настраиваем свой канвас, кнопку и текст как Вам удобно. У меня получилось как-то так:

50% кликера сделано. Осталось чуть чуть.

Теперь перейдём к созданию C# скрипта при помощи вкладки Project, и назовём его, например, clicker. Впишем в него следующий C# код:

using UnityEngine;
using UnityEngine.UI;

public class clicker : MonoBehaviour
{
    private int num;
    public Text textNum;

    public void addNum()
    {
        num++;
        textNum.text = num.ToString();
    }
}

Давайте разберём, что мы тут сделали:

Поскольку мы будем работать с UI элементами(кнопками и текстом), то в строке №2 мы добавили специальную библиотеку: using UnityEngine.UI;

Далее в строке 6 и 7 мы создаём две переменные. В переменной num храним информацию о количестве кликов. А в переменной textNum будем хранить ссылку на уже нами созданный текстовый элемент UI, чтобы в дальнейшем туда записывать количество наших кликов из переменной num.

В строке №9 мы создали функцию addNum(), которая увеличивает наш счётчик num на +1, и в ледующей же строке, конвертирует наше число num, в текст, и записывает этот текст в textNum.text.

На этом всё, счётчик готов. Как видите он очень простой. Единственное что осталось сделать, это связать наши объекты с данным скриптом. Для этого необходимо выполнить 4 простых действия, которые указаны на картинке:

  • (1) — перетаскиваем наш скрипт clicker в окно Inspector, для объекта Button(кнопки)
  • (2) Перетаскиваем объект Text(наш счётчик) в поле Text Num
  • (3) Перетаскиваем объект Button(кнопку) в событие On Click
  • (4) Указываем наш скрипт, в котором имеется наша функция: clicker -> addNum()

На этом всё. Надеюсь я максимально подробно объяснил принцип работы счётчика. Если у меня это получилось, буду рад вашим лайкам :)

In this article, we will see how to create an auto-clicker using Python. The code will take input from the keyboard when the user clicks on the start key and terminates auto clicker when the user clicks on exit key, the auto clicker starts clicking wherever the pointer is placed on the screen. We are going to use the pynput module here. 

What is Auto Clicker?

Auto-Clicker is a script where you can auto control mouse and keyboard as many numbers of times as you want. It is controlled using user-defined keys. It works on various platforms like Windows, Mac and Linux. Auto clicker is present in pywin32 module.

Approach:

In this project, we will use a cross-platform module pynput to control the mouse and monitor the keyboard at the same time to create simple auto-clicker. To check for mouse events we will install pynput module (used to control the mouse) for this execute, pip install pynput in cmd. 

Note: If you’re stuck on how to set up python-pip package on your system then click here

Installation of pynput module

Verify whether the pynput module has been successfully installed into your working environment for this, open IDLE on the system that is cmd or Python Shell. Execute the command import pynput, after executing this the output should give zero errors which means your module is successfully installed. 

Verifying module installation

Implementation:

Let’s now proceed with the code that is required to build an Auto-clicker using Python. Follow the below steps to create an auto-clicker:

Step 1: Import time and threading then import Button and Controller from pynput.mouse module. Import Listener and KeyCode from pynput.keyboard.

Python3

import time

import threading

from pynput.mouse import Button, Controller

from pynput.keyboard import Listener, KeyCode

Step 2: Create four variables  as mentioned below,

  • delay: Delay between each click (in seconds)
  • button: Button is used to click in whatever direction you want to.  Button.left | Button.middle | Button.right
  • start_stop_key: The key used for start and stop of the click while you run the program for executing the auto clicker. It should be from a key class or set using KeyCode.
  • exit_key: The key used to terminate the auto clicker that is being executed. This should be from the key class or set using KeyCode.

Python3

delay = 0.001

button = Button.right

start_stop_key = KeyCode(char='a')

stop_key = KeyCode(char='b')

Step 3: Create a class extending threading.Thread. Pass delay and button to the class that have two flags to check if the program is executed or not.

Python3

class ClickMouse(threading.Thread):

    def __init__(self, delay, button):

        super(ClickMouse, self).__init__()

        self.delay = delay

        self.button = button

        self.running = False

        self.program_running = True

Step 4: Add methods to control the threads externally.

Python3

def start_clicking(self):

        self.running = True

def stop_clicking(self):

        self.running = False

def exit(self):

        self.stop_clicking()

        self.program_running = False

Step 5: A method is created when the thread starts, the program_running runs on loop until the value comes out to be true and also create another loop inside the existing loop where it checks if running is set to true or not. In case, we are inside both loops, it will click on the set button and sleep for the set delay.

Python3

def run(self):

    while self.program_running:

        while self.running:

            mouse.click(self.button)

            time.sleep(self.delay)

        time.sleep(0.1)

Step 6: Creating an instance for the mouse controller, then create ClickMouse thread. Start the instance to move into the loop inside the run method.

Python3

mouse = Controller()

click_thread = ClickMouse(delay, button)

click_thread.start()

Step 7: Create a method on_press which takes a key as an argument and sets up a keyboard listener. The start_stop_key matches with a start key (a) when it is executed. Then the click is to be terminated when running flag is set to True in the thread. Exit method is called in the method if the exit key (b) is executed and stop the listener.

Python3

def on_press(key):

    if key == start_stop_key:

        if click_thread.running:

            click_thread.stop_clicking()

        else:

            click_thread.start_clicking()

    elif key == stop_key:

        click_thread.exit()

        listener.stop()

with Listener(on_press=on_press) as listener:

    listener.join()

After the code is run we can see in the output as shown below, it shows the number of clicks the auto-clicker has made after the code is implemented. It is compatible with Windows, Mac and Linux. Auto-Clicker is helpful software for the systems as it let’s save a reasonable amount of time that is spent on repeated amount of clicks. 

Below is the complete program:

Python3

import time

import threading

from pynput.mouse import Button, Controller

from pynput.keyboard import Listener, KeyCode

delay = 0.001

button = Button.right

start_stop_key = KeyCode(char='a')

stop_key = KeyCode(char='b')

class ClickMouse(threading.Thread):

    def __init__(self, delay, button):

        super(ClickMouse, self).__init__()

        self.delay = delay

        self.button = button

        self.running = False

        self.program_running = True

    def start_clicking(self):

        self.running = True

    def stop_clicking(self):

        self.running = False

    def exit(self):

        self.stop_clicking()

        self.program_running = False

    def run(self):

        while self.program_running:

            while self.running:

                mouse.click(self.button)

                time.sleep(self.delay)

            time.sleep(0.1)

mouse = Controller()

click_thread = ClickMouse(delay, button)

click_thread.start()

def on_press(key):

    if key == start_stop_key:

        if click_thread.running:

            click_thread.stop_clicking()

        else:

            click_thread.start_clicking()

    elif key == stop_key:

        click_thread.exit()

        listener.stop()

with Listener(on_press=on_press) as listener:

    listener.join()

Now let’s execute the python program we’ve written and then press the start (a) and stop (a) keys in order to initiate the auto clicker. 

Output:

Понравилась статья? Поделить с друзьями:
  • Как написать кириллицей фамилию имя отчество для авиабилета
  • Как написать клиенту который не отвечает на сообщения
  • Как написать книгу про отношения
  • Как написать клиенту который давно не делает заказ
  • Как написать книгу про мафию