Туториал 0: Юнити3Д и C# скрипт - основы

Вступление:

Скажу честно, я совсем не программист, так что вполне возможно будут какие-то неточности. Это не игровой туториал. Я постараюсь рассказать, как работают скрипты в Юнити 3Д.

С# относительно не сложный язык. Точнее сама основа языка простая, сложности начинаются, когда уже совсем далеко углубляешься в него. Но на самом деле с помощью Юнити3Д можно делать простенькие игрушки только используя основы С#. C# объектно ориентированный язык програмирования. Это пишут все, но как я понял большинство не особо понимает смысл этого, как и я. Но учитывая, что придется работать со скриптами, это не так и важно для начала. Не стоит пытаться создать один огромный трудно понимаемый скрипт для всей игры. Пробуйте разделить игру на части. В свою очередь эти части могут взамодействовать друг с другом. Используйте эти части как кирпичи для создания итоговой игры. В C# используется class, который исполняет роль кирпичика. В Юнити довольно просто создать такой class. Создаете новый C# скрипт и называете его по желанию. И, грубо говоря, созданный скрипт и выполняет роль class в Юнити3Д. Далее можно использовать скрипт как компонет для игрового объекта.

Небольшой пример:

Как пример, решили сделать простенькую космическую стрелялку. Что-то похожее на этот шедевр:
космическая игра эскиз
В Юнити3Д это будет сцена с множеством игровых объектов: фон, звезды, камера, враг, астероид, игрок... Каждый объект в игре это GameObject. И все GameObjects состоят из Components. Некотрые компоненты заранее заготовленны в Юнити3Д. Как пример: Transform (определяет позицию объекта в пространстве), Sprite Renderer (рисует заданный объект в мире), UI Text (показывает текст в интерфейсе) и много других компонентов. И ваши C# скрипты тоже будут компонентами, которые будут присоединяться к объектам. У C# скриптов есть доступ к другим компонетами. По сути, вся работа со скриптами это просто работа с переменными.

Как использовать C# скрипты ?

Создадим empty GameObject(пустышку) в сцене. У этого объекта уже будет Transform компонент (у каждого объекта в Юнити3Д есть Transform компонент). У созданного объекта пока нет компонента отвечающего за отрисовку, поэтому мы не увидим его игре. Поэтому добавим ешё один компонент Sprite Renderer. И укажем какую картинку должен рисовать этот компонент, например астероид. Тогда мы сможем увидеть астероид в игре.
Юнити эдитор
Просто смотреть на астероид скучно, поэтому можно сделать скрипт для его управления.
Не забываем, это не игровой туториал, только вкратце о принципе работы с Юнити3Д.
Создаём новый С# скрипт и называем его(например Moving, так как этот скрипт будет перемещать астероид). Если мы откроем скрипт, то увидим следующую заготовку:
using UnityEngine;
using System.Collections;

