Как стать автором
Обновить

Урок на Unity. Интерактивное взаимодействие игрока с окружающими предметами в 3D с помощью меток

Уровень сложностиСредний
Время на прочтение15 мин
Количество просмотров7.4K

Взаимодействие с окружением без коллайдеров и лучей, на простой математике

Бонус урока. Делаем простое пианино!

Наверно всем знаком такой элемент в игре, как всплывающая иконка рядом с игровым объектом, позволяющая с ним интерактивно взаимодействовать.

Интерактивную иконку для взаимодействия с окружением будем далее называть меткой.

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

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

Это мой первый урок по программированию на Unity, поэтому прошу сильно не бить критика будет уместна. Если что не очень понятно – эти моменты разберу подробнее.

Система взаимодействия метки с игроком будет состоять из 2 скриптов.

Первый скрипт будет расположен на самой метке. А метка будет крепиться к нужному объекту в 3D мире (хотя можно ипользовать и чисто одну метку, например, для заскриптованных действий).

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

Поэтому понадобится общий скрипт.

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

Приступим!

Урок будет разделен на 2 главы.

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

А во второй главе подробно рассмотрим, как все работает изнутри. Будет полезно для тех, кто хочет доделать алгоритм для себя.

Содержание

  1. Делаем метки для взаимодействия с окружением:

    1. Как работает

    2. Как добавлять

    3. Параметры меток

  2. Изучаем скрипты:

    0. Основа

    1. Функция LateUpdate

    2. Функция ChangeState

    3. Функция NewActiveMark

    4. Функция SetNoticeTxt

    5. Функция MarkReady

    6. Функция UseMark

Глава 1. Делаем метки для взаимодействия с окружением

Принцип работы метки:

Подходя на определенное расстояние к объекту с меткой – метка становится видимой (=расстояние включения ).

Подходя очень близко –
включается активный режим метки (иконка меняет цвет) и появляется текст подсказки, например: “Нажмите клавишу E для открытия двери”. Можно нажать нужную кнопку и будет выполнено ваше действие (скрипт). После этого метку можно удалить с объекта или оставить для дальнейшего использования.

Метка является спрайтом, расположенным в мировых координатах (не видно за другими объектами). Ее можно смещать относительно самого объекта в удобное положение (спереди, сбоку, выше и т.д.).

Для активации метки среди большого количества объектов, первостепенно проверяется угол положения метки относительно камеры. Наиболее близкая к центру экрана, далее уже проверяется на нужное расстояние (подробнее во второй главе).

С помощью данного функционала можно реализовать недоступность нажатия метки, расположенной через стену от игрока или за другими объектами. Для этого в параметрах метки можно поставить маленькое расстояние активации, и тогда метка станет активной только при близком нахождении игрока.

Посмотреть работу интерактивных меток в действии можно на видео (сцена MarkScene из проекта):

Передвижение W A S D или стрелки на клавиатуре. Использовать метку – клавиша E или клик левой кнопкой мыши. Пауза – пробел.

Архив всего проекта в конце статьи.

Все что вы видите на сцене, сделано для примера работы с метками. В вашем проекте можно сделать все что угодно.


Теперь рассмотрим, как добавлять метки в ваш проект. Для примера создадим новую сцену TestScene (В вашем случае это может быть уже готовая сцена в проекте). И выполним минимальный набор действий для работоспособности метки.

Важно: часть описанных действий, возможно, придется переделать под ваш проект (в основном это переименование имен в скрипте).

Копируем в ваш проект папку Mark из исходника:

Создаем на сцене отдельный элемент для скриптов (чтобы они работали при запуске приложения). Если у вас есть такой элемент, продолжаем:

Добавим этому элементу тег SceneScript, чтобы другие объекты могли найти его и по тегу:

Новый тег можно создать, выбрав в этом списке Add Tag...

Перенесем в этот элемент скрипт Assets – Prefabs – Mark – Script – MainScriptMark.cs :

В поле Scene Script переносим ваш скрипт сцены.

Под скриптом сцены понимается скрипт вашего проекта, в котором уже есть какая-то логика и можно просто ее доработать. Или создать новый скрипт.

Если имя вашего основного скрипта сцены отличается от SceneScript

Переименуйте его в скриптах Mark.cs и MainScriptMark.cs:

Чтобы задать свои клавиши для активации меток (по умолчанию, клавиша E или левый клик мыши), можно отредактировать код в функции Update скрипта MainScriptMark.cs:

if (Input.GetKeyDown(KeyCode.E) || Input.GetMouseButtonDown(0)) 
{
    if (active_mark != null) 
    UseMark();
}

В вашей игре может понадобиться пауза.

В текущем проекте пауза реализована таким способом - в скрипте сцены переменная game_status хранит текущий статус игры.

Например, на паузе game_status будет равен 0. А в обычной игре game_status будет равен 1.

Если вы будете использовать паузу в игре, то в вашем скрипте сцены должна быть функция:

public bool GameWorking() 
{
bool ret = true;

if (game_status != 1) // переменная хранит текущее состояние игры
ret = false;

return ret;
}

Другие объекты, вызывая в скрипте сцены публичный метод GameWorking(), будут знать о текущем состоянии игрового процесса, и, например, во время паузы останавливать работу, приглушать звук и т.д.

При включении паузы все метки переводятся в неактивное состояние с помощью вызова функции RemoveAllMark() в общем скрипте меток.

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

Если вам такая логика не нужна, то все что связано с паузой, можно убрать.

Для работы с текстом я использую библиотеку TextMeshPro.

Установка TextMeshPro

Для установки пакета заходим во вкладку WindowPackage Manager, выбираем раздел Unity Registry и находим TextMeshPro, и затем скачиваем и добавляем в проект:

Сам текстовой элемент можно добавить правой кнопкой мыши в Hierarchy:

Если вы используете другой способ работы с текстом, то вам не составит труда отредактировать нужные строки скрипта MainScriptMark под другой текстовой класс.

Для плавной смены значений переменных и анимации я использую DOTween.

Установка DOTween

Заходим во вкладку WindowAssets:

Или по прямой ссылке.


Теперь добавим на сцену ваш объект, для которого будет использоваться метка. Для этого создадим пустой объект:

А затем в него перенесем ваш меш объекта. И на этом же уровне вложенности добавим префаб метки:

Саму метку можно (и нужно) сместить в удобную точку относительно объекта.

Для примера, создадим обычный куб, и перетащим центр метки чуть выше самого куба:

В завершение, можно создать префаб объекта.

Чтобы сделать префаб нового объекта

Переносим родительский объект в любую папку проекта, например Prefabs:

Все готово! Наконец-то

Чтобы проверить работоспособность на пустой сцене – в инспекторе двигаем камеру ближе к объекту. Метка сначала появляется, а вблизи меняет цвет. Отлично!

Дальнейшую настройку функционала рассмотрим во второй главе.

Осталось настроить параметры метки на ваш вкус:

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

Главное запомнить этот номер, дальше он будет использоваться в скрипте.

Mark status – если одна метка нужна для разных действий (например, открыть, а потом закрыть и т.д.), здесь задаем номер начального действия при запуске приложения. В скрипте в зависимости от этого значения нужно будет написать начальные положения объектов.

Attach obj - ссылка на объект, над которым будут производиться действия. Перемещаем сюда конкретный вложенный объект (например, если нужно поворачивать дверь при открытии, то указываем объект с нужным transform. Если нужно выполнить отдельный скрипт, указываем ссылку на объект, на котором расположен этот скрипт). Затем из скрипта метки можно будет легко обратиться к этому объекту.

Cof scale – коэффициент масштаба метки. Значение напрямую влияет на видимый размер на экране.

Distanse active – расстояние от камеры до метки, при котором метка станет интерактивной.

Distanse show – расстояние от камеры до метки, при котором метка будет видимой.

Enable – дополнительный параметр, который позволяет не использовать эту метку (не удаляя сам компонент).

Angle active – в скрипте используется расчет угла обзора. Если угол направления от камеры к метке в пределах этого значения, то метка перейдет из видимого состояния в активное.

Color Show – цвет включенной метки.

Остальные цвета можно настроить в скрипте, или вывести в виде переменной.

В следующей главе рассмотрим код проекта.

Глава 2. Изучаем скрипты

Самые важные элементы при работе с метками, это переменные mark_type и mark_status.

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

mark_status - Это своего рода состояние, в котором находится объект метки. Например, 0 – выключен, 1 – загружается, 2 – включен. 3 – сломан, и т.д. Это значение можно задать в инспекторе, а затем в коде выполнять нужные действия для объекта, на основе этой переменной. Например, есть выдвижной ящик, с mark_status = 0 он будет закрытым. Нажав по метке, ящик откроется, и состояние поменяется на mark_status = 1. Тогда следующее действие уже будет закрывать его.

Здесь я не буду писать построчно весь код, а сразу начнем разбирать, как и что работает.

Интересно услышать ваше мнение, насколько понятно написан материал в этой главе).

1. Функция LateUpdate из скрипта Mark.cs

Это базовая функция для проверки текущего состояния метки (state = 0, выключена, 1 = интерактивная, 2 = просто видимая).

Код функции LateUpdate
private void LateUpdate() 
{
    if (enable) 
    {
        distanse = Vector3.Distance(main_cam.position, tr.position); 

        if (distanse < distance_show) 
        {
            if (!show) 
            {                   
                render.enabled = true;
                show = true;             
            }

            if (state == 1 || state == 2) 
            angle_to_object = Mathf.Abs(Vector3.SignedAngle(tr.position - main_cam.position, main_cam.forward, Vector3.right));  
            
            if (state != 1 && distanse <= distance_active && angle_to_object <= angle_active && (script_scene == null || (script_scene != null && script_scene.GameWorking()) ) ) 
                ChangeState(1);
            else if ((state != 2 && distanse > distance_active) || (state == 1 && angle_to_object > angle_active) || (state == 1 && priority_stop)) 
                ChangeState(2);               
        }
        else if (state != 0 && distanse > distance_show + 1) 
            ChangeState(0);

        if (show) 
        {
            tr.rotation = main_cam.rotation;
            sprite_size = cof_scale * distanse;  
            Vector3 scale_cof = new Vector3(tr.localScale.x / tr.lossyScale.x, tr.localScale.y / tr.lossyScale.y, tr.localScale.z / tr.lossyScale.z); 
            tr.localScale = Vector3.Slerp(tr.localScale, scale_cof * sprite_size, Time.deltaTime * 10);
        }
    }
}

Функция каждый кадр отслеживает расстояние до метки. Масштабирует иконку и разворачивает ее в сторону игрока.

Сначала определяем текущее расстояние от камеры до метки:

distanse = Vector3.Distance(main_cam.position, tr.position);

Если это расстояние меньше дистанции видимости:
if (distanse < distance_show)

то включаем графическое отображение метки (если до этого была выключена):

if (!show)                
{                   
   render.enabled = true;
   show = true;
}

Проверяем состояние метки в прошлом кадре, и если она уже была включена (1 – активна, 2 – просто видимая), находим угол между меткой и направлением камеры для дальнейшего использования:

if (state == 1 || state == 2) 
angle_to_object = Mathf.Abs(Vector3.SignedAngle(tr.position - main_cam.position, main_cam.forward, Vector3.right)); 

Метод SignedAngle позволяет находить угол в виде сферического угла обзора (не только по горизонтали, но и по вертикали).

Дальше проверяем условие перехода в активное состояние (state = 1):

if (state != 1 && distanse <= distance_active && angle_to_object <= angle_active && script_scene.GameWorking()) 
ChangeState(1); 

Если метка сейчас в другом состоянии, и расстояние до камеры входит в заданное расстояние активности (distance_active), и угол между направлением камеры и меткой в пределах заданного (angle_active), и скрипт сцены разрешает сейчас активировать метку (например, игра не на паузе), то запускаем функцию ChangeState с аргументом 1. Функцию ChangeState рассмотрим позже.

Если условие для перехода активное состояние метки не выполнено, проверяем условие перехода в состояние обычной видимости (state = 2):

else if ((state != 2 && distanse > distance_active) || (state == 1 && angle_to_object > angle_active) || (state == 1 && priority_stop)) 
ChangeState(2);

Если метка еще не в этом состоянии, и расстояние до камеры больше расстояние активности (distance_active), или метка сейчас в состоянии активности, но текущий угол обзора стал больше заданного пользователем (angle_active), или метка сейчас в состоянии активности, но это состояние принудительно нужно выключить общим скриптом меток ( например, в приоритете другая метка), то запускаем функцию ChangeState с аргументом 2.

Если вышестоящее условие if (distanse < distance_show) не было выполнено, то проверяем условие выключения метки:

else if (state != 0 && distanse > distance_show + 1) 
ChangeState(0); 

Если метка еще не в выключенном состоянии и дистанция до камеры больше расстояния видимости плюс 1 метр (Здесь добавляем + 1 метр к расчету, чтобы не было постоянного включения / выключения при легком смещении игрока), то запускаем функцию ChangeState с аргументом 0.

Далее, если метка видна, смещаем метку параллельно камере и меняем ее масштаб:

if (show) 
{
  tr.rotation = main_cam.rotation;
  sprite_size = cof_scale * distanse;  
  Vector3 scale_cof = new Vector3(tr.localScale.x / tr.lossyScale.x, tr.localScale.y / tr.lossyScale.y, tr.localScale.z / tr.lossyScale.z); 
  tr.localScale = Vector3.Slerp(tr.localScale, scale_cof * sprite_size, Time.deltaTime * 10);
}

2. Функция ChangeState из скрипта Mark.cs

Код функции ChangeState
private void ChangeState(int num) 
{
    bool mark_access = true; 
    if (num == 1) 
        mark_access = script.NewActiveMark(this.gameObject, angle_to_object, mark_type, mark_status); 

    if (mark_access) 
    {
        priority_stop = false; 
        if (state == 1 && num != 1) 
            script.RemoveActiveMark(this.gameObject); 
        
        state = num;          
        ChangeColor(state);
    }   
}

После проверки изменения метки, эта функция устанавливает новое состояние метки.

Задаем переменную для проверки смены состояния, по умолчанию разрешено:

bool mark_access = true;

Если нужно поменять состояние на активное (функция запущена с аргументом 1), то, дополнительно проверяем в общем скрипте:

if (num == 1) 
mark_access = script.NewActiveMark(this.gameObject, angle_to_object, mark_type, mark_status); 

Функция NewActiveMark будет рассмотрена позже. Она возвращает успешность активации метки. В нее передаем текущий объект метки, угол камеры к метке, тип и состояние метки.

Дальше выполняется блок кода:

if (mark_access) 
{
    priority_stop = false; 
    if (state == 1 && num != 1) 
       script.RemoveActiveMark(this.gameObject); 
    
    state = num;     
    ChangeColor();
}

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

Если новое состояние метки нужно сделать неактивным, но сейчас оно еще активное, то вызываем в общем скрипте функцию RemoveActiveMark для отключения активности этой метки, и в качества аргумента передаем текущий объект метки.

После всех изменений устанавливаем новое значение метки и меняем ее цвет.

3. Функция NewActiveMark из скрипта MainScriptMark.cs

Код функции NewActiveMark
public bool NewActiveMark(GameObject obj, float angle, int mark_type, int mark_status) 
{
    bool ret = false;
    bool game_pause = false;

    if (scene_script != null && !scene_script.GameWorking())
        game_pause = true;

    if (!game_pause) 
    {
        if (active_mark != obj) 
        {
            if (active_mark != null) 
            {
                if (angle < active_mark.GetComponent<Mark>().GetAngle()) 
                {
                    active_mark.GetComponent<Mark>().RemoveActiveState(); 
                    ret = true;
                }
            }
            else ret = true;
        }

        if (ret) 
        {
            active_mark = obj; 
            if (notice_txt != null) 
            {
                string type_str = action_type_txt[mark_type - 1][mark_status];
                notice_txt.text = type_str + " - <b><color=#ffffff>E</color></b>";
                notice_txt.enabled = true;
                EffectScaleTxt(); 
            }
        }
    }

    return ret;
}

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

Если новая метка еще не является активной, то включаем ее. А если в этот момент была активна другая метка, то сравниваем углы расположения меток относительно направления камеры. Новая метка станет активной только в случае если она расположена под меньшим углом к камере (и в этом случае делаем запрос в скрипт старой метки для принудительного оповещения об ее отключении и штрафом) :

if (active_mark != obj) 
{
    if (active_mark != null) 
    {
        if (angle < active_mark.GetComponent<Mark>().GetAngle()) 
        {
            active_mark.GetComponent<Mark>().RemoveActiveState(); 
            ret = true;
        }
    }
    else ret = true;
}

Если новая метка стала активной, записываем ссылку на ее объект в переменную (для будущего отслеживания) и обновляем текстовое поле с использованием анимации:

