Как сделать полоску здоровья для 3d обьекта в Unity?
Нашел видео как сделать billboard, но как сделать полоску? Типо полоска в обводке со значением от 0 до 1.

Но чтобы эта штука была как billboard над обьектом.
Как это сделать?
- Вопрос задан более двух лет назад
- 241 просмотр
Комментировать
Решения вопроса 0
Ответы на вопрос 1

~KraGen~ @KraGenDeveloper
Unity Developer
в общем размещай Slider над игроком и делаешь скрипт который накидаешь его на игроку скрипт и в скрипте мутишь такие строчки
using Unity.Engine.UI;//это в самый верх public Slider HPslider;//сама полоска хп int HP = 100;//кол-во хп public void Update()
Unity: отрисовываем множество полосок здоровья за один drawcall
Недавно мне нужно было решить задачу, достаточно распространённую во многих играх с видом сверху: рендерить на экране целую кучу полосок здоровья врагов. Примерно вот так:

Очевидно, что я хотел сделать это как можно эффективнее, желательно за один вызов отрисовки. Как обычно, прежде чем приступать к работе, я провёл небольшое онлайн-исследование решений других людей, и результаты были очень разными.
Я не буду никого стыдить за код, но достаточно сказать, что некоторые из решений были не совсем блестящими, например, кто-то добавлял к каждому врагу объект Canvas (что очень неэффективно).
Метод, к которому я в результате пришёл, немного отличается от всего того, что я видел у других, и не использует вообще никаких классов UI (в том числе и Canvas), поэтому я решил задокументировать его для общества. А для тех, кто хочет изучить исходный код, я выложил его на Github.
Почему бы не использовать Canvas?
По одному Canvas для каждого врага — это очевидно плохое решение, но я мог использовать общий Canvas для всех врагов; единственный Canvas тоже бы привёл к батчингу вызовов отрисовки.
Однако мне не нравится связанный с таким подходом объём выполняемой в каждом кадре работы. Если вы используете Canvas, то в каждом кадре придётся выполнять следующие операции:
- Определять, какие из врагов находятся на экране, и выделять каждому из них из пула полоску UI.
- Проецировать позицию врага в камеру, чтобы расположить полоску.
- Изменять размер «заливки» части полоски, вероятно, как Image.
- Скорее всего изменять размер полосок в соответствии с типом врагов; например, у крупных врагов должны быть большие полоски, чтобы это не выглядело глупо.
Вкратце о моём решении
Краткое описание процесса моей работы:
- Прикрепляем объекты полосок энергии к врагам в 3D.
- Это позволяет автоматически располагать и усекать полоски.
- Позицию/размер полоски можно настраивать в соответствии с типом врага.
- Полоски мы направим на камеру в коде с помощью transform, который всё равно есть.
- Шейдер гарантирует, что они всегда будут рендериться поверх всего.
Что такое Instancing?
В работе с графикой уже давно используется стандартная техника: несколько объектов объединяются вместе, чтобы они имели общие данные вершин и материалы и их можно было отрендерить за один вызов отрисовки. Нам нужно именно это, потому что каждый вызов отрисовки — это дополнительная нагрузка на ЦП и GPU. Вместо выполнения по одному вызову отрисовки на каждый объект мы рендерим их все одновременно и используем шейдер для добавления вариативности в каждую копию.
Можно делать это вручную, дублируя данные вершин меша X раз в одном буфере, где X — максимальное количество копий, которое может быть отрендерено, а затем использовав массив параметров шейдера для преобразования/окраски/варьирования каждой копии. Каждая копия должна хранить знание о том, каким нумерованным экземпляром она является, чтобы использовать это значение как индекс массива. Затем мы можем использовать индексированный вызов рендера, который приказывает «рендерить только до N», где N — это число экземпляров, которое на самом деле нужно в текущем кадре, меньшее, чем максимальное количество X.
В большинстве современных API код для этого уже есть, поэтому вручную это делать не требуется. Эта операция называется «Instancing»; по сути, она автоматизирует описанный выше процесс с заранее заданными ограничениями.
Движок Unity тоже поддерживает instancing, в нём есть собственный API и набор макросов шейдеров, помогающие в его реализации. Он использует определённые допущения, например, о том, что в каждом экземпляре требуется полное 3D-преобразование. Строго говоря, для 2D-полосок оно нужно не полностью — мы можем обойтись упрощениями, но поскольку они есть, мы будем использовать их. Это упростит наш шейдер, а также обеспечит возможность использования 3D-индикаторов, например, кругов или дуг.
Класс Damageable
У наших врагов будет компонент под названием Damageable , дающий им здоровье и позволяющий им получать урон от коллизий. В нашем примере он довольно прост:
public class Damageable : MonoBehaviour < public int MaxHealth; public float DamageForceThreshold = 1f; public float DamageForceScale = 5f; public int CurrentHealth < get; private set; >private void Start() < CurrentHealth = MaxHealth; >private void OnCollisionEnter(Collision other) < // Collision would usually be on another component, putting it all here for simplicity float force = other.relativeVelocity.magnitude; if (force >DamageForceThreshold) < CurrentHealth -= (int)((force - DamageForceThreshold) * DamageForceScale); CurrentHealth = Mathf.Max(0, CurrentHealth); >> >Объект HealthBar: позиция/поворот
Объект полосы здоровья очень прост: по сути, это всего лишь прикреплённый к врагу Quad.

Мы используем scale этого объекта, чтобы сделать полоску длинной и тонкой, и разместим её прямо над врагом. Не беспокойтесь о её повороте, мы исправим его с помощью кода, прикреплённого к объекту в HealthBar.cs :
private void AlignCamera() < if (mainCamera != null) < var camXform = mainCamera.transform; var forward = transform.position - camXform.position; forward.Normalize(); var up = Vector3.Cross(forward, camXform.right); transform.rotation = Quaternion.LookRotation(forward, up); >>Этот код всегда направляет quad в сторону камеры. Мы можем выполнять изменение размера и поворота в шейдере, но я реализую их здесь по двум причинам.
Во-первых, instancing в Unity всегда использует полный transform каждого объекта, и поскольку мы всё равно передаём все данные, можно их и использовать. Во-вторых, задание масштаба/поворота здесь гарантирует, что ограничивающий параллелограмм для усечения полоски всегда будет верным. Если бы мы сделали задание размера и поворота обязанностью шейдера, то Unity мог бы усекать полоски, которые должны быть видимы, когда они находятся близко к краям экрана, потому что размер и поворот их ограничивающего параллелограмма не будут соответствовать тому, что мы собираемся рендерить. Разумеетсяя, мы могли бы реализовать свой собственный способ усечения, но обычно при возможности лучше использовать то, что у нас есть (код Unity является нативным и имеет доступ к большему количеству пространственных данных, чем мы).
Я объясню, как рендерится полоска, после того, как мы рассмотрим шейдер.
Шейдер HealthBar
В этой версии мы создадим простую классическую красно-зелёную полоску.
Я используют текстуру размером 2×1, с одним зелёным пикселем слева и одним красным справа. Естественно, я отключил mipmapping, фильтрацию и сжатие, а для параметра addressing mode задал значение Clamp — это значит, что пиксели нашей полоски всегда будут идеально зелёными или красными, а не растекутся по краям. Это позволит нам изменять в шейдере координаты текстуры для сдвига линии, разделяющей красный и зелёный пиксели вниз и вверх по полоске.
(Так как здесь всего два цвета, я мог просто использовать в шейдере функцию step для возврата в точке одного или другого. Однако в этом методе удобно то, что при желании можно использовать более сложную текстуру, и это будет работать аналогично, пока переход находится в середине текстуры.)
Для начала мы объявим необходимые нам свойства:
Shader "UI/HealthBar" < Properties < _MainTex ("Texture", 2D) = "white" <>_Fill ("Fill", float) = 0 >_MainTex — это красно-зелёная текстура, а _Fill — значение от 0 до 1, где 1 — это полное здоровье.
Далее нам нужно приказать полоске рендериться в очереди overlay, а значит, игнорировать всю глубину в сцене и рендериться поверх всего:
SubShader < Tags < "Queue"="Overlay" >Pass < ZTest OffСледующая часть — это сам код шейдера. Мы пишем шейдер без освещения (unlit), поэтому нам не нужно беспокоиться об интеграции с различными моделями поверхностных шейдеров Unity, это простая пара вершинного/фрагментного шейдеров. Для начала напишем бутстреппинг:
CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_instancing #include "UnityCG.cginc"По большей мере это стандартный bootstrap, за исключением #pragma multi_compile_instancing , которая сообщает компилятору Unity, что нужно компилировать для Instancing.
Вершинная структура должна включать в себя данные экземпляров, поэтому мы сделаем следующее:
struct appdata < float4 vertex : POSITION; float2 uv : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID >;Также нам нужно задать, что именно будет находиться в данных экземляров, кроме того, что за нас обрабатывает Unity (transform):
UNITY_INSTANCING_BUFFER_START(Props) UNITY_DEFINE_INSTANCED_PROP(float, _Fill) UNITY_INSTANCING_BUFFER_END(Props)Так мы сообщаем, что Unity должен создать буфер под названием «Props» для хранения данных каждого экземпляра, и внутри него мы будем использовать по одному float на экземпляр для свойства под названием _Fill .
Можно использовать и несколько буферов; это стоит делать, если у вас есть несколько свойств, обновляемых с разной частотой; разделив их, можно, например, не менять один буфер при изменении другого, что более эффективно. Но нам этого не требуется.
Наш вершинный шейдер почти полностью выполняет стандартную работу, потому что размер, позиция и поворот и так передаются в transform. Это реализуется с помощью UnityObjectToClipPos , который автоматически использует transform каждого экземпляра. Можно представить, что без instancing это обычно было бы простым использованием одного матричного свойства. но при использовании instancing внутри движка это выглядит как массив матриц, и Unity самостоятельно выбирает матрицу, подходящую к данному экземпляру.
Также нем нужно изменять UV, чтобы менять расположение точки перехода из красного в зелёный в соответствии со свойством _Fill . Вот соответствующий фрагмент кода:
UNITY_SETUP_INSTANCE_ID(v); float fill = UNITY_ACCESS_INSTANCED_PROP(Props, _Fill); // generate UVs from fill level (assumed texture is clamped) o.uv = v.uv; o.uv.x += 0.5 - fill;UNITY_SETUP_INSTANCE_ID и UNITY_ACCESS_INSTANCED_PROP выполняют всю магию, осуществляя доступ к нужной версии свойства _Fill из буфера констант для этого экземпляра.
Мы знаем, что в обычном состоянии UV-координаты четырёхугольника (quad) покрывают весь интервал текстуры, и что разделительная линия полоски находится посередине текстуры по горизонтали. Поэтому небольшие математические расчёты по горизонтали сдвигают полоску влево или вправо, а значение Clamp текстуры обеспечивают заполнение оставшейся части.
Фрагментный шейдер не мог быть проще, потому что вся работа уже выполнена:
return tex2D(_MainTex, i.uv);Полный код шейдера с комментариями доступен в репозитории GitHub.
Материал Healthbar
Дальше всё просто — нам всего лишь нужно назначить нашей полоске материал, который использует этот шейдер. Больше почти ничего делать не нужно, достаточно только выбрать нужный шейдер в верхней части, назначить красно-зелёную текстуру, и, что самое важное, поставить флажок «Enable GPU Instancing».

Обновление свойства HealthBar Fill
Итак, у нас есть объект полоски здоровья, шейдер и материал, который нужно рендерить, теперь нужно задать для каждого экземпляра свойство _Fill . Мы делаем это внутри HealthBar.cs следующим образом:
private void UpdateParams() < meshRenderer.GetPropertyBlock(matBlock); matBlock.SetFloat("_Fill", damageable.CurrentHealth / (float)damageable.MaxHealth); meshRenderer.SetPropertyBlock(matBlock); >Мы превращаем CurrentHealth класса Damageable в значение от 0 до 1, разделив его на MaxHealth . Затем мы передаём его свойству _Fill с помощью MaterialPropertyBlock .
Если вы ещё не использовали MaterialPropertyBlock для передачи данных в шейдеры, даже без instancing, то вам нужно изучить его. Он не так хорошо объяснён в документации Unity, но является самым эффективным способом передачи данных каждого объекта в шейдеры.
В нашем случае, когда используется instancing, значения для всех полосок здоровья упаковываются в буфер констант, чтобы их можно было передать их все вместе и отрисовать за один раз.
Здесь нет почти ничего, кроме бойлерплейта для задания переменных, и код довольно скучный; подробности см. в репозитории GitHub.
Демо
В репозитории GitHub есть тестовое демо, в котором куча злых синих кубиков уничтожается героическими красными сферами (ура!), получая урон, отображаемый описанными в статье полосками. Демо написано в Unity 2018.3.6f1.
Эффект от использования instancing можно наблюдать двумя способами:
Панель Stats
Нажав Play, щёлкните на кнопку Stats над панелью Game. Здесь вы можете увидеть, сколько вызовов отрисовки экономится благодаря instancing (батчингу):

Запустив игру, вы можете нажать на материал HealthBar и снять флажок с «Enable GPU Instancing», после чего число сэкономленных вызовов снизится до нуля.
Отладчик кадров
Запустив игру, перейдите в меню Window > Analysis > Frame Debugger, а затем нажмите в появившемся окне «Enable».
Слева внизу вы увидите все выполняемые операции рендеринга. Заметьте, что пока там есть множество отдельных вызовов для врагов и снарядов (при желании можно реализовать instancing и для них). Если прокрутить до самого низа, то вы увидите пункт «Draw Mesh (instanced) Healthbar».
Этот единственный вызов рендерит все полоски. Если нажать на эту операцию, а затем на операцию над ней, то вы увидите, что все полоски исчезнут, потому что они отрисовываются за один вызов. Если находясь во Frame Debugger, вы снимете у материала флажок «Enable GPU Instancing», то увидите, что одна строка превратилась в несколько, а после установки флажка — снова в одну.
Как можно расширить эту систему
Как я говорил раньше, поскольку эти полоски здоровья являются реальными объектами, ничто не мешает превратить простые 2D-полоски во что-то более сложное. Они могут быть полукругами под врагами, которые уменьшаются по дуге, или вращающимися ромбами над их головами. Используя тот же подход, можно по-прежнему рендерить их все за один вызов.
- Работа с 3D-графикой
- Разработка игр
- Unity
Как создать игру, ничего не умея. Часть четвёртая: интерфейс
Как добавить нашим персонажам шкалу здоровья и опыта, а так же необходимые для их работы скрипты? Вы можете узнать из этого гайда.

Кстати предыдущие статьи тут!
Шкала здоровья и опыта
Недолго думая пора нам добавить в Наш проект: Жизнь(HP), Опыт(XP), а главное придумать их систему получения и потери этих параметров. Что же полезли в интернет в поисках необходимой информации!
Начнём со шкалы здоровья, надеюсь Вы не удалили Ваш проект за это время и он, как и у меня в том же состоянии. Открываем Level_02 в Unity и добавляем в Hierarchy Canvas. Правой кнопкой в Hierarchy UI->Canvas.

Далее жмём правой кнопкой на Наш Canvas и выбираем Slider. UI->Slider. Так как мы делаем шкалу здоровья нам идеально подходит слайдер.Что бы нам было проще настраивать нашу шкалу, переведите Scene в 2d режим, и дважды нажмите на строку Slider в Hierarchy. Перед Вами появится Ваш слайдер.

Начнём же превращать эту невзрачную полоску в шкалу здоровья! Для начала разворачиваем наш слайдер и заходим в Background и в inspector напротив Source image нажимаем на кружок и выбираем none. В строке color я выбрал прозрачный, но вы можете сделать любой как Вам будет угодно.

Многие писали в комментариях, что не могут найти те или иные элементы, поэтому специально для Вас я отметил их красным цветом:

- Переходим в Fill Area->Fill проделываем туже процедуру с выставлением none в source image, а в строке color выбираем красный цвет(если конечно Вас персонаж имеет красный цвет крови или же Вам необходим именно красный индикатор).
- Далее в Handle Slide Area -> Handle и в inspector отключаем Image, это уберет кружок что нам в данный момент только мешает.
- Основа есть, теперь растянем наш Background, для этого снова выбираем наш Background переходим в inspector нажимаем на custom stretch и с зажатым ALT выбираем растянуть по всей области.
То же самое проделываем с Fill Area. Корректируем Fill дабы он не вылезал за границы нашего слайдера, проверить это можно выбрав Slider и в inspector в самом низу будет строка value передвигаю которую Вы сможете увидеть как будет заполняться слайдер, и если где-то он вылазит за края то перейдите в строку Fill и просто передвиньте границы.
Теперь мы можем переименовать наш Slider в HP и добавить текстовое описание строки. Правой кнопкой нажимаем на наш Canvas->UI-Text, после прикрепляем текст к нашему HP перетащив его в иерархию HP, и в inspector в строке text пишем HP, выбираем Font Size — > Bolt что бы его было лучше видно, и позиционируем его как Вам будет удобнее.Позиционируется он очень просто выбираем его в иерархии и просто передвигаем на нужно место.

Осталось только от позиционировать нашу шкалу здоровья и готово! Повторяем трюк который мы проделывали ранее и в inspector с зажатым alt выбираем левый верхний угол. Немного корректировок с расположением и вот у нас уже есть полоска здоровья!

И поскольку Мы очень ленивые люди, просто делаем дубликат данного слайдера, переименовываем его в EXP и меняем цвет Fill, получилась строка опыта! Для тех кто потеряет свой дубликат, он находится поверх полоски HP, его просто нужно выбрать в иерархии и переместить в сцене ниже или выше полоски ХП.

