Поиск по этому блогу

среда, 26 декабря 2012 г.

Как сделать 2.5D игру с помощью Unity (Часть 2)


Это вторая часть урока на тему “Как сделать простую 2.5D игру для iPhone с помощью Unity”
В первой части мы получили базовое понимание о Unity и о написании кода на C#. Мы создали простую игру, в которой самолет летает вперед-назад, бомбит акул и защищает рыбу-клоуна.
Во второй и заключительной части мы улучшим игру. Добавим музыку и звуковые эффекты, допилим логику игры и добавим еще несколько сцен.
Если у вас нет исходников, то скачайте и запустите проект в Unity. Ок, давайте узнаем о Unity еще больше и прокачаем нашу игрушку.

Добавляем улучшения.

Вы наверняка заметили, что когда бомба попадает в акулу, она просто тихо исчезает и думает про себя: “Фу! Это не круто”.
Но все поправимо, мы добавим крутые подводные взрывы.
Из меню выберите “GameObject/Create other/Particle System”, и вы увидите как на сцене появилась система частиц (particle system). Переименуйте “Particle System” в “Explosion” в панели “Hierarchy” установите позицию Explosion на [1, 1, 8].
Теперь, если вы смыслите в Particle System, взгляните на инспектор и настройте все сами, а если нет, сделайте как у меня. Скопируйте эти значения в инспектор.

Здесь наиболее важным свойством является “One shot”. Если это свойство включено, то система будет выпускать частицы только один раз, когда происходит взрыв. Теперь давайте так же установим свойства анимации. Попытайтесь хотя бы приблизительно повторить цвета ниже, хотя это не так важно:
Здесь есть еще одно важное свойство – “Autodestruct”. Если свойство включено, то Particle System удалит себя со сцены, когда живых частиц не останется. Это именно то, что нам нужно.
Теперь у вас есть небольшой взрыв, и вам предстоит сделать с ним то же, что и с бомбой – сделать префаб, чтобы клонировать его, когда необходимо.
Щелкните правой кнопкой мыши в панели “Project” в папке “Prefabs” и выбирите “Create/Prefab”, переименуйте префаб в “ExplosionPrefab”. Перетащите объект “Explosion” из “Hierarchy” на новый префаб “ExplosionPrefab”. Клик правой кнопкой на “Explosion” в “Hierarchy” и выберите “Delete”.
Правый клик в панели “Project” и выбор “Sync MonoDevelop Project”, откроется MonoDevelop. Загрузите в эдиторе BombClass.cs и добавьте этот код:
?
1
2
3
4
5
//right under definition of "ySpeed"
public GameObject explosionPrefab;
//inside OnTriggerEnter, right after Destroy(this.gameObject)
Instantiate(explosionPrefab, transform.position, Quaternion.identity);
Теперь вернитесь в Unity и выберите BombPrefab в панели “Project”. В инспекторе вы увидите новое свойство (property) “ExplosionPrefab”. Перенесите “ExplosionPrefab” из “Project” на поле нового свойства и все готово.
Вот и все – нажмите Play посмотрите на взрывы, когда вы попадаете в акулу.

Улучшаем/добавляем звуки.

Как мы могли сделать игру без фоновой музыки? Давайте зайдем в гости к еще одному замечательному парню – Кевину Маклауду, который создает замечательную музыку для фильмов и игр под лицензией CC. Перейдите наhttp://incompetech.com/m/c/royalty-free/index.html?keywords=the%20cannery. Скачайте композицию “The Cannery” и сохраните у себя на диске. Перетащите трек “The Cannery.mp3” в папку “Audio” в панели “Project”.
Нам нужно чтобы музыка играла все время… но вот вопрос, к какому объекту стоит ее прикрепить? Давайте прикрепим ее к камере. Камера тоже объект игры и к ней можно прикрепить другие объекты.
Перетащите “The Cannery” из панели “Project” на “Main Camera” в “Hierarchy”. Выберите “Main Camera” в “Hierarchy” и в инспекторе найдите строчку Audio Source – установите флажок на “Loop” и выставите громкость на “0.22”
Все очень просто – запустите игру и проверьте фоновую музыку.