if (ret)
{
    active_mark = obj; 
    if (notice_txt != null) 
    {
        string type_str = action_type_txt[mark_type - 1][mark_status];
        notice_txt.text = type_str + " - <b><color=#ffffff>E</color></b>";
        notice_txt.enabled = true;
        EffectScaleTxt(); 
    }              
}

4. Функция SetNoticeTxt из скрипта MainScriptMark.cs

Код функции SetNoticeTxt
private void SetNoticeTxt() 
{   
    AddNoticeTxt(new string[] { "Уменьшить", "Увеличить" }); // для 1 mark_type
    AddNoticeTxt(new string[] { "Поднять вверх 1 раз" }); // 2
    AddNoticeTxt(new string[] { "Удалить" }); // 3
    AddNoticeTxt(new string[] { "Сдвинуть вверх","Сдвинуть вниз" }); // 4

    // для пианино       
    string[] piano_str = new string[] { "Ля #", "Си", "До", "До #", "Ре", "Ре #", "Ми", "Фа", "Фа #", "Соль", "Соль #", "Ля", "Ля #", "Си", "До", "До #" };
    for (int i = 0; i < piano_str.Length; i++)
        piano_str[i] += " <size=25>сыграть</size> ";
    AddNoticeTxt(piano_str); // 5

    // Для кубиков
    AddNoticeTxt(new string[] { "середина", "верх", "низ" }); // 6
}

В этой функции заполняем данные текстовых подсказок с помощью функции AddNoticeTxt([текстовой массив]).


Каждый вызов функции добавляет подсказки к типу метки mark_type, начиная со значения 1. А каждая текстовая фраза в коде уже будет соотноситься с mark_status, начиная со значения 0. Например, после выполнения действия, можно переключать mark_status, а вместе с ней будет меняться и текстовая подсказка для следующего действия.

Для однократного действия AddNoticeTxt(new string[] { "Открыть дверь" }); // будет использоваться для mark_type = 1

Для метки с двумя действиями AddNoticeTxt(new string[] {"Открыть дверь","Закрыть дверь"});// для mark_type = 2
Подсказка Открыть дверь будет показываться при mark_status = 0, а подсказка Закрыть дверь будет показываться при mark_status = 1.

Подсказки для пианино:

string[] piano_str = new string[] { "Ля #", "Си", "До", "До #", "Ре", "Ре #", "Ми", "Фа", "Фа #", "Соль", "Соль #", "Ля", "Ля #", "Си", "До", "До #" };
for (int i = 0; i < piano_str.Length; i++)
    piano_str[i] += " сыграть ";
AddNoticeTxt(piano_str);

Здесь создаем массив с названиями нот и передаем его в качестве аргумента функции AddNoticeTxt.

5. Функция MarkReady из скрипта MainScriptMark.cs

Код функции MarkReady
public void MarkReady (GameObject mark, int mark_type, int mark_status) 
{
    if (mark_type == 4) 
    {
        if (mark_status == 0)
            mark.GetComponent<Mark>().SetMarkActiveColor(1, 0);
        else mark.GetComponent<Mark>().SetMarkActiveColor(1, 1);
    }

    if (mark_type == 6) 
    {
        Transform cubic_tr = mark.GetComponent<Mark>().attach_obj.transform;
        if (mark_status == 0)
            cubic_tr.localScale = new Vector3(1, 1.5f, 1);
        else if (mark_status == 1)
            cubic_tr.localScale = new Vector3(1, 2f, 1);
        else if (mark_status == 2)
            cubic_tr.localScale = new Vector3(1, 2.5f, 1);
    }
}

Функция MarkReady запускается каждой меткой при появлении на сцене.

В данной функции можно прописать начальные параметры для объектов (например, в зависимости от начального состояния mark_status метки, передвигаем объект в определенную позицию, или закрытую дверь делаем сразу открытой). А далее изменения уже происходят при активации меток (функция UseMark).

Рассмотрим метку с mark_type = 6, которая переключается между 3 состояниями (mark_status), изменяя высоту объекта.
Сохраняем в переменную cubic_tr ссылку на transform (при создании метки в публичное поле attach_obj можно указать объект для быстрого взаимодействия) управляемого объекта. А затем в зависимости от начального mark_status меняем масштаб объекта по высоте:

if (mark_type == 6) 
{
    Transform cubic_tr = mark.GetComponent<Mark>().attach_obj.transform;
    
    if (mark_status == 0)
        cubic_tr.localScale = new Vector3(1, 1.5f, 1);
    else if (mark_status == 1)
        cubic_tr.localScale = new Vector3(1, 2f, 1);
    else if (mark_status == 2)
        cubic_tr.localScale = new Vector3(1, 2.5f, 1);
}

6. Функция UseMark из скрипта MainScriptMark.cs

Код функции UseMark
private void UseMark() 
{
    if (notice_txt != null)
        notice_txt.enabled = false; 

    Mark mark = active_mark.GetComponent<Mark>(); 
   
    int cur_mark_type = mark.GetMarkType(); 
    int cur_mark_status = mark.GetMarkStatus(); 
    GameObject cur_obj = mark.GetMarkObj();                

    if (cur_mark_type == 6) // кубики, меняем их высоту 
    {           
        cur_mark_status += 1;
        if (cur_mark_status > 2)
            cur_mark_status = 0;

        float new_scaleY = 1.5f; // для mark_status = 0
        if (cur_mark_status == 1)
            new_scaleY = 2f; // для 1
        if (cur_mark_status == 2)
            new_scaleY = 2.5f; // для 2 

        cur_obj.transform.DOScale(new Vector3(1, new_scaleY, 1), 0.5f);
        
        mark.SetMarkStatus(cur_mark_status); 
        RefreshTxtNotice(action_type_txt[cur_mark_type - 1][cur_mark_status]);
    }
}

Функция запускается непосредственно после нажатия клавиши по активной метке.

В ней перебираем все значения mark_type и прописываем свое действие на каждый тип метки.

Вспомнить, где задавали mark_type

После активации действия сразу скрываем текстовую подсказку. Затем кешируем активную метку в переменную mark, и получаем текущий тип метки, ее статус и управляемый ею объект:

if (notice_txt != null)
    notice_txt.enabled = false; 

Mark mark = active_mark.GetComponent<Mark>(); 

int cur_mark_type = mark.GetMarkType(); 
int cur_mark_status = mark.GetMarkStatus(); 
GameObject cur_obj = mark.GetMarkObj();

Рассмотрим код действия для mark_type = 6 (Для этого объекта мы задавали начальные значения в функции MarkReady):

if (cur_mark_type == 6) 
{           
    cur_mark_status += 1;
    if (cur_mark_status > 2)
        cur_mark_status = 0;

    float new_scaleY = 1.5f; // масштаб для mark_status = 0
    if (cur_mark_status == 1)
        new_scaleY = 2f; // для 1
    if (cur_mark_status == 2)
        new_scaleY = 2.5f; // для 2 

    cur_obj.transform.DOScale(new Vector3(1, new_scaleY, 1), 0.5f); //плавная анимация
    
    mark.SetMarkStatus(cur_mark_status); 
    RefreshTxtNotice(action_type_txt[cur_mark_type - 1][cur_mark_status]);
}

Переключаем состояние метки cur_mark_status на следующее. Затем меняем масштаб объекта (который привязан к этой метке) в зависимости от нового состояния.

Новое состояние метки обязательно передаем в локальный скрипт метки mark.SetMarkStatus(cur_mark_status);

Т.к. данная метка используется для нескольких действий, нужно обновить текстовое поле для показа новой подсказки, вызывая функцию RefreshTxtNotice.


Заключение: мы успешно разобрали функционал работы с интерактивными метками. Теперь вы сможете наполнить свой проект большим разнообразием интерактивного взаимодействия с предметами.

В качестве упражнения попробуйте разобрать, как в проекте работает пианино:).

Усложненную версию данного функционала меток (реализация иконок в виде системы частиц) можно посмотреть на видео:

UPD 21.05.23 – Доработан функционал, улучшена читаемость кода.

UPD 28.05.23 – Дописан код масштабирования иконок. Теперь родительские элементы можно масштабировать отдельно, без нарушения пропорций показываемой иконки.

Ссылка на исходник проекта.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Насколько хорошо и понятно изложен материал?
0% Статья понравилась!0
0% Буду использовать в своем проекте0
33.33% Полезный урок1
0% Тяжело разобрать скрипты0
66.67% Буду изучать2
0% Есть непонятные моменты (напишу в комментариях)0
Проголосовали 3 пользователя. Воздержались 2 пользователя.
Теги:
Хабы:
Всего голосов 3: ↑3 и ↓0+3
Комментарии8

Публикации

Истории

Работа

Ближайшие события