public class Moving : MonoBehaviour {

// Use this for initialisation
void Start () {
}

//Update is called once per frame
void Update () {
}
}
Небольшое объяснение строчек:
Using UnityEngine; Добавляем доступ на UnityEngine библиотеку. Это набор различных классов/функций от Юнити3Д.
using System.Collections; Это стандартная библиотека от Microsoft .NET.
public class Moving : MonoBehaviour { public даёт возможность доступа на этот скрипт с других скриптов. class Moving название нашего скрипта/класса. MonoBehaviour это основной класс от Юнити3Д, в нём такие важные функции как Update(), Start (). Смотрите Unity Documentation для более точного описания.
// Use this for initialization Это просто комментарий. Только для вашего удобства.
void Start () {} void Обозначение, что последующая функция не будет возвращать параметр (то-есть никаких вернувшихся переменных после её вызова не намечается). Start () {} эта функция выполнится один раз при старте скрипта.
void Update () {} Эта функция будет выполняться заново каждый игровой кадр.

Теперь мы можем передвинуть астероид в центр игрового мира (находится на координатах (0, 0, 0)). Позиция игрового объекта определяется в Transform компоненте. Нам надо присоединить наш скрипт к астероиду. С этого скрипта мы можем менять переменные в Transform компонент. Эти переменные и определяют положение объекта в сцене.
трансформ компонент

Например мы хотим переместить астероид в центр при старте игры.

Если надо получить доступ на какой-то компонент игрового объекта, то обычно надо сделать следующие шаги:

1. Найти нужный игровой объект: Тут много разных вариантов, все зависит от условий.
- Можно использовать поиск из C# скрипта GameObject.Find ("Name") / GameObject.FindWithTag ("Tag"). Это поиск по имени или тэгу.
- можно создать переменную типа игровой объект public GameObject; и потом в эдиторе указать к какому объекту привязана эта переменная.
- можно получить линк на игровой объект с различных функций. Например: (Trigger, Raycast,...).
- и не нужен поиск объекта, если скрипт уже находится на этом объекте.

2. Получить доступ на компонент этого объекта: через команду gameobject.GetComponent <ComponentName>(); или GetComponent <ComponentName>(); если скрипт на том-же объекте.

Доступ к Transform компоненту:

Для удобства есть простой путь доступа на свой Transform компонент, если компонент и скрипт находятся на одном объекте. Просто используете transform. Для доступа на другие компоненты используйте GetComponent (в старых версиях Юнити таких быстрых доступов было больше, не забываем об этом при работе с неактульными туториалами)
Позиция в игровом мире это вектор (x,y,z). Если мы хотим поместить астероид в центр, то нам надо изменить координаты вектора на (0,0,0). C# строчка будет:
transform.position = new Vector3 (0, 0, 0);
Если мы хотим сделать это перемещение один раз в начале игры, то нам надо поместить эту строчку в функцию void Start () {}. И наш скрипт будет:
using UnityEngine;
using System.Collections;

public class Moving : MonoBehaviour {

// Use this for initialisation
void Start () {
//сменить местоположение игрового объекта
transform.position = new Vector3 (0, 0, 0);
}

//Update is called once per frame
void Update () {
}
}
Если мы запустим нашу игру, то астероид будет перемещён в центр игрового мира.
Теперь посмотрим на Transform компонент:
трансформ компонент
Используйте команду transform. для доступа к Transform компоненту на том-же игровом объекте. И в нём можно изменить позицию, вращение и размер игрового объекта (пример: transform.localScale = new Vector3 (0.5F, 1, 1);). Ваш IDE (редактор) будет пытаться помочь в наборе команд и будут показанны различные возможные варианты.
C# эдитор
Можно не только изменять переменные, но и просто запросить их значения.

Теперь посмотрим как работать с другими компонентами. У нашего астероида есть компонент для его отрисовки (рендерер).
рендерер компонент
Например мы хотим зеркально повернуть астероид вдоль y-оси. Так как наш скрипт уже прикреплён к астероиду, то нам не нужен его поиск в игровом мире. Мы можем получить доступ на компонент командой GetComponent <ComponentName>().. Название компонента можно посмотреть в инспекторе игрового объекта (только осторожно с пробелами). C# команда будет:
GetComponent< SpriteRenderer >().flipY = true;
И скрипт будет:
using UnityEngine;
using System.Collections;

public class Moving : MonoBehaviour {

// Use this for initialisation
void Start () {
//сменить местоположение игрового оьъекта
transform.position = new Vector3 (0, 0, 0);
//доступ к компоненту отрисовки и сменить переменную, отвечающую за зеркальное отображение по оси Y
GetComponent< SpriteRenderer >().flipY = true;
}

//Update is called once per frame
void Update () {
}
}
Не забываем, при наборе кода ваш IDE будет пытаться показывать возможные варианты.
C# эдитор
Обычно, если планируете работать часто с каким-нибудь компонентом, то лучше сделать ссылку на этот компонент. Для этого вам надо создать ссылочную переменную того же типа, что и компонент (тип переменной будет как и название компонента). И потом указать ссылку на этот компонент GetComponent <ComponentName>(). Потом сможете использовать эту переменную для быстрого доступа к компоненту.
using UnityEngine;
using System.Collections;

public class Moving : MonoBehaviour {

//декларация ссылочной переменной для компонента отрисовки спрайтов
SpriteRenderer sr;

// Use this for initialisation
void Start () {
//сменить местоположение игрового оьъекта
transform.position = new Vector3 (0, 0, 0);
//делаем ссылку на компонент
sr = GetComponent< SpriteRenderer >();
//меняем переменную, отвечающую за зеркальное отображение по оси Y
sr.flipY = true;
}

//Update is called once per frame
void Update () {
}
}
Похожим образом мы можем получить и доступ к компонентам на других игровых объектах.
Для начала нам надо "найти" другой игровой объект. Как я уже и писал, тут довольно много различных вариантов в зависимости от ситуации. Например в сцене есть другой игровой объект. Его имя Ship и тэг Player (Tag используется для идентификации объектов (похоже на имя, только скорость обработки быстрее)).
Юнити эдитор

1. Если игровой объект находится в сцене с самого начала и не пропадает до запроса к нему, то мы можем создать открытую ссылочную переменную playerShip (с тимпом GameObject) в нашем скрипте и добавить этот объект в эту переменную через инспектор (просто перетащить объект на переменную в редакторе). И тогда через эту переменную у нас будет доступ на игровой объект. Например мы хотим поместить корабль на позицию (1, 1, 0) при старте игры. C# строчка будет:
playerShip.transform.position = new Vector3 (1, 1, 0);
using UnityEngine;
using System.Collections;

public class Moving : MonoBehaviour {

//декларация ссылочной переменной для компонента отрисовки спрайтов
SpriteRenderer sr;
//публичная ссылочная переменная для корабля игрока
public GameObject playerShip;

// Use this for initialisation
void Start () {
//сменить местоположение игрового оьъекта
transform.position = new Vector3 (0, 0, 0);
//делаем ссылку на компонент
sr = GetComponent< SpriteRenderer >();
//меняем переменную, отвечающую за зеркальное отображение по оси Y
sr.flipY = true;
//получаем доступ к transform компоненту игрока и меняем его местоположение
playerShip.transform.position = new Vector3 (1, 1, 0);
}

//Update is called once per frame
void Update () {
}
}
Юнити эдитор

2. Мы можем найти игровой объект в сцене через поиск и сделать на него ссылку через скрипт с помощью команды GameObject.Find ("Name") or GameObject.FindWithTag ("Tag"). Поиск по тэгу производится быстрее чем по имени. C# строчка будет:
playerShip = GameObject.Find("Ship");
или:
playerShip = GameObject.FindWithTag("Player");
using UnityEngine;
using System.Collections;

public class Moving : MonoBehaviour {

//декларация ссылочной переменной для компонента отрисовки спрайтов
SpriteRenderer sr;
//публичная ссылочная переменная для корабля игрока
public GameObject playerShip;

// Use this for initialisation
void Start () {
//сменить местоположение игрового оьъекта
transform.position = new Vector3 (0, 0, 0);
//делаем ссылку на компонент
sr = GetComponent< SpriteRenderer >();
//меняем переменную, отвечающую за зеркальное отображение по оси Y
sr.flipY = true;
//ищем в сцене корабль игрока и делаем ссылку на него
playerShip = GameObject.FindWithTag("Player");
//получаем доступ к transform компоненту игрока и меняем его местоположение
playerShip.transform.position = new Vector3 (1, 1, 0);
}

//Update is called once per frame
void Update () {
}
}
Не забываем: используйте GetComponent <ComponentName>();, если хотите получить доступ на отличный от Transform компонент (transform. это просто укороченный вариант для этого компонента). Например если хотите зеркально повернуть корабль по X-оси из скрипта на астероиде, то комманда в C# будет:
playerShip.GetComponent < SpriteRenderer > ().flipX = true;
using UnityEngine;
using System.Collections;

public class Moving : MonoBehaviour {

//декларация ссылочной переменной для компонента отрисовки спрайтов
SpriteRenderer sr;
//публичная ссылочная переменная для корабля игрока
public GameObject playerShip;

// Use this for initialisation
void Start () {
//сменить местоположение игрового объекта
transform.position = new Vector3 (0, 0, 0);
//делаем ссылку на компонент
sr = GetComponent< SpriteRenderer >();
//меняем переменную, отвечающую за зеркальное отображение по оси Y
sr.flipY = true;
//ищем в сцене корабль игрока и делаем ссылку на него
playerShip = GameObject.FindWithTag("Player");
//получаем доступ к transform компоненту игрока и меняем его местоположение
playerShip.transform.position = new Vector3 (1, 1, 0);
//указываем, что работаем с кораблем игрока, получаем доступ к компоненту отрисовки и зеркально отображаем по оси Х
playerShip.GetComponent < SpriteRenderer > ().flipX = true;
}

//Update is called once per frame
void Update () {
}
}
3. Мы можем получть объект с различных игровых функций. Я покажу некотрые из них в последующих туториалах (как пример: OnTriggerEnter2D (Collider2D otherCollider) {} Ссылка на объект попавший в триггер будет в otherCollider переменной).

Наверняка уже заметили, что создание игры в Юнити - это просто работа с переменными в компонентах игровых объектов (я сейчас не говорю о создании музыки, моделей, текстов ... Это уже совсем другая история). Теперь посмотрим на основы С# скриптов. Описание будет довольно коротким и затронет только базовые части. Так что для подробностей придется немножко погуглить или побродить по форумам.


Основы С# скриптов:

1. Переменные:

Переменные это именованные области памяти, хранящие определенную информацию. Так как придется работать с переменными, то конечно лучше знать их основные типы:

2. Ссылочные переменные классов:

Как уже видно из описания, такие переменные хранят в себе не классы, а только ссылки на области памяти с этими классами. В качестве примера, если у вас есть переменная типа игровой объект с объектом "игрок" и вы присвоите ей значение объект "враг", то ваш "игрок" никуда не пропадёт, вы просто измените ссылку на другой объект в этой переменной. Объект, на который указывает такая ссылка, является классом с названием по типу переменной. Так вы можете создать ссылочную переменную для компонентов и объектов в Юнити. Основной смысл таких переменных в Юнити это доступ к самому классу (объекту/компоненту) или к его функциям и переменным. Ниже приведу несколько примеров:

3. Массивы (статические и динамические):

Массив это упорядочненая структура данных состоящая из элементов одного типа. Массивы могут быть не только одномерными, но и многмерными. Все элементы массива проиндексированны, для целенаправленного доступа к ним. Статический массив имеет определённый размер (количество элементов), который нельзя изменить после декларации. Размер динамического массива можно менять. Индексация массива начинается с 0, то есть первый элемент массива имеет индекс [0].

4. Модификаторы доступа:

В С# переменные могут быть с разным уровнем доступа. Уровень доступа определяет возможность обращения к переменной.

5. Области видимости(действия) переменных:

Место декларации переменной определяет область её видимости в содержащем скрипте.

6: Арифметические операторы:

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

7. Математические функции:

В С# есть структура Mathf для различных математических функций. Смотрите Unity Documentation для более точного описания.

8. Условия и логические операторы:

Для разветвления (выбора другого варианта исполнения) программ есть различные условные операторы. Для сравнения вариантов используются логические операторы сравнения.

9. Циклы:

Циклы используются для многократного исполнения определенных задач (фрагментов кода).

10. Функции:

Функция это обособленный фрагмент кода для выполнения определенной задачи. Функция может вызываться многократно в тех случаях, когда нужно решить эту задачу. Это позволяет избежать повторения кода. Функции могут быть как сделанные вами индивидуально для достижения ваших целей, так и заготовками от Юнити для стандартных решений. Функции являются частью классов. Функции, как и переменные, имеют различные уровни доступа. К public функциям возможен доступ из других классов, доступ к private функциям возможен только внутри их содержащих классов. Для работы функциям могут понадобиться входные данные - аргументы (функции могут использовать и глобальные переменные их содержащих классов). После своей работы функция может возвращать какой-то результат (используйте Return для возвращения результата). Тип функции должен соответсвовать типу возвращаемого результата. Существуют и функции не возвращающие результат, такие функции имеют тип void.

11. События:

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

Тут были показаны только самые основы С# скриптов. Более детальное описание отдельных частей C# скриптов смотрите в дальнейших туториалах.

Reklame: