В нынешнюю эпоху мобильных технологий замечательно то, что каждый теперь может стать успешным разработчиком. Ни разу со времен ZX Spectrum разработчики-одиночки не могли создавать и распространять популярные приложения, способные идти в ногу с продукцией крупных издателей, как сейчас.
Немногие вещи иллюстрируют это больше, чем случай Flappy Bird. Flappy Bird была очень простой игрой, разработанной 28-летним Донг Нгуеном под названием dotGEARS. Механика и графика не могли быть проще, но они приносили 50 000 долларов в день. Это увлекательная история, о которой вы можете прочитать все на RollingStone.
Дело в том, что в приложении не было ничего особенного. Он оказался в нужном месте в нужное время и, если удача на его стороне, обогатил создателя. Это все еще может случиться сегодня — вам просто нужна правильная идея.
Чтобы продемонстрировать, насколько легко создать что-то вроде этого, я собираюсь показать вам, как вы можете создать свою собственную игру Flappy Bird в всего 10 минут. Я уже обсуждал, как это сделать в Android Studio, что, по общему признанию, было немного сложнее (хотя все еще довольно быстро). Я также обсуждал, как можно создать 2D-платформер в Unity за 7 минут — хотя на самом деле это была только базовая структура.
Но если объединить легкость Unity с простотой Flappy Bird — ну, это действительно 10-минутная работа.
Персонаж игрока
Сначала создайте новый проект, не забудьте выбрать 2D.
Поместите спрайт Flappy Bird в вашу сцену. Я создал его ранее для последнего проекта, поэтому воспользуюсь им снова. Не стесняйтесь использовать тот, который вы сделали тоже!
Как только спрайт окажется в вашей сцене, измените его размер по своему вкусу, перетаскивая углы. Теперь он также должен быть виден в вашем окне «Иерархия» слева. Это показывает вам все объекты в вашей «сцене», и на данный момент их должно быть только два: камера и птица.
Перетащите камеру в этом виде на птицу и затем отпустите. Теперь он должен появиться под птицей, что означает, что теперь это «ребенок» птицы. Это означает, что положение камеры по отношению к птице останется неизменным. Если наша птица движется вперед, вид будет двигаться вместе с ней.
Снова выберите птицу либо в виде сцены, либо в иерархии. Вы увидите список параметров и атрибутов справа в представлении с пометкой Inspector . Здесь вы можете управлять конкретными переменными, относящимися к этому объекту.
Спуститесь вниз и выберите Добавить компонент . Теперь выберите Physics2D> Rigidbody2D . Это хороший готовый набор инструкций, которые применит гравитацию к нашему игроку.. Нажмите Ограничения на этой панели, а затем выберите заморозить вращение Z . Это не позволит вашей птичке кружиться, как сумасшедший, и брать с собой камеру, что может довольно быстро вызвать тошноту.
Добавьте Polygon Collider в таким же образом, который сообщит Unity, где находятся края персонажа. Нажмите Play , и спрайт персонажа должен теперь бесконечно падать, увлекая за собой камеру.
Пока все хорошо!
Мы также хотим, чтобы наш персонаж мог летать, но это достаточно легко реализовать.
Сначала нам нужно создать сценарий C #. Создайте для этого папку (щелкните правой кнопкой мыши в любом месте ресурсов, чтобы создать папку с именем «Scripts»), затем щелкните правой кнопкой мыши и выберите Create> C # Script .
Я назвал свой «Персонаж». Дважды щелкните по нему, чтобы открыть редактор C #, которым может быть MonoDevelop или Visual Studio. Теперь добавьте следующий код:
public class Character: MonoBehaviour {public Rigidbody2D rb; public float moveSpeed; public float flapHeight; //Используйте это для инициализации void Start () {rb = GetComponent (); }//Обновление вызывается один раз за кадр void Update () {rb.velocity = new Vector2 (moveSpeed, rb.velocity.y); if (Input.GetMouseButtonDown (0)) {rb.velocity = new Vector2 (rb.velocity.x, flapHeight); } If (transform.position.y> 18 || transform.position.yЭтот код выполняет две функции. Он заставляет игрока постоянно двигаться вперед со скоростью, которую мы сможем определить в инспекторе, и добавляет нашу способность «взмахивать руками». Метод Update () вызывается многократно во время работы вашей игры, поэтому все, что вы здесь разместите, будет происходить постоянно. В этом случае мы добавляем немного скорости нашему твердому телу. Rb - это физический скрипт ( RigidBody2D ), который мы применили к нашему объекту ранее, поэтому, когда мы говорим rb.velocity , мы имеем в виду скорость игрового объекта.
![]()
A mouseclick интерпретируется Unity как касание в любом месте экрана, если вы используете мобильное устройство. Когда мы это обнаруживаем, мы заставляем персонажа немного приподняться.
Public float moveSpeed будет контролировать скорость движения, а public float flapHeight будет обрабатывать увеличение высоты птицы при каждом нажатии. Поскольку эти переменные являются общедоступными, мы сможем изменять их извне скрипта.
Death () - это общедоступный метод.. Это означает, что это набор кода, относящегося к нашему персонажу, который могут вызывать другие сценарии и объекты. Он просто возвращает позицию нашего игрока в начальную точку. Мы также будем использовать его каждый раз, когда персонаж поднимается слишком высоко или слишком низко. Вы скоро поймете, почему это должно быть обнародовано. rb.velocity = Vector3.zero; линия предназначена для того, чтобы убить весь импульс - чтобы наш персонаж не начинал падать все быстрее и быстрее каждый раз, когда он перезапускается с самого начала.
![]()
Выйдите из редактора и добавьте сценарий как компонент к вашему персонажу (выберите птицу, выберите Добавить компонент> Скрипты> Персонаж ). Теперь вы можете определять moveSpeed и flapHeight в инспекторе (это то, что делает общедоступная переменная). Я установил для себя 3 и 5 соответственно, что кажется правильным.
Еще одна вещь: в инспекторе вы также захотите добавить тег к своему персонажу. . Щелкните там, где написано Тег: без тегов , а затем выберите Player в раскрывающемся списке.
Препятствия
Далее мы добавим препятствия: трубы. Туннель для одного человека к скрытым грибам - смертельный враг для другого.
Перетащите спрайт трубы в свою сцену примерно туда, где вы хотите, чтобы первое препятствие могло появиться, и назовите его pipe_up .
Теперь создайте новый скрипт, как и раньше, и назовите его «Pipe». Вот как это выглядит:
public class Pipe: MonoBehaviour {private Character character; //Используйте это для инициализации void Start () {character = FindObjectOfType (); }//Обновление вызывается один раз за кадр void Update () {if (character.transform.position.x - transform.position.x> 30) {}} void OnCollisionEnter2D (Collision2D other) {if (other.gameObject.tag = = "Игрок") {персонаж.Death (); }}}Добавьте этот скрипт к спрайту канала таким же образом, как и раньше. Это покажет, когда труба уйдет за левую часть экрана. На самом деле мы здесь еще ничего не поместили, но мы вернемся к этому.
![]()
OnCollisionEnter2D - это метод, вызываемый всякий раз, когда ваш коллайдер контактирует с другим коллайдером. В данном случае: когда игрок попадает в трубу. Затем вызывается метод Death () , который мы создали ранее, заставляя нашего персонажа вернуться в начальную точку.
Теперь у вас есть одна труба, которая время от времени исчезает и снова появляется на другом конце экрана. Если прикоснешься к ней, ты умрешь!
Трубы перевернутые
Пока у тебя будет только одна вертикальная труба.
Теперь добавьте еще один спрайт. Вы можете сделать это, щелкнув правой кнопкой мыши в иерархии и сказав Новый 2D-объект> Спрайт , а затем выбрав спрайт, который вы хотите использовать; проще просто перетащить файл в сцену еще раз.
Переименуйте этот файл: pipe_down . Там, где в инспекторе указано Режим рисования , установите флажок Flip: Y . Как вы уже догадались, теперь наш спрайт перевернулся. Добавьте тот же
![]()
Затем создайте еще один новый сценарий C #, на этот раз с именем PipeD . Он будет содержать почти тот же код:
public class PipeD: MonoBehaviour {private Character character; //Используйте это для инициализации void Start () {character = FindObjectOfType (); }//Обновление вызывается один раз за кадр void Update () {if (character.transform.position.x - transform.position.x> 30) {}} void OnCollisionEnter2D (Collision2D other) {if (other.gameObject.tag = = "Игрок") {персонаж.Death (); }}}Если бы мы создавали более сложную игру, мы, вероятно, создали бы сценарий под названием Hazard , который причинял бы вред игроку, и отдельный сценарий с именем Regen , чтобы препятствие обновлялось, когда игрок заходил слишком далеко вправо.
Prefab sprout
Теперь мы могли сделайте всю нашу игру Flappy Bird с помощью всего этого фрагмента кода. Мы могли перемещать каналы в правую часть экрана каждый раз, когда они исчезали, или копировать и вставлять столько каналов, сколько захотим, по экрану.
Нам нужно было идти с первым вариантом будет сложно убедиться, что трубы правильно выстроены, когда они генерируются случайным образом, и поддерживать справедливость. Когда персонаж умирает, он может возродиться за много миль от первой трубы!
Если бы мы выбрали последний вариант - копирование и вставку - мы бы излишне использовали много памяти, замедляя нашу игра и ограничение повторного воспроизведения (потому что это будет каждый раз одно и то же!).
Вместо этого давайте использовать так называемые «префабы». Это сокращение от «сборные», и в основном это означает, что мы собираемся превратить наши трубы в шаблоны, которые затем можно будет использовать для эффективного производства большего количества труб по желанию. Для программистов среди вас сценарий конвейера - это наш класс, а каждый канал на экране - всего лишь экземпляр этого объекта.
Для этого просто создайте новую папку с именем Prefabs . Теперь перетащите свои pipe_up и pipe_down из иерархии в папку.
Каждый раз, когда вы перетаскиваете объект из своей сборной папки, он будет иметь те же свойства, что означает, что вам не нужно будет добавлять компоненты. Что еще более важно, это означает, что редактирование размера префаба в папке повлияет на размер каналов на протяжении всей игры - нет необходимости изменять их все по отдельности.
Как вы понимаете, здесь есть множество преимуществ с организационной точки зрения, позволяющей сэкономить время. Это также означает, что мы можем взаимодействовать с нашими объектами из нашего кода. Мы можем создавать «экземпляры» наших каналов.
Сначала добавьте этот код в оператор if, который мы оставили пустым в обновлении нашего первого скрипта pipe . () метод:
void Update () {if (character.transform.position.x - transform.position.x> 30) {float xRan = Random.Range (0, 10); float yRan = Random.Range (-5, 5); Instantiate (gameObject, new Vector2 (character.transform.position.x + 15 + xRan, -10 + yRan), transform.rotation); Уничтожить (gameObject); }}Это сначала «инстанцирует» наш gameObject . Создание экземпляра создает новую идентичную копию. В Unity, когда вы используете слово gameObject , оно относится к объекту, к которому в данный момент прикреплен скрипт, - в данном случае к нашему каналу.
Мы регенерация указанной трубы с небольшими случайными вариациями в интересах забавы.
![]()
Но вместо того, чтобы делать то же самое в скрипте PipeD, мы генерируем оба объекта в одном месте. Таким образом, мы можем легко сохранить положение второй трубы относительно этой первой. Это также означает, что нам нужно меньше кода для PipeD.
Создайте общедоступный gameObjec t с именем pipeDown . Затем обновите код следующим образом:
if (character.transform.position.x - transform.position.x> 30) {float xRan = Random.Range (0, 10); float yRan = Random.Range (-5, 5); float gapRan = Random.Range (0, 3); Создать экземпляр (gameObject, новый Vector2 (character.transform.position.x + 15 + xRan, -11 + yRan), transform.rotation); Instantiate (pipeDown, new Vector2 (character.transform.position.x + 15 + xRan, 12 + gapRan + yRan), transform.rotation); Уничтожить (gameObject); }Я также добавил переменную gapRan , которая позволит нам немного изменять размер зазора между двумя трубками, просто для того, чтобы было интересно.
Теперь вернитесь в Unity и перетащите префаб pipe_down из папки префабов (важно!) в место, где написано «Pipe Down» (обратите внимание, как он переводится наш случай верблюда, вставив пробел) на спрайте трубы. Помните, мы сделали Pipe Down общедоступным gameObject, что означает, что мы можем определить, что это за объект из другого места - в данном случае через инспектор.. Выбирая префаб для этого объекта, мы гарантируем, что при создании экземпляра канала он будет включать все атрибуты и скрипт, которые мы добавили к нему ранее. Здесь мы не просто создаем спрайт, а регенерируем объект с коллайдером, который может убить игрока.
Все, что вы собираетесь добавить в тот же раздел на Скрипт PipeD представляет собой простой Destroy (gameObject) , поэтому он самоуничтожится, когда выйдет за левую сторону.
Если вы нажмете кнопку играть сейчас, игра будет прокручиваться автоматически, и вы погибнете, если дотронетесь до любой трубы. Пройдите достаточно далеко, и эти трубы исчезнут, а затем возродятся перед вами.
Но, конечно, в игре есть большой промежуток между трубами, и экран выглядит довольно пустым. Мы могли бы исправить это, перетащив несколько префабов в нашу сцену, чтобы к нам постоянно приближался своего рода конвейер труб. Однако лучше было бы создать каналы в скрипте. Это важно, поскольку в противном случае, когда персонаж умирает, трубы в начале будут уничтожены, и снова будет большое пустое пространство.
Таким образом, мы можем строить первые несколько труб каждый раз при загрузке игры и каждый раз, когда персонаж умирает, чтобы вернуть все в нормальное состояние.
Бесконечный вызов
Теперь вы собираетесь создать общедоступный pipe_up и общедоступный pipe_down в сценарий вашего персонажа. Таким образом, вы можете ссылаться на созданные вами объекты, перетаскивая префабы на объект-персонаж, как если бы вы добавили pipe_down в свой скрипт Pipe.
Вам нужно будет добавить это:
public GameObject pipe_up; public GameObject pipe_down;Затем мы собираемся создать следующий метод:
public void BuildLevel () {Instantiate (pipe_down, new Vector3 (14, 12) , transform.rotation); Создать экземпляр (pipe_up, new Vector3 (14, -11), transform.rotation); Создать экземпляр (pipe_down, new Vector3 (26, 14), transform.rotation); Создать экземпляр (pipe_up, new Vector3 (26, -10), transform.rotation); Создать экземпляр (pipe_down, new Vector3 (38, 10), transform.rotation); Создать экземпляр (pipe_up, new Vector3 (38, -14), transform.rotation); Создать экземпляр (pipe_down, new Vector3 (50, 16), transform.rotation); Создать экземпляр (pipe_up, new Vector3 (50, -8), transform.rotation); Создать экземпляр (pipe_down, new Vector3 (61, 11), transform.rotation); Создать экземпляр (pipe_up, new Vector3 (61, -13), transform.rotation); }С помощью BuildLevel () мы затем вызовем этот метод один раз в методе Update () и один раз в метод Death () .
Когда игра запускается, вызывается Update () , и мы помещаем каналы в эту конфигурацию . Это сделает первые несколько испытаний всегда одинаковыми для игрока. Когда игрок умирает, трубы также будут перемещены в ту же конфигурацию..
Эта компоновка каналов - хорошая установка для моего спрайта (у которого масштаб установлен на «4»), но вы можете поэкспериментировать со своим. Вы также можете проверить скорость и расстояние, чтобы изменить сложность игры.
![]()
Вернитесь в свою сцену в Unity и удалите там два канала. Ваша «игра» будет выглядеть как пустой экран и птичка. Нажмите Play , и появятся трубы, рандомизирующие их позиции после первых нескольких.
Заключительные комментарии
Это почти весь игра! Добавьте несколько очков, возможно, сделайте его немного оригинальнее, и сложность будет возрастать по мере игры. Вам понадобится экран меню. Также было бы неплохо уничтожить трубы на экране, когда персонаж умирает.
![]()
Но как только вы это сделаете, у вас будет готовый продукт для Play Store, очень похожий на приложение, которое сделало другого разработчика очень богатым. Это просто показывает, что вам не нужно быть гением программирования или иметь за плечами крупного издателя, чтобы добиться успеха.
Вам просто нужна хорошая идея и десять минут!