С полосками разобрались, теперь добавим отображение уровня. Для этого снова выбираем наш canvas-UI-Image. Мы уже знаем как всё настраивается, и поэтому не буду снова всё делать пошагово, центрирует получившийся квадрат где нам удобно, настраиваем цвет, добавляем к нему текст, и получаем отображение уровней.

Вот так вот просто у нас получилось подобие интерфейса где отображается наш ХП, ЕХП, и уровень.
Теперь займёмся отображением ХП наших врагов, ведь никто не любит когда непонятно сколько ХП у врага и идёт ли по нему хоть какой-то урон. Снова дублируем нашу полоску с ХП, и переименуем её в EnemyHP, попутно не забыв убрать текст говорящий что это полоска ХП, просто уберите галочку в inspector рядом со словом text.
Пишем скрипт
Давайте теперь подключим все эти полоски к нашему игроку, для этого создаём C# Script и назовем его PStats, используем данный код, но не забывайте что сайт все галочки превращает в стрелочки =)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class PStats: MonoBehaviour
//Здоровье
public float maxLife = 100;
//Текущее здоровье
public float curLife = 100;
//Уровень
public int level = 1;
//Опыт до следующего уровня
public float nextLevelExp = 100;
//Текущий опыт
public float curExp = 0;public Text Level_Text;
public Slider EXP_Slider;
public Slider HP_Slider;
// Start is called before the first frame update
void Start()
>// Update is called once per frame
void Update()
HP_Slider.value = (curLife / maxLife);
EXP_Slider.value = (curExp / nextLevelExp);
Level_Text.text = level.ToString();
>
>Добавляем этот скрипт к персонажу, и перетаскиваем значения слайдеров и текст уровня в нужные поля, должно получится вот так.

Запускаем игру и проверяем как работают наши полоски меняя значения ХП и ЕХП, если всё сделано правильно то полоски будут изменятся, как и значения.

Перейдём к настройке ХП врагов. Для начала нам нужно прикрепить полоску ХП к нашему противнику, для этого создадим очередной скрипт и назовём его EnemyHP.
Скрипт ХП противника.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class EnemyHP: MonoBehaviour
public float maxHP = 100;public float curHP = 100;
public GameObject Enemy;
public Vector3 offset;
// Start is called before the first frame update
void Start()
>// Update is called once per frame
void Update()
GetComponent().position = Camera.main.WorldToScreenPoint(Enemy.transform.position+offset);
GetComponent().value = curHP / maxHP;Перетаскиваем наш скрипт в слайдер EnemyHP, а после в строку Enemy в inspector перетаскиваем нашего противника.

Теперь если запустить игру, то полоска будет прикреплена к противнику, но чтобы более точно её настроить, мы добавили в скрипт offset и меняя его значения можно точно настроить расположение строки ХП. Так же если строка получилась слишком большой то её можно просто уменьшить. Немного манипулирую значением по Y и выставив всего 2, полоска ХП появилась точно над головой, так же изменил её масштаб и всё стало именно так как мне и нужно.

Заключение
В этой короткой статье мы добавили зачатки интерфейса, в следующей уже поработаем над боевой системой и значениями ХП и ЕХП, надеюсь что кому были интересны эти статьи не будут сильно ругаться за задержки =).
PS:
Вся информация была взята из интернета и открытых источников.
И после такого уже становится интересно имея столько готовой информации в интернете, почему каждый не стал разработчиком игр?Как сделать полоску здоровья(Hp Bar) в Unity

Как сделать Hp Bar в Unity? Да очень просто). Приятного просмотра! --- Если понравился ролик то ставь лайк #gamedev #unity3d #blobjoystudio --- Ссылки на мои соц. сети: ➡️ Telegram: https://t.me/blob_joy ➡️ Discord: https://discord.gg/5QKHHYJjKd ☢️ ➡️ Паблик в ВК: https://vk.com/blob_joy_studio ➡️ Instagram: https://www.instagram.com/blob.joy.st . ️ ➡️ TikTok: https://vm.tiktok.com/ZMd3XVRDg/ ➡️ Мои игры в Google Play: https://play.google.com/store/apps/dev?id=8140921423912299684 --- Таймкоды: 00:00 - Вступление 00:13 - Пишем скрипт 00:35 - Настраиваем Unity 02:03 - Hp Bar работает 02:30 - Подпишись бро!
Показать больше
Войдите , чтобы оставлять комментарии