Создаем GUI в Unity

Давайте изучим еще один немаловажный вопрос – GUI (Графический интерфейс пользователя). В Unity есть некоторые стандартные ярлыки (label) и кнопки, но это не самая сильная сторона Unity. Мы собираемся использовать лейблы для отображения счета игры, поэтому вначале нам нужно реализовать логику оценочной системы. Переключимся в MonoDevelop, откроем класс PlayerClass.cs и добавим новое свойство:
?
1
public static int score = 0;
Кое что новенькое – “static”. Это свойство будет создано, когда класс будет загружен и останется независимо ни от чего. Кроме того это свойство доступно из всех других классов, именно поэтому мы подсчитываем очки в свойстве “static”.
Теперь добавьте метод, который позаботится об обновлении очков:
?
1
2
3
public void updateScoreBy(int deltaScore) {
    PlayerClass.score += deltaScore;
}
Добавьте еще один метод в PlayerClass. Он будет отвечать за вывод очков на экран:
?
1
2
3
4
5
void OnGUI() {
    GUIStyle style = new GUIStyle();
    style.fontSize = 20;
    GUI.Label(new Rect(10,10,100,20), "Score:"+PlayerClass.score, style );
}
Обработчик события “OnGUI” вызывается каждый слоя GUI, поэтому все, что вам нужно в GUI делается именно здесь.
GUIStyle – это класс, который схож с классом CSS, так что, если вы знакомы с CSS вы можете использовать FontSize, marginLeft и т.д., а если нет, то пока давайте ограничимся только размером шрифта. GUI.Label() – это метод, который содержит 3 аргумента: границы лейбла, строка для рисования и стиль текста. Это все.
Единственная задача – обновлять очки, когда мы попадаем или промахиваемся. Откройте BombClass.cs и внесите следующие изменения:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//add a new property
public PlayerClass player;
//replace the existing OnTriggerMethod with
void OnTriggerEnter(Collider obj) {
    if (obj.gameObject.name == "Shark") {
        //reset shark
        obj.gameObject.transform.rotation = Quaternion.identity;
        obj.gameObject.transform.position = new Vector3(20f, -3f, 8f);
        player.updateScoreBy(+1);
        Destroy(gameObject);
        Instantiate(explosionPrefab, transform.position, Quaternion.identity);
    }
    if (obj.gameObject.name == "ClownFish") {
        //reset fish
        obj.gameObject.transform.rotation = Quaternion.identity;
        obj.gameObject.transform.position = new Vector3(-20f, -1f, 8f);
        player.updateScoreBy(-1);
        Destroy(gameObject);
        Instantiate(explosionPrefab, transform.position, Quaternion.identity);
    }
}
Это очень похоже на то, что мы делали до этого, но у нас появилось новое свойство “player” и когда нам нужно обновить очки мы вызываем player.UpdateScoreBy().
Так же, что бы сделать игру еще интереснее, вы будете получать один балл, когда попадете в акулу и лишаться его, если попадете в рыбу-клоуна. Теперь это выглядит как трудная игра.
Еще одна вещь – установить свойство player на бомбу. Сейчас мы не можем сделать это как раньше, потому что бомбы создаются динамически, но к счастью бомбы создаются самим игроком, так что можно установить свойство player на время создания.
Давайте сделаем это – откройте PlayerClass.cs и прямо под строкой “bombObject.transform.position = this.gameObject.transform.position;” добавьте следующий код:
?
1
2
BombClass bomb = (BombClass)bombObject.GetComponent("BombClass");
bomb.player = this;
Давайте обсудим кое-что новенькое. bombObject - это экземпляр GameObject, так что мы вызываем “GetComponent” и таким образом получаем доступ ко всем прикрепленным компонентам к игровому объекту. В итоге мы получаем ссылку на класс C# прикрепленному к игровому объекту. Затем мы просто устанавливаем для него свойство “player” (экземпляр PlayerClass).
Запустите игру и вы увидите счетчик очков!
Объекты и компоненты в Unity.
У вас уже должно быть достаточно практики, чтобы познакомиться с моделью игровых объектов Unity. Хорошо бы понимать, что вы делаете на самом дела, не так ли? Давайте быстренько уясним как игровые объекты соотносятся с компонентами прикрепленными к ним.
Все эти строки мы увидим в панели инспектора – это все компоненты, которые привязываются к игровым объектам. Пустой игровой объект имеет лишь компонент transform (position, rotation, scale). Вот и все. Все остальные компоненты присоединяются вами.
Если вы взгляните на наш BombPrefab, вы увидите много разных компонентов:
  • Transform – отвечает за положение (position), поворот (rotation) и scale (масштаб).
  • Mesh Filter – отчечает за геометрию видимого объекта.
  • Mesh Rendered – рендеринг геометрии.
  • Rigidbody – отвечает за физику.
  • Audio Source – отвечает за проигрывание аудио.
  • Script – программное обновление объектов.
Это лишь часть из тех компонентов, которые можно привязать к объекту. Для лучшего понимания давайте посмотрим на диаграмму.
Итак, посмотрите на компонент Script. Теперь должно быть немного яснее, почему мы должны вызывать:
?
1
Destroy(this.gameObject);
Для того чтобы уничтожить все экземпляры связанные с объектом. Из свойства GameObject мы так же можем получить доступ ко всем другим компонентам, так что мы можем изменять физику, громкость звука и т.д.

Добавляем больше сцен.

Теперь наша игра выглядит куда более лучше, но в нее нельзя выиграть или проиграть.
Давайте добавим экран “You Win”, который будет выводиться, когда игрок наберет более 3-х очков.
Из меню выберите “New Scene”, затем снова из меню “Save Scene”, выбирите папку [your project's directory]/Assets/Scenes и сохраните сцену с именем “WinScene”.
Выберите “Main Camera” в “Hierarchy” и установите: Position [0, 0, 0], Projection на “Orthographic”, Size на “10”, Near на “0.5” и Far на “22”. Из меню выберите “GameObject/Create Other/Directional Light” и установите в инспекторе Position на [0, 0, 0].
Все что нам нужно на сцене, это добавить плоскость (как и фон в нашей игре) и наложить на него сообщение “You Win”. Действуем как в первой части урока: Из меню переходим в “GameObject/Create Other/Plane” и в инспекторе устанавливаем Position на [0, 0, 8], Rotation на [90, 180, 0], Scale [3, 1, 2].
Теперь скачайте и сохраните изображение подготовленное Vicki Wenderlich (картинка кликабельна):

Перетащите “gameover_youwin.png” в панель “Project” в папку “Textures”. Текстура экспортируется и вы заметите, что вид у нее не очень. Во всем виновато сжатие. Выберите “gameover_youwin” и найдите в инспекторе раздел “Format”, измените значение на 16bits и нажмите Apply. Теперь перетащите “gameover_youwin” из панели “Project” на “Plane” в “Hierarchy”. В панели “Game” вы должны увидеть “You Win”.
Нам остается только оживить все это. Когда сцена срабатывает необходимо чтобы игра перезапустилась. Щелкните правой кнопкой мыши в панели “Project” в папке “Class”, выберите “Create/C Sharp Script” и переименуйте созданный файл в “GameOverClass”. Правый клик и выбор “Sync MonoDevelop Project”. В MonoDevelop откройте GameOverClass.cs и замените содержимое на это:
?
1
2
3
4
5
6
7
8
9
10
11
12
using UnityEngine;
using System.Collections;
public class GameOverClass : MonoBehaviour {
    // Update is called once per frame
    void Update () {
        if (Input.anyKeyDown) {
            PlayerClass.score = 0;
            Application.LoadLevel("LevelScene");
        }
    }
}
Когда игрок тапнет по экрану, счет сбросится и загрузится игровая сцена. Application.LoadLevel() содержит имя сцены, которая будет загружена, все просто.
Вернитесь в Unity и перетащите “GameOverClass” из папки “Class” на “Main Camera”. Теперь, чтобы включить эту сцену в проект выберите “File/Build Settings” и в открывшемся окне кликните “Add Current” и закройте окно. Вы добавили сцену в проект.
Давайте быстро добавим экран “You loose”. Как и в тот раз создаем “New Scene”, затем “Save Scane” с именем “LooseScene” в папке Scenes.
Выберите “Main Camera” в “Hierarchy” и установите: Position на [0, 0, 0], Projection на “Orthographic”, Size на “10”, Near на “0.5” и Far на “22”. Из меню перейдите “GameObject/Create Other/Directional Light” и в инспекторе Position на [0, 0, 0]. Из меню “GreateObject/Create other/Plane” и в инспекторе Position на [0, 0, 0], Rotation на [90, 180, 0], Scale на [3, 1, 2]
Скачайте это изображение и сохраните на диске:

Для добавления сцены сделайте все как в прошлый раз:
  • Перетащите файл “gameover_youloose.png” в папку “Textures”.
  • Выберите “gameover_youloose.png”, в инспекторе найдите “Format”, измените значение на “16bit” и нажмите “Apply”
  • Перетащите “gameover_youloose.png” на “Plane” в “Hierarchy”
  • Перетащите файл GameOverClass” из папки “Class” на “Main Camera”
  • Из меню выбирите “File/Build Settings” и в открывшемся окне нажмите “Add current”. Закройте окно.
Итак, теперь у вас 3 сцены. Нам нужно соединить их.
Двойным нажатием по “LevelScene” в панели “Project” загрузите LevelScene. Переключитесь в MonoDevelop и откройте PlayerClass.cs. Мы собираемся изменить метод updateScoreBy, чтобы проверять, количество очков (3 или -3).
?
1
2
3
4
5
6
7
8
9
//replace the updateScoreBy method with this code
public void updateScoreBy(int deltaScore) {
    PlayerClass.score += deltaScore;
    if (PlayerClass.score>3) {
        Application.LoadLevel("WinScene");
    } else if (PlayerClass.score<-3) {
        Application.LoadLevel("LooseScene");
    }
}
Теперь сцены настроены. Вы можете протестировать игру нажав кнопку Play в Unity. Чтобы протестировать на iPhone, нажмите “File/Build&Run” и когда Xcode откроется, нажимайте “Run”.

Переходим к 2.5D

Да, время пришло. Пора сделать то, что мы хотели сделать все 2 части урока – 2.5D!
Ранее, в настройках камеры мы использовали опцию “Orthographic” и наша игра выглядела как 2D. Этому пришел конец.
В LevelScene выберите “Main Camera” и в инспекторе в Projection установите значение “Perspective”, а “Field View” установите на “100” (для компенсации перспективы). Это все. Нажмите Play и посмотрите на свою игру в 2.5D. Классно, не так-ли?

Но на этом мы останавливаться не намерены.
Вот план – мы сделаем игру более трудной и будем поворачивать и перемещать камеру каждый раз когда количество очки будет увеличиваться. Таким образом, чем больше попаданий, тем под более странным углом вы будете смотреть на персонажей игры.
Переключитесь в MonoDevelop и внесите следующие изменения в PlayerClass.cs:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//add the properties
public GameObject mainCamera;
public GameObject gameBackground;
public float nextZ = 0;
//at the end of the updateScoreBy method
if (PlayerClass.score>0) {
    nextZ = PlayerClass.score*2.5f;
}
//at the end of the Update method
if (nextZ > mainCamera.transform.position.z) {
    mainCamera.gameObject.transform.Translate(
        3* Mathf.Sin(transform.position.z/2 ) * Time.deltaTime,
        0,
        -Mathf.Sin(transform.position.z /2 ) * Time.deltaTime *0.3f
    );
    mainCamera.gameObject.transform.RotateAroundLocal( Vector3.up, Time.deltaTime*0.1f );
    gameBackground.gameObject.transform.RotateAroundLocal( Vector3.up, Time.deltaTime*0.1f );
}
Отлично. Получилось немало кода, но это все, что нам нужно. Давайте разберемся.
Во-первых мы задекларировали свойства содержащие ссылки на Main Camera и плоскость фона (Background). Мы будем двигать и вращать камеру и фон тоже будет вращаться.
Камера со своего текущего положения Z c позиции 0 примерно до 7.5Z. Каждый раз, когда игрок будет получать очко, nextZ будет установлен на 2.5, затем на 5.0, затем на 7.5 и от этих значений Main Camera пойдет по дуге с помощью функции sin.
Кстати, все математические функции доступны через класс Mathf – для sin это Mathf.Sin (). Так же мы поворачиваем камеру с помощью transform.RotateAroundLocal. Мы вращаем камеру и фон вместе, так что камера всегда лицом к фону (т.е. фон н выйдет за экран).
И еще одно – давайте соединим новые public свойства (public properties). Переключитесь в Unity и выберите объект “Player” в “Hierarchy”. Перетащите “Main Camera” из “Hierarchy” на новое свойство “Main Camera” в инспекторе; перетащите объект “Background” из “Hierarchy” на новое свойство “Game Background” в инспекторе.
Поздравляю, мы наконец-то закончили! Выберите “File/Build&Run” и запустите игру на iPhone и наслаждайтесь бомбежкой акул под разными углами.

Дебаггинг в Unity.

Во время работы над этим проектом у вас могут возникнуть проблемы. Вот несколько вещей, которые надо иметь ввиду:
  • Не забывайте про кнопку Pause в тулбаре. Если вам нужно остановить выполнение игры и проверить свойства объектов, просто нажмите Pause, а затем осматривайте сцену и значения в инспекторе.
  • Если вы не уверены, что ваш метод запускается, напечатайте в консоле сообщение (как и в Xcode). Используйте: Debug.Log(“message”). Чтобы перейти в консоль выберите “Window/Console”.
  • Развивайте в себе привычку: Когда вы закончили кодинг в MonoDevelop и вернулись в Unity, посмотрите в статус бар в нижней части окна. Если вы написали неверный код вы получите соответствующее сообщение красными буквами.
  • Если объекты не двигаются, трижды проверьте привязан ли код к объекту.
  • Когда игра запущена, вы можете редактировать значения в инспекторе, чтобы выбрать то, что вам подходит. Однако, следует помнить, что эти изменения будут потеряны, когда вы остановите игру. Так что, после тестирования остановите игру и только потом продолжайте разработку.

Что дальше?

Здесь можно скачать законченный проект с изменениями из второй части урока.
Если вы хотите углубиться в изучение Unity, то вам может помоч официальный сайт программы, а именно раздел Support и, конечно же, Форум.
Если того что мы сделали вам не достаточно вы сможете усовершенствовать нашу игру добавив, например, следующее:
  • Добавить звуки к взрывам и музыку к game over/win сценам.
  • Добавить несколько вариантов взрывов со случайной сменой.
  • Добавить несколько уровней.
Мы прошли очень простое введение в Unity, но я думаю сейчас у вас есть понимание того, в каком направлении двигаться. Если у вас есть какие-либо предложения, замечания, поправки, то прошу вас оставлять комментарии.

Скачать исходники проекта (конечный вариант) & Скачать с GIt

Уроки не мои и переводил не я, но отредактировано и очищено от вирусов мной. Уроки с сайта http://lookapp.ru/, им говорим спасибо за такой прекрасный перевод статей. Оригинал статьи - http://www.raywenderlich.com/.