Управление игровыми объектами (GameObjects) с помощью компонентов
В редакторе Unity вы изменяете свойства Компонента используя окно Inspector. Так, например, изменения позиции компонента Transform приведет к изменению позиции игрового объекта. Аналогично, вы можете изменить цвет материала компонента Renderer или массу твёрдого тела (RigidBody) с соответствующим влиянием на отображение или поведение игрового объекта. По большей части скрипты также изменяют свойства компонентов для управления игровыми объектами. Разница, однако, в том, что скрипт может изменять значение свойства постепенно со временем или по получению ввода от пользователя. За счет изменения, создания и уничтожения объектов в заданное время может быть реализован любой игровой процесс.
Обращение к компонентам
Наиболее простым и распространенным является случай, когда скрипту необходимо обратиться к другим компонентам, присоединенных к тому же GameObject. Как упоминалось во разделе Введение, компонент на самом деле является экземпляром класса, так что первым шагом будет получение ссылки на экземпляр компонента, с которым вы хотите работать. Это делается с помощью функции GetComponent. Типично, объект компонента сохраняют в переменную, это делается в C# посредством следующего синтаксиса:
void Start () < Rigidbody rb = GetComponent(); >
В UnityScript синтаксис немного отличается:
function Start () < var rb = GetComponent.(); >
Как только у вас есть ссылка на экземпляр компонента, вы можете устанавливать значения его свойств, тех же, которые вы можете изменить в окне Inspector:
void Start () < Rigidbody rb = GetComponent(); // Change the mass of the object's Rigidbody. rb.mass = 10f; >
Дополнительная возможность, недоступная в окне Inspector — вызов функций экземпляра компонента:
void Start () < Rigidbody rb = GetComponent(); // Add a force to the Rigidbody. rb.AddForce(Vector3.up * 10f); >
Имейте ввиду, что нет причины, по которой вы не можете иметь больше одного пользовательского скрипта, присоединенного к одному и тому же объекту. Если вам нужно обратиться к одному скрипту из другого, вы можете использовать, как обычно, GetComponent, используя при этом имя класса скрипта (или имя файла), чтобы указать какой тип Компонента вам нужен.
Если вы попытаетесь извлечь Компонент, который не был добавлен к Игровому Объекту, тогда GetComponent вернет null; возникнет ошибка пустой ссылки при выполнении (null reference error at runtime), если вы попытаетесь изменить какие-либо значения у пустого объекта.
Обращение к другим объектам
Пусть иногда они и существуют изолированно, все же, обычно, скрипты отслеживают другие объекты. Например, преследующий враг должен знать позицию игрока. Unity предоставляет несколько путей получения других объектов, каждый подходит для конкретной ситуации.
Связывание объектов через переменные
Самый простой способ найти нужный игровой объект — добавить в скрипт переменную типа GameObject с уровнем доступа public:
public class Enemy : MonoBehaviour < public GameObject player; // Other variables and functions. >
Переменная будет видна в окне Inspector, как и любые другие:

Теперь вы можете перетащить объект со сцены или из панели Hierarchy в эту переменную, чтобы назначить его. Функция GetComponent и доступ к переменным компонента доступны как для этого объекта, так и для других, то есть вы можете использовать следующий код:
public class Enemy : MonoBehaviour < public GameObject player; void Start() < // Start the enemy ten units behind the player character. transform.position = player.transform.position - Vector3.forward * 10f; >>
Кроме того, если объявить переменную с доступом public и заданным типом компонента в вашем скрипте, вы сможете перетащить любой объект, который содержит присоединенный компонент такого типа. Это позволит обращаться к компоненту напрямую, а не через игровой объект.
public Transform playerTransform;
Соединение объектов через переменные наиболее полезно, когда вы имеете дело с отдельными объектами, имеющими постоянную связь. Вы можете использовать массив для хранения связи с несколькими объектами одного типа, но связи все равно должны быть заданы в редакторе Unity, а не во время выполнения. Часто удобно находить объекты во время выполнения, и Unity предоставляет два основных способа сделать это, описанных ниже.
Нахождение дочерних объектов
Иногда игровая сцена может использовать несколько объектов одного типа, таких как враги, путевые точки и препятствия. Может возникнуть необходимость отслеживания их в определенном скрипте, который управляет или реагирует на них (например, все путевые точки могут потребоваться для скрипта поиска пути). Можно использовать переменные для связывания этих объектов, но это сделает процесс проектирования утомительным, если каждую новую путевую точку нужно будет перетащить в переменную в скрипте. Аналогично, при удалении путевой точки придется удалять ссылку на отсутствующий объект. В случаях, наподобие этого, чаще всего удобно управлять набором объектов, сделав их дочерними одного родительского объекта. Дочерние объекты могут быть получены, используя компонент Transform родителя (так как все игровые объекты неявно содержат Transform):
using UnityEngine; public class WaypointManager : MonoBehaviour < public Transform[] waypoints; void Start() < waypoints = new Transform[transform.childCount]; int i = 0; foreach (Transform t in transform) < waypoints[i++] = t; >> >
Вы можете также найти заданный дочерний объект по имени, используя функцию Transform.Find:
transform.Find("Gun");
Это может быть полезно, когда объект содержит дочерний элемент, который может быть добавлен или удален в игровом процессе. Хороший пример — оружие, которое может быть подобрано и выброшено.
Нахождение объектов по имени или тегу
Нахождение игровых объектов в любом месте иерархии доступно всегда, когда у вас есть некоторая информация, по которой их можно идентифицировать. Отдельные объекты могут быть получены по имени, используя функцию GameObject.Find:
GameObject player; void Start()
Объект или коллекция объектов могут быть также найдены по их тегу, используя функции GameObject.FindWithTag и GameObject.FindGameObjectsWithTag:-
GameObject player; GameObject[] enemies; void Start()
Туториал 0: Юнити и 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 (); или GetComponent (); если скрипт на том-же объекте.
Доступ к 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 (редактор) будет пытаться помочь в наборе команд и будут показанны различные возможные варианты.
Можно не только изменять переменные, но и просто запросить их значения.
Теперь посмотрим как работать с другими компонентами. У нашего астероида есть компонент для его отрисовки (рендерер).
Например мы хотим зеркально повернуть астероид вдоль y-оси. Так как наш скрипт уже прикреплён к астероиду, то нам не нужен его поиск в игровом мире. Мы можем получить доступ на компонент командой GetComponent ().. Название компонента можно посмотреть в инспекторе игрового объекта (только осторожно с пробелами). 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 будет пытаться показывать возможные варианты.
Обычно, если планируете работать часто с каким-нибудь компонентом, то лучше сделать ссылку на этот компонент. Для этого вам надо создать ссылочную переменную того же типа, что и компонент (тип переменной будет как и название компонента). И потом указать ссылку на этот компонент GetComponent (). Потом сможете использовать эту переменную для быстрого доступа к компоненту.
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 ();, если хотите получить доступ на отличный от 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. Переменные:
Переменные это именованные области памяти, хранящие определенную информацию. Так как придется работать с переменными, то конечно лучше знать их основные типы:
Spoiler int:
Этот тип переменных может принимать значение целого (не дробного) числа. Переменная лежит в диапазоне от -2 147 483 648 до 2 147 483 647. Обычно используется для подсчета количества объектов или индексации массивов/циклов (о них чуть позже). int score;
int healthPoint = 5 ;
float:
Этот тип для чисел с плавающей запятой (дробные числа). Переменная лежит в диапазоне от -3.4 × 10^38 до +3.4 × 10^38. Обычно используется там, где важна дробная часть (как пример — расстояние до цели). Используйте точку для разделения знаков. float speed;
float distanceToShoot = 10.5 ;
bool:
Это тип переменных имеющих всего два значения true/false (истинно/ложно). Используется в проверке условий (условные операторы). bool isDead;
bool canMove = false;
char:
Тип переменных для символов Юникода. Например отдельные буквы. Используйте одинарные кавычки при инициализации. char litera;
char firstLitera = ‘A’ ;
string:
Тип переменных для символьной последовательности/текст (на самом деле это ссылочная переменная, то есть в ней хранится ссылка на область памяти, где находится эта символьная последовательность. Но её принцип работы похож на обычные переменные). В классе string содержится несколько функций. Одна из них string.Length, позволяющая узнать количество символов в строке. Для инициализации используйте кавычки. string newText;
string enemyText = «Stop! Don’t move»;
//пример использования функции string.Length
int howManyLetters = enemyText.Length; Vector3:
Я поместил вектор3 к обычным переменным, но на самом деле это структура. Структуры довольно похожи на классы, но в отличии от класса со значениями стурктуры можно работать напрямую, а не через ссылку. Поэтому они частично похожи на переменные. Возможны арифметические операции. Далее структура Vector3 содержит довольно много функций для работы с векторами. Смотрите Unity Documentation для более точного описания. При инициализации вектора3 надо создавать новую инстанцию с помощью new или использовать уже существующий вектор3 (тогда будет созданна его копия). Часто используется для определения местоположения объектов или их скорости и направления движения. Vector3 move;
Vector3 enemyPos = new Vector3 ( 1 , 1 , 0 );
//считывание длинны вектора
float vectorLength = enemyPos.magnitude; Существуют и другие векторы. Например Vector2 или Vector4, но принцип их работы не отличается (кроме многомерности). var:
Это ключевое слово, с помощью которого можно задекларировать переменную без обозначения её типа. Тип переменной будет определен позже в контексте программы. var summe;
float a = 1 ;
float b = 1 ;
//переменная summe получит тип float
summe = a + b;
2. Ссылочные переменные классов:
Как уже видно из описания, такие переменные хранят в себе не классы, а только ссылки на области памяти с этими классами. В качестве примера, если у вас есть переменная типа игровой объект с объектом «игрок» и вы присвоите ей значение объект «враг», то ваш «игрок» никуда не пропадёт, вы просто измените ссылку на другой объект в этой переменной. Объект, на который указывает такая ссылка, является классом с названием по типу переменной. Так вы можете создать ссылочную переменную для компонентов и объектов в Юнити. Основной смысл таких переменных в Юнити это доступ к самому классу (объекту/компоненту) или к его функциям и переменным. Ниже приведу несколько примеров:
Spoiler GameObject:
Все объекты в сцене Юнити это игровые объекты. Поэтому GameObject как базовый класс используется довольно часто. Как и большинство классов содержит в себе не только различные переменные, но и функции. Смотрите Unity Documentation для более точного описания. GameObject player;
//поиск оьъекта с тэгом Enemy и создание переменной с ссылкой на него
GameObject enemy = GameObject .FindWithTag ( «Enemy» );
Transform:
Один из очень часто используемых компонентов. Этот компонент есть у любого игрового объекта в Юнити. Отвечает за местоположение и размеры объекта в игровом мире. Так как в Юнити есть упрощённый доступ к компонету Transform у игрового объекта через команду gameobject.transform. , то используется как ссылочная переменная не так уж и часто. Смотрите Unity Documentation для более точного описания. Transform enemyPosition;
//считывание координаты х из трансформ компонента
float x = enemyPosition.position.x;
Sprite:
Класс представляющий 2Д картинку (спрайт) в качестве рессурса в Юнити. Если вы импортируете спрайт в Юнити, то будет автоматически создан новый класс содержащий этот спрайт с его различными настройками. В качестве ссылочной переменной понадобится при работе с компонентом отрисовки спрайтов. Смотрите Unity Documentation для более точного описания. Sprite enemyBullet;
SpriteRenderer:
Класс отвечающий за отрисовку (рендеринг) спрайта на экране. Рессурс для отрисовки берется из Sprite (см. выше). Как и большинство компонентов содержит в себе различные переменные и функции. Смотрите Unity Documentation для более точного описания. SpriteRenderer sr;
//активация переменной, отвечающей за зеркальное отображение по оси Y
GetComponent< SpriteRenderer >().flipY = true ;
С# скрипты и другие компоненты:
Как я уже говорил, ваши скрипты являются компонентами в Юнити. Поэтому ссылочные переменные работают аналогичным образом.
1. создаёте переменную по типу компонента (используйте его название).
2. получаете доступ к компоненту через ссылочную переменную: variable = GetComponent ();
В качестве примера скрипт из вступления: Moving moving;
moving = GetComponent< Moving >();
moving.playerShip.transform.position = new Vector3 ( 1 , 1 , 0 );
Все остальные компоненты работают аналогично. Не стану их описывать, так как их просто огромное количество.
3. Массивы (статические и динамические):
Массив это упорядочненая структура данных состоящая из элементов одного типа. Массивы могут быть не только одномерными, но и многмерными. Все элементы массива проиндексированны, для целенаправленного доступа к ним. Статический массив имеет определённый размер (количество элементов), который нельзя изменить после декларации. Размер динамического массива можно менять. Индексация массива начинается с 0, то есть первый элемент массива имеет индекс [0].
Spoiler статические массивы:
Так как размер такого массива заранее определен, то скорость работы с таким массивом выше. Поэтому используйте статические массивы, если размер массива заранее известен и будет постоянен. //декларация двумерного массива типа int, состояшего из 2*5 элементов
int [,] numbers = new int [ 2 , 5 ];
//декларация одномерного массива типа GameObject, размер массива определяется через инспектор игрового объекта
public GameObject [] enemy;
//декларация одномерного массива типа string, c его последующей инициализацией
string [] animals = new string [2] < "кот" , "собака" >;
динамические массивы:
Динамические массивы используют класс List. Этот класс находится в пространстве System.Collections.Generic, поэтому лучше присоединить это пространство к скрипту с помощью using System.Collections.Generic;. Динамические массивы позволяют различные действия с размером массива. Можно добавлять, вставлять и удалять элементы. Смотрите MSDN для более точного описания. //присоединение пространства для работы с классом List
using System.Collections.Generic;
//декларация динамического массива типа string
List < string >cars = new List < string >();
//добавление к массиву нового элемента
cars.Add( «Запорожец» );
//вставка нового элемента в массив как первый элемент
cars.Insert(0, «Ауди» );
//удаление элемента «Запорожец»
cars.Remove( «Запорожец» );
4. Модификаторы доступа:
В С# переменные могут быть с разным уровнем доступа. Уровень доступа определяет возможность обращения к переменной.
Spoiler private:
По умолчанию (если вы никак не обозначили переменную) она получает приватный уровень доступа. Так же этот уровень можно и указать с помощью private. Доступ к таким перменным возможен только внутри их содержащих классов. float speed;
private int score;
public:
Уровень доступа public даёт неограниченный доступ. Например такие переменные можно изменить из других классов. Так же переменная с публичным доступом будет видна в редакторе, это даёт возможность её настройки через инспектор. У компонентов в Юнити публичный уровень доступа. public float speed;
public GameObject player;
protected:
Уровень доступа для базового и наследуемых классов.
5. Области видимости(действия) переменных:
Место декларации переменной определяет область её видимости в содержащем скрипте.
Spoiler глобально в классе:
Переменная декларированная в классе, но не входящая в функции этого класса может быть использована глобально всеми функциями этого класса.
локально в функции:
Переменная декларированная в функции, может быть использована только в этой функции.
локально в цикле:
Область видимости такой переменной ещё меньше, она используется только локально в пределах цикла. using UnityEngine;
using System.Collections;
public class Moving : MonoBehaviour
//переменная speed может быть использованна любой функцией этого класса
public int speed;
void RandomFunction () //переменная isMoving может быть использованна только в пределах RandomFunction () <. >
bool isMoving = true;
//переменная i может быть использованна только в пределах цикла
for ( int i = speed; i < 0 ; i--) speed = i;
>
isMoving = false;
>
>
6: Арифметические операторы:
С переменными, содержащими значения, возможны различные арифметические действия. Тут приведу только основные примеры.
Spoiler Будьте осторожны при арифметических действиях с разными типами переменных. Если например записать дробный результат в целую переменную, то дробная часть потеряется. float a = 10 ;
float b = 5 ;
float c;
//сложение: c = 10 + 5
c = a + b;
//вычитание: a = 10 — 2
a = a — 2 ;
//деление: b = 8 / 2
b = a / 2 ;
//умножение: с = 10 * 1.5
с = 10 * 1.5 ;
//присвоение: a = 15
a = c;
//инкремент: увеличение а на единицу
a++;
//декремент: уменьшение а на единицу
a—;
//инкремент/декремент: могут стоять до или после переменной, изменяя порядок вычислений
7. Математические функции:
В С# есть структура Mathf для различных математических функций. Смотрите Unity Documentation для более точного описания.
Spoiler //функция вернет значение -1, так как проверочный результат отрицательный
float sign = Mathf .Sign (-5);
8. Условия и логические операторы:
Для разветвления (выбора другого варианта исполнения) программ есть различные условные операторы. Для сравнения вариантов используются логические операторы сравнения.
Spoiler логические операторы сравнения:
Результат сравнения это бинарный результат, то есть ложно или истинно (true/false). Возможны следующие сравнения:
== — равно
!= — не равно
> — больше
< - меньше
>= — больше или равно
условные операторы:
Условные операторы позволяют провести сравнение и выполнить определенную часть кода в зависимости от результата.
if . else:
Краткая форма этого условного оператора состоит только из условия и выполняемой части кода, если условие истинно. if (hp isDead = true ;
canFight = false ;
> Полная форма состоит из условия, выполняемой части кода, если условие истинно и выполняемой части кода, если условие ложно. if (hp isDead = true ;
canFight = false ;
> else isDead = false ;
canFight = true ;
> Возможно объединение различных условий в одну конструкцию. if (hp isDead = true ;
canFight = false ;
> else if (hp == 100 ) isDamaged = false ;
> бинарные логические операторы:
С помощью таких операторов можно создавать более сложные условия, состоящих из нескольких подусловий.
&& — логическое И . Будет истинно, если оба условия истинны. Или ложно, если оба или одно из них ложно.
|| — логическое ИЛИ . Будет истинно, если одно из условий истинно. И ложно, если оба условия ложны.
if (hp < 0 || hp >0 ) isZero = false ;
> else isZero = true ;
> switch:
Этот логический оператор используется для обработки множества вариантов при нахождении истинного значения. string direction = «left» ;
//искомое выражение
switch (direction) //возможный вариант, если совпадает, то выполняется последующий код
case ( «up» ):
move = Vector2 .up;
isMoving = true ;
//прерывает дальнейшее выполнение логического оператора (при совпадении варианта)
break ;
//возможный вариант, если совпадает, то выполняется последующий код
case ( «down» ):
move = Vector2 .down;
isMoving = true ;
//прерывает дальнейшее выполнение логического оператора (при совпадении варианта)
break ;
//в данном примере выполнится эта часть кода и работа логического оператора будет завершена
case ( «left» ):
move = Vector2 .left;
isMoving = true ;
//прерывает дальнейшее выполнение логического оператора (при совпадении варианта)
break ;
//возможный вариант, если совпадает, то выполняется последующий код
case ( «right» ):
move = Vector2 .down;
isMoving = true ;
//прерывает дальнейшее выполнение логического оператора (при совпадении варианта)
break ;
//если ни один из вариантов не был истинной, то выполняется часть кода стоящая после default
default :
move = Vector2 .zero;
isMoving = false ;
//прерывает дальнейшее выполнение логического оператора
break ;
>
9. Циклы:
Циклы используются для многократного исполнения определенных задач (фрагментов кода).
Spoiler for:
Этот цикл выполняется определённое количество раз. //для примера, декларация массива на 10 целых элементов
public int [] numbers = new int [ 10 ];
//int i = 0; декларация с инициализацией счетчика цикла
//i < 10;проверка возможности запуска следующего цикла (пока условие истинно)
//i++; изменение счетчика цикла
for ( int i = 0 ; i < 10 ; i++) //исполняемая часть кода при каждом новом цикле
//в данном примере, заполнение массива числами от 0 до 9
numbers [i] = i;
> Хоть это и не обязательно, но советую использовать в счетчиках циклов целые значения, чтобы избежать ошибок от дробных вычислений.
while:
Этот цикл выполняется пока условие истинно. //для примера, декларация массива на 10 целых элементов
public int [] numbers = new int [ 10 ];
//декларация с инициализацией переменной к, которую будем использовать для проверки условия цикла
int k = 9 ;
//проверка условия цикла, выполнить цикл, если условие истинно
while (k >= 0 ) //выполняемый фрагмент кода в фигурных скобках
//заполнение массива числами от 9 до 0 в обратном порядке
numbers [k] = k; //уменьшение переменной на 1 (чтобы получить ложное условие)
k—;
> Будьте осторожны с циклами, чтобы не получить «вечный» цикл, когда условие всегда истинно и цикл повторяется вечно.
do. while:
Отличие этого цикла от цикла while только в том, что проверка условия производится после выполнения фрагмента кода. Поэтому этот цикл выполнится всегда как минимум один раз. //декларация публичной текстовой переменной
public string testText;
//декларация и инициализация целой переменной, для условия цикла
int j = 0 ;
//выполнение фрагмента кода цикла
do //добавление к текстовой переменной цифры преобразованной в текстовый символ
testText = testText + j.ToString();
//увеличение j на единицу
j ++;
//проверка условия, повторить цикл, если условие истинно
> while (j < 10 );
//результат работы цикла это текст «0123456789»
foreach:
Цикл для работы с массивами. Перебирает все элементы по одному. //декларация и инициализация массива
int [] numbers = new int [ 5 ] < 21 , 28 , 13 , 4 , 15 >;
//декларация массива для копирования
int [] numbersCopy = new int [ 5 ];
//вспомогательная переменная для индекса массива
int i = 0 ;
//запуск цикла проходяшего по всем элементам массива
//переменная number должна совпадать с типом элементов массива
foreach ( int number in numbers) //копирование текущего элемента в другой массив
numbersCopy [i] = number;
//увеличение индекса на 1, для доступа к следующему элементу в массиве для копии
i++;
>
//итог цикла это копирование элементов в новый массив
операторы break и continue:
Эти операторы помогают управлять исполнением циклов.
break — этот оператор полностью прекращает цикл и переходит к дальнейшему исполнению программы
continue — этот оператор прекращает текущее исполнение цикла и начинает новое
//вспомогательная переменная для индекса массива
int i = 0 ;
//запуск цикла проходяшего по всем элементам массива
//переменная number должна совпадать с типом элементов массива
foreach ( int number in numbers) //копирование текущего элемента в другой массив
numbersCopy [i] = number;
//увеличение индекса на 1, для доступа к следующему элементу в массиве для копии
i++;
//как переменная для индекса стала равна 3, прекращаем исполнение цикла
if (i == 3 )
break ;
>
10. Функции:
Функция это обособленный фрагмент кода для выполнения определенной задачи. Функция может вызываться многократно в тех случаях, когда нужно решить эту задачу. Это позволяет избежать повторения кода. Функции могут быть как сделанные вами индивидуально для достижения ваших целей, так и заготовками от Юнити для стандартных решений. Функции являются частью классов. Функции, как и переменные, имеют различные уровни доступа. К public функциям возможен доступ из других классов, доступ к private функциям возможен только внутри их содержащих классов. Для работы функциям могут понадобиться входные данные — аргументы (функции могут использовать и глобальные переменные их содержащих классов). После своей работы функция может возвращать какой-то результат (используйте Return для возвращения результата). Тип функции должен соответсвовать типу возвращаемого результата. Существуют и функции не возвращающие результат, такие функции имеют тип void.
Spoiler стандартные функции от Юнити:
Существует много различных функций в Юнити. Тут я точно не смогу показать и малой части. Поэтому пока ограничимся несколькими примерами. В дальнейшем в туториалах будут попадаться и новые функции.
Instantiate:
Эта функция помещает клон объекта в сцену. Обычно используется для клонирования префаба(образца). //создаёт клон префаба в сцене на заданной позиции (Vector3) с заданным вращением (Quaternion)
Instantiate (prefab, position, rotation); Destroy:
Эта функция удаляет объект из сцены. //удаляет игровой объект из сцены после заданного интервала (или сразу, если время не указать)
Destroy (gameobject, time); функции классов:
Часто, если используете какой-то компонент (класс), то он содержит различные функции для работы с ним. Список этих функций можно увидеть в редакторе C# после набора ссылочной переменной (+ точка) указывающей на этот компонент. //изменяет дробную переменную в компоненте аниматор
GetComponent < Animator >().SetFloat ( «Speed» , 0 );
функции пользователя:
Конечно можно использовать и свои функции для выполнения нужных задач. //функция нанесения повреждений
public void DoDamage ( int damage) hp = hp — damage;
if (hp Destroy (gameObject);
>
11. События:
События похожи на функции, только их запуск привязан к различным событиям. Приведу только несколько примеров.
Spoiler void Awake() //это событие вызывается один раз при загрузке скрипта. Обычно используется для инициализации объектов.
void Awake() //C#
> void Start() //это событие вызывается один раз после того, как все void Awake() были выполнены. Используется для инициализации переменных.
void Start() //C#
> void Update() //это событие вызывается заново каждый новый кадр. Обычно содержит основной код для управления игровым объектом.
void Update() //C#
> void LateUpdate() //это событие вызывается заново каждый новый кадр после обработки всех Update(). Обычно используется для управления камерой, чтобы отрисовать уже готовую сцену.
void LateUpdate() //C#
> void FixedUpdate() //это событие вызывается через фиксированный интервал (стандарт 0.02). Используется в расчетах физики (физика не считается в Юнити каждый кадр).
void FixedUpdate() //C#
> OnTriggerEnter2D()
Возможны и другие варианты срабатывания триггера (Enter, Exit, Stay). //это событие вызывается при попадании коллайдера2Д в триггер2Д. Переменная other будет содержать ссылку на попавший в триггер объект.
void OnTriggerEnter2D( Collider2D other) //C#
>
Позиция объекта не меняется
В скрипте я создаю объект, добавляю ему родителя canvas и привязываю к нему изображение.
Я хочу, чтобы он оказался в позиции (x,y,z), но изображение (а значит и объект) появляется в центре экрана и не меняет позицию. Пробовал по-разному менять положение, пробовал и у объекта, и у привязанного изображения менять позицию — все также в центре экрана. Причем изменение размера через rectTransform.sizeDelta работает отлично.
GameObject newObject = new GameObject("ObjectName"); newObject.transform.SetParent(GameCanvas.transform, false); RectTransform rectTransform = newObject.AddComponent(); rectTransform.sizeDelta = new Vector2(100, 100); Image image = newObject.AddComponent(); image.sprite = Sprites[spriteNum]; newObject.GetComponent().anchoredPosition = new Vector3(targetPosition.x, targetPosition.y, floatz);
Пробовал разные варианты, не помогло (anchoredPosition, localPosition, position):
1 вариант. newObject.GetComponent().anchoredPosition = new Vector3(targetPosition.x, targetPosition.y, floatz); 2 вариант. newObject.transform.localPosition = new Vector3(targetPosition.x, targetPosition.y, floatz); newObject.transform.TransformPoint(new Vector3(targetPosition.x, targetPosition.y, floatz));` 3 вариант. rectTransform = newObject.AddComponent(); rectTransform.position = new Vector3(targetPosition.x, targetPosition.y, floatz); 4 вариант. transform.GetComponent().localPosition = new Vector3(targetPosition.x, targetPosition.y, floatz); image.transform.position = new Vector3(targetPosition.x, targetPosition.y, floatz);` `image.rectTransform.localPosition = new Vector3(targetPosition.x, targetPosition.y, floatz); 5 вариант. newObject.GetComponent().transform.position = new Vector3(targetPosition.x, targetPosition.y, floatz); image.rectTransform.anchoredPosition = new Vector3(targetPosition.x, targetPosition.y, floatz); newObject.GetComponent().localPosition = new Vector3(targetPosition.x, targetPosition.y, floatz);
Update Поменял скрипт: в самом скрипте ничего не создаю, только передаю туда уже созданное на canvas’e изображение. Пытаюсь изменить его позицию в скрипте:
image.rectTransform.position = new Vector3(targetPosition.x, targetPosition.y, floatz); Debug.Log(targetPosition.x); Debug.Log(targetPosition.y);
Но изображение появляется не в targetPosition, a в левом нижнем углу экрана. Пробовал дебажить позицию: значения адекватные — в рамках экрана(не в центре и не в левом нижнем углу). Непонятно, почему такое происходит.
Как изменить позицию дочернего объекта при создании (куратина)?
Я хочу при создании дочернего игрового объекта, допустим Cube, изменить его положение относительно родителя, по вектору родителя, но пока я в тупике.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Strike : MonoBehaviour < public GameObject Cube; public void SetParent(GameObject Sphere) < Cube.transform.parent = Sphere.transform; >void Update() < if (Input.GetMouseButtonDown(0)) StartCoroutine(instObj()); Cube.transform.position = transform.position; >IEnumerator instObj() < yield return new WaitForSeconds(0f); Instantiate(Cube); >>
На что меня только хватило, это
instCube = Instantiate(Cube) as GameObject; instCube.transform.localPosition = new Vector3(0f, 25f, 0); instCube.transform.position = transform.position;
Но это ни сколько не помогло. Он так же создаёт дочерний объект внутри родительского объекта. Даже при таком большом значении Y.
Подскажите пожалуйста. В каком направлении мне двигаться, что бы решить эту задачу. Так как опыта пока у меня мало. Что почитать, что изучить?
- Вопрос задан более трёх лет назад
- 3958 просмотров
Комментировать
Решения вопроса 0
Ответы на вопрос 1

Denis Gaydak @MrMureno Куратор тега Unity
VR for all
что ж давайте попробуем разобрать
instCube.transform.localPosition = new Vector3(0f, 25f, 0); instCube.transform.position = transform.position;
в первой строке вы сместили локальную позицию (относительно родителя, если он в этот момент есть).
потом следующей строкой вы присваиваете ГЛОБАЛЬНЫЕ координаты этому же объекту.
Вы перезатираете значения ранее установленные (возможно думая что смещение как-то дополнительно останется).
Вам надо или все время прибавлять смещение для дочернего объекта или перестать его двигать(изменять) и менять координаты/двигать родителя.
Плюс после Instantiate — не забудьте сначала родителя назначить, по умолчанию там Null и объект будет просто в корне иерархии.
Ответ написан более трёх лет назад
Нравится 1 3 комментария
Вячеслав Марвин @Marcheslav Автор вопроса
Спасибо за ответ.
Можете пожалуйста ещё подсказать. Если я убрал
instCube.transform.position = transform.position;
то он у меня создаётся в одном и том же месте. Я старался этой строчкой создать изменение позиции, при изменении позиции родителя. Чем я могу её заменить, чтобы при движении родителя, дочерний объект создавался на определённом расстоянии от родителя по вектору его?
Таким образом, я хочу чтобы при нажатии кнопки, создавался объект и после чего отправлялся по вектору камеры вперёд и толкал другой объект, по типу снаряда. По-этому посчитал, что функция «Луч» не подойдёт для меня.
Так же, хотел бы узнать, как добавлять родителя в «Instantiate», так как скрипт уже висит на родителе. И в этой функции указываю, кто родитель
public void SetParent(GameObject Sphere)

Denis Gaydak @MrMureno Куратор тега Unity
давайте попробую еще раз пояснить о чем я.
instCube.transform.parent = Sphere.transform; instCube.transform.localPosition = new Vector3(0f, 25f, 0)
в этом случае сместит относительно родителя,корректно и правильно.
instCube.transform.localPosition = new Vector3(0f, 25f, 0) instCube.transform.parent = Sphere.transform;
в этом случае сначала сместит, а потом уже станет дочерним объектом и как там сместилось — не ясно и может быть бардак.
а про изменение позиции — если объект стал дочерним. то при изменении позиции родителя — он и так будет двигаться вместе с родителем.
если вам нужно чтоб он и с родителем двигался и еще дополнительно относительно родителя смещался — то вам надо на дочернем объекте писать свое движение. как ? это уже смотреть уроки и примеры, чтоб понять принципы основные.
Вячеслав Марвин @Marcheslav Автор вопроса
К сожалению. В данном случаем
public GameObject Cube; private GameObject instCube; public void SetParent(GameObject Sphere) < //Cube.transform.parent = Sphere.transform; instCube.transform.parent = Sphere.transform; >void Update() < if (Input.GetMouseButtonDown(0)) StartCoroutine(instObj()); Cube.transform.position = transform.position; >IEnumerator instObj() < yield return new WaitForSeconds(0f); instCube = Instantiate(Cube) as GameObject; //instCube.transform.parent = Sphere.transform; instCube.transform.localPosition = new Vector3(0f, 0.5f, 0.7f); //instCube.transform.localPosition = transform.localPosition; >
объект создаётся с заданными позициями. Но при перемещении родителя, последовательно создаётся в том же самом месте, а не с новыми координатами родителя.