Microsoft — мировой лидер в области ПО и ИТ-услуг
531,52
рейтинг
11 сентября 2014 в 21:17

Разработка → Про создание платформера на Unity. Часть вторая, звездная tutorial

Привет, Хабр!

Я, как и обещал, продолжаю делиться с вами знаниями, приобретенными в процессе освоения 2D-режима Unity. От начинающего, так сказать, начинающим. Систематизирую, улучшаю и прогрессирую вместе с вами. Сегодня мы добавим к содержимому первой статьи управление камерой, сбор бонусов и первый способ умереть в игре. Если вам, как и мне, не терпится приступить, то добро пожаловать под кат.

И да, гифок там еще больше, чем в прошлый раз.





Скорее в бой!

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



Сам скрипт очень короткий и выглядит вот так:

using UnityEngine;
using System.Collections;

public class newcam : MonoBehaviour {
	public float dampTime = 0.15f;
	private Vector3 velocity = Vector3.zero;
	public Transform target;
	
	// Update is called once per frame
	void Update () 
	{
		if (target)
		{
			Vector3 point = camera.WorldToViewportPoint(new Vector3(target.position.x, target.position.y+0.75f,target.position.z));
			Vector3 delta = new Vector3(target.position.x, target.position.y+0.75f,target.position.z) - camera.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, point.z)); //(new Vector3(0.5, 0.5, point.z));
			Vector3 destination = transform.position + delta;


			transform.position = Vector3.SmoothDamp(transform.position, destination, ref velocity, dampTime);
		}
		
	}
}


Main camera, на которую повешен этот скрипт, следует за объектом, который указан у него (скрипта) в публичной переменной target. К слову — все публичные переменные в скриптах можно изменять прямо из редактора Unity. В нашем случае укажем на храброго носатого героя, запустим игру и проверим.



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



Прогуляемся по уровню. Прыжок, еще прыжок… Неудачное движение и герой падает в яму и не собирается возвращаться назад. Как нам с этим бороться? Да легко! Основная идея в том, что в пустоте под платформами будут расположены невидимые объекты, при пересечении которых можно будет загрузить уровень заново или вернуть персонажа назад с, допустим, минус одной жизнь. Для этого нам понадобятся пустой объект, которому добавим Box Collider (вытянутый по горизонтали) и поставим на нем в инспекторе галку Is trigger. Триггер в Unity отличается от простого коллайдера тем, что он не является препятствием, но при пересечении с объектом может вызвать какое-то событие. Объект переименуем в dieCollider и напишем немного кода для обработки падения. Вот вам гифка по случаю такого длинного абзаца:



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



Для обработки пересечения с триггером используется функция OnTriggerEnter2D. Для простого коллайдера аналогично OnCollisionEnter2D.
Добавим следующий код:


void OnTriggerEnter2D(Collider2D col){
		if (col.gameObject.name == "dieCollider")
						Application.LoadLevel (Application.loadedLevel);
	}


Как видите, все логично. Если имя объекта, с которым мы столкнулись, совпадает со строкой «dieCollider», то перезагружаем уровень вполне очевидным методом — загружаем еще раз уровень, который уже загружен.
Теперь можно упасть в яму и…



Отлично, персонаж возвращается на стартовую позицию. Сделаем префаб из dieCollider'а, расставим его копии во всех опасных местах и осмотримся: чего-то явно не хватает. Конечно, бонусов, которые можно собирать! Принцип их создания абсолютно аналогичен созданию областей, которые возвращают нас назад. Мы будем снова использовать триггеры, только теперь будем сталкиваться не с невидимым объектом, а со вполне конкретной звездой. Работает это так:



Теперь вернемся в MonoDevelop и напишем немного кода. Объявим float переменную score (которую на первое время можно сделать публичной, чтобы следить за результатом в процессе создания игры) и добавим следующий код в функцию OnTriggerEnter2D


if (col.gameObject.name == "star") {
						score++;
						Destroy (col.gameObject);
				}


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



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


void OnGUI(){
	GUI.Box (new Rect (0, 0, 100, 100), "Stars: " + score);
}


Проще некуда. Прямо на ДжиЮАе рисуется квадрат с координатой левого верхнего угла (0,0) и размерами 100x100. В нем выводится текст, прямо говорящий нам о количестве собранных звездочек.



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

И да: напомню, что прямо сейчас проходит конкурс разработчиков игр, в котором можно принять участие и получить Xbox One или Lumia 930!

Stay tuned!

Предыдущая часть
О чем рассказать в следующей части?
36%
(184)
Как анимировать персонажа
25%
(127)
Как добавить в игру врагов
39%
(199)
Как веселиться с физикой

Проголосовало 510 человек. Воздержалось 45 человек.

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

Автор: @evil_me
Microsoft
рейтинг 531,52
Microsoft — мировой лидер в области ПО и ИТ-услуг

Комментарии (16)

  • +17
    и, после двойного клика, впервые познакомимся с MonoDevelop в рамках этой серии уроков

    Неожиданно рад, что в «Блоге компании Microsoft» знакомят со средой разработки MonoDevelop. Ожидал, что в первом уроке первым пунктом будет «Как подключить среду разработки Visual Studio к Unity».
  • 0
    Хорошие туториалы у вас, продолжайте в том же духе.
    Для разнообразия к этой главе было бы неплохо добавить какой-либо визуальный эффект при подборе звезды, например анимировать её улетание к квадрату, всего-то снять триггер с коллайдера и проанимировать координаты, а визуально приятнее.
  • 0
    При «сборе» объектов (в данном случае — звезд) лучше не вызывать Destroy, а деактивировать объект через GameObject.SetActive(false); потому как Destroy() вызывает процесс сборки мусора и на слабых машинах и мобильных устройствах может быть подергивание при работе сборщика, особенно при большом количестве активных объектов в сцене.

    Кроме того, логику камеры лучше выполнять в LateUpdate(), т.е. после того, как контроллер о остальные «подвижные» объекты обновили свое положение.
    • 0
      Если просто играетесь с юнити, то ничего страшного)
      Если делаете настоящюю игру, то довольно быстро придете к идее пула объектов. Плюсов куча, даже объяснять не буду — все и так разжевано 1000 раз.

      Кроме того, функция Destroy() не вызывается мгновенно, а лишь помечает что объект должен быть уничтожен когда-нибудь, когда предоставиться такая возможность так что у него есть все шансы дожить до следующего кадра и скорее всего так и произойдет и он может снова столкнуться с игроком или чем то еще. Тогда мы за одну звезду заработаем 2 очка.
      Например у нас сетевая игра и играют 2 персонажа которые почти одновременно подошли к звезде. или мы дергаемся около звезды вправо-влево, так что звезда еще не уничтожилась, а мы успели покинуть пределы коллайдера и снова его пересечь) Вот тут пул тоже здорово поможет
      • 0
        Функция Destroy() гарантирует удаление перед рендерингом кадра, т.е. на следующем Update() его не должно быть. При этом объект не удалится в текущем кадре.
        Я не говорю про сетевую игру, не знаю как там устроенно.
        • 0
          Перед рендерингом следуйщего кадра, это конечно хорошо, но вот только физика имеет свой отдельный цикл, не связаный с рендерингом
          В этом можно убедиться если заменить OnTriggerEnter() на OnTriggerStay(). звезда подберется несколько раз
      • 0
        Если делаете настоящюю игру, то довольно быстро придете к идее пула объектов. Плюсов куча, даже объяснять не буду — все и так разжевано 1000 раз.

        Киньте ссылку, чтоли
  • +3
    Оу, почему в голосовании не чекбоксы!?
  • 0
    Мне кажется, было бы лучше проверять не по имени GameObject, a по наличию компонента Star, в котором заодно можно завести количество очков, получаемых при сборе этой звезды.

    Например, так:

    class Star: MonoBehaviour { public int score = 100; }

    И проверять его наличие так:
    var star = col.gameObject.GetComponent<Star>(); if(star != null) { score += star.score; Destroy(star.gameObject); }
    • 0
      Определенно можно :) Забыл добавить дисклеймер о том, что предложенное решение не является единственным возможным или оптимальным :)
  • 0
    Спасибо, отличная статья! Почти все понятно лучше чем в других встреченных мне туториалах.

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

    И ещё вопрос, если можно. Мы создали коллайдер, назначили его триггером, поставили на него скрипт с названием dieScript. По логике вещей — это скрипт привязанный именно к этому триггеру. Почему внутри скрипта мы должны ещё раз проверять совпадение имени объекта столкновения? Я бы ещё понял если бы мы там проверяли что наш триггер столкнулся именно с объектом-игроком, а не с каким-то другим. Но получается что все столкновения возможны только с игроком, и мы проверяем что игрок столкнулся именно с этим триггером. Непонятная логика какая-то.
    И потом скрипт на звезду — мы создали для объекта звезды свой скрипт? Или мы дописали ещё один иф в dieScript? Если второе — то я вообще не понимаю логики работы скриптов.
    • 0
      Скрипты столкновения, насколько я понял, привязаны к обьекту игрока, а не к триггерам. Поэтому нужна проверка, с чем мы собственнo столкнулись.
    • +1
      В гифке добавляется dieScript к dieCollider, но код на проверку с ним же. Исправить: проверять с именем героя или просто скрипт добавить на героя. Это косяк автора статьи, я ему утром написал, но он ещё не ответил.
      Со звездой: можно добавить скрипт как для звезды, так и для героя, просто не перепутать кто с кем сталкивается. Но по смыслу кода, OnGUI и код столкновения со звездой должен быть в коде героя. Там же переменная score.
      • 0
        Да, конечно, никакого dieScript, это моя больная фантазия :) Проверка столкновения вешается на героя и все в порядке.
      • 0
        Спасибо, это всё объясняет.
  • 0
    Ну где там уже следующая статья? :)

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

Самое читаемое Разработка