Search Unity

В Unity 5.3 появилась возможностью настраивать модули системы частиц с помощью скриптов. Понимаем, что кого-то новые возможности скриптинга могут смутить. Чем же может быть полезен такой способ?

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

Доступ к модулям

Пример

Ниже приводится пример изменения атрибута Rate (частота) модуля Emission (испускания частиц).


Те, кто уже знаком с .NET, могут заметить, что мы берем конструкт, задаем ему значение, но не назначаем его обратно системе частиц. Как вообще система частиц примет эти изменения, что это за магия такая?

Всего лишь интерфейс

Модули системы частиц в Unity полностью принадлежат той части движка, которая написана на C++. Вызов системы частиц или одного из ее модулей на самом деле адресован стороне, написанной C++.
В сущности, модули системы частиц представляют собой ее атрибуты. Они зависят от системы частиц, и мы никогда не меняем их принадлежность и не делаем их общими с другими системами. Поэтому даже при наличии возможности взять модуль и использовать его в скрипте он все равно будет всегда принадлежать одной и той же системе.

Чтобы было понятнее, давайте подробнее рассмотрим предыдущий пример.
Система получает запрос на создание модуля испускания частиц, движок создает новый конструкт EmissionModule и задает ему единственный параметр — система частиц, которой он принадлежит. Это необходимо, потому что для доступа к свойствам модулей необходима система частиц.


Когда мы задаем частоту, для доступа к модулю и непосредственной настройки используется переменная m_ParticleSystem. Следовательно, нам более не потребуется возвращать принадлежность модуля системе частиц, поскольку он всегда остается ее частью и все изменения применяются мгновенно. Поэтому все, что делает модуль — это вызов системы, которой он принадлежит. Сам модуль представляет собой лишь интерфейс для внутренних компонентов системы частиц.
Во внутренних модулях хранятся соответствующие свойства, в них же содержится информация о состоянии, поэтому они не могут принадлежать или быть отнесены к нескольким разным системам частиц.
Если вы хотите применить свойства одной системы к другой, то вам лучше скопировать только значения, а не весь класс, поскольку это сокращает обмен данными между частью на C++ и частью на C#.

MinMaxCurve

Класс MinMaxCurve часто используется в свойствах модулей, чтобы описывать изменения значений во времени. Класс поддерживает 4 режима; давайте рассмотрим, как использовать эти режимы в скриптах.

Константы

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

1

Доступ к константе происходит следующим образом:


Случайное значение между двумя константами

Этот режим дает случайное значение на промежутке между двумя постоянными значениями. В самой системе две константы хранятся как опорные точки для кривых min и max соответственно. Значение получается путем линейной интерполяции между двумя значениями с использованием нормализованного случайного параметра для нашей величины линейной интерполяции. Это требует примерно столько же работы, сколько требует режим выбора случайного значения между двумя кривыми.

1

Доступ к двум константам модуля реализуется так:

Кривая

В этом режиме значение атрибута будет меняться по кривой. Использование кривых из MinMaxCurve в скрипте таит несколько подводных камней.
Во-первых, если вы попытаетесь считать атрибут curve, использующий один из режимов с кривыми, то получите следующее сообщение: «Режимы, использующие переменное значение, не поддерживают чтение кривых частицы из скрипта».
В силу особенностей сжатия кривых в движке доступ к классу MinMaxCurve невозможен, если он не использует один из двух константных режимов. Это не очень-то приятно, но мы работаем над решением этой трудности. Причина заключается в том, что система не хранит AnimationCurve, а выбирает один из двух путей. Если кривая простая (не больше 3 опорных точек, две из которых находятся на концах), то мы используем оптимизированную многочленную кривую, которая обеспечивает большую производительность. Если кривая сложнее, то мы используем стандартную неоптимизированную кривую. В окне Inspector неоптимизированная кривая будет показана с маленькой иконкой в правом нижнем углу, за которой скрыто предложение оптимизировать эту кривую.

Оптимизированная кривая

1

Неоптимизированная кривая

1

Несмотря на невозможность доступа к кривой из модуля в скрипте, мы можем решить проблему, сохранив собственную кривую и применяя ее к модулю при необходимости, примерно так:

Случайное значение между 2 кривыми.

В этом режиме случайные значения выбираются из промежутка между кривой наименьших и кривой наибольших значений с использованием времени в качестве координаты x для выбора значения. Закрашенная область соответствует возможным значениям. Этот режим аналогичен режиму кривой в том, что здесь также невозможно получить доступ к кривым из скрипта, а также в том, что здесь тоже используются многочленные кривые (по возможности). Для этого обе кривые должны поддаваться оптимизации, то есть иметь не больше 3 ключевых точек и по одной точке с каждого конца. Как и в режиме кривой, понять, оптимизирована ли кривая, здесь можно, взглянув в правый нижний угол окна редактора.

1

Этот пример очень похож на пример с кривой, хотя здесь мы еще задаем кривую наименьших значений.

Производительность

Мы провели простое сравнение производительности этих режимов. Выборки были сделаны до недавней SIMD-оптимизации, которая могла дать существенный прирост производительности. В специально созданной тестовой сцене мы получили следующие результаты:
1

Упрощаем задачу

Считывание кривых из класса MinMaxCurve

Мы знаем, что доступ к кривым системы частиц должен быть в любом случае, независимо от используемого режима. В настоящий момент ведется активная работа по снятию этого ограничения, чтобы вы могли легко читать или менять кривые из скриптов. Кроме того, пока что у нас нет возможности запросить кривую для проверки того, используется ли кривая в данном режиме, не вызывая ошибку. Это мы тоже планируем исправить.

Перевод модулей из конструктов в классы

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

Пример:

И в заключение

Надеемся, что статья была вам полезна. Примеры и обсуждение статьи можно найти на форуме по этой ссылке. Мы также добавим эту информацию в нашу документацию.

Комментарии закрыты.

  1. Have you guys thought about extracting some useful classes to be used in other non-particle related cases?

    I could imagine a lot of places, e.g. in the AI, where I would need «a random value between two curves». And benefiting from optimized 3-key curves never sounds wrong… not all animation curves are imported from 3DMax with 50+ keyframes ;)

    1. Hi.
      We have added an Evaluate function in 5.4 to the MinMaxCurve – http://docs.unity3d.com/540/Documentation/ScriptReference/ParticleSystem.MinMaxCurve.Evaluate.html
      You can now use it although it uses a c# side evaluation so no polynomial curves, it will just be like calling Evaluate on a standard AnimationCurve when you use the curve modes.
      For it to be useful as a generic component it would need to have inspector support and possibly add the poly curves in. That’s not currently on the near term roadmap for us but certainly something we will consider in the future.

  2. any plan to support particle positioning in between Unity UI?

    1. There have been discussions on this. We have proposed changes for the UI system in the future to allow for this but nothing concrete at the moment.

  3. Not sure if this is the place to ask, but in the event that MinMaxCurve becomes modifiable from script, will they be usable in custom inspectors for non-particle related things?

    1. Hi David.
      We have added an Evaluate function in 5.4 to the MinMaxCurve — http://docs.unity3d.com/540/Documentation/ScriptReference/ParticleSystem.MinMaxCurve.Evaluate.html
      You can now use it although it uses a c# side evaluation so no polynomial curves, it will just be like calling Evaluate on a standard AnimationCurve when you use the curve modes.
      The main problem is that it does not currently appear in the inspector or serialize itself so we would need to sort that out for it to be useful. I’ll add the suggestion to our list of features so we can discuss it in the future.
      Thanks!

  4. the blog is very interesting and will be much useful for us. thank you for sharing the blog with us. please keep on updating.

  5. this content is really useful and you have done a tremendous work ,it is really awesome and it is good too.

  6. Any plans about the shader side ?

    Would it be possible to add a special case where a custom geometry shader could be used to display particles ? So the mesh generated by shuriken could only contains a vertex for each particle (+ all interesting data to be used in the shader)

    And in the current state, do you think passing particles data (age, lifetime, size, etc…) to the shader to be something doable ?

    1. We have a vertex stream module in the works that may do some of those features but i’m not too sure of the exact details at the moment.

    2. I can provide a bit more information on that.

      In 5.5, we are hoping to introduce a Vertex Streams feature to the Renderer Module. You will be able to choose exactly what gets sent to your shader (positions, colors, velocities, rotations, lifetime information, etc). This should allow you to do a wide variety of custom shader-based effects.

      Additionally, you will be able to set custom particle data from script, and pass that to your shaders too. So, to give an example, you could tag particles with a custom property to represent the «health» of each particle, and then use that in your script logic, or in your shader, or both.

      You’ll need to be comfortable writing your own shaders in Unity, to be able to make the most of this feature, but in its basic form, it will finally allow you to properly use the Standard Shader to create normal mapped particles, by including the Tangent Stream.

      1. After Unity 5.2 . we can’t see stable version on Android !?

  7. Hi Karl,
    These new changes are welcome, but would it be possible to _please_ have someone fill out the documentation with stub example usages for the Shuriken-related classes/structs/etc. on the Script Reference?

    1. Funny you should mention it I have been doing that today :)

  8. We need Standard Unity Video Player (Android,IOS,Windows,….) ;

  9. Those API features are indeed well overdue, thanks.
    But we need better UI and UX with Shuriken, and maybe a buit-in previewer.

    1. What do you have in mind by better UI/UX? You can preview the system in the editor, how do you see a built in previewer working?

      1. I think being able to preview a particle system without placing it in the scene, how you can preview a prefab or texture by clicking it in your Assets list. That would be pretty cool.

        1. Interesting. I have added it to out feature requests list so we can consider it in the future when we next review it.

  10. Thanks for the post. The struct mechanism was a little confusing to me at first as well. Will the classes in a future version simply be references to a private member variable then? I assume so from your post, but just wanted to make sure it wasn’t allocating a wrapper every time the property was accessed.

    1. Yes it will only be allocated once when the system is created and then just accessed. So no wrapping each time it’s accessed.

  11. Since the post is about particle,
    is there any plan to support custom module addition in shuriken?

    1. Its been discussed but nothing concrete at the moment. If you have an idea about it or how it should work you can post it on feedback https://feedback.unity3d.com/
      There is not much regarding custom modules so its certainly worth doing it.

  12. Why have I been seeing GetComponent() in a lot of Unity articles lately? Is this a new version of the method I have not seen, which has no parameters or generic parameters? Thanks!

    1. Never mind. I realized it was because C# can infer the generic parameter type. Sacrifices a little clarity for brevity in this case, I suppose. =)

    2. It’s actually plain old generic GetComponent , but since angle brackets have a special meaning in HTML this gets rendered incorrectly. And apparently no one ever proofreads these posts, so there.

    3. Looks like the brackets were removed when i made some changes. Its fixed now.

  13. Hi ! I like this article, this clarifies a lot of things with Particle System Scripting.

    I downloaded the new Unity 5.4 beta and it was said that there are new ways to deal with Particle System events in the code, but I can’t found the documentation related to this.
    Can you tell me where ?
    Thank you !

    1. Hi Marc, do you mean the new Trigger Module?

      The documentation for it is here: http://docs.unity3d.com/540/Documentation/Manual/PartSysTriggersModule.html

      1. Yes that’s it !
        Thank you ! :D

  14. I wonder why «Random between two Constants» isn’t faster than «Random between two Curves». I also wonder why «Random between two Constants» is so much more expensive than «Constant». Finding a random point between two constants doesn’t sound like an expensive feature.

    1. Its because we store the 2 constant values as keys in the min max curve. This means the values need to be normalised as well. Doing a random between 2 constants is almost the same process as a random between 2 curves. We have discussed this approach a little internally and may try and optimize it in the future.

      1. That begs the question: Why do you store 2 constant values as «key in the min max curve» then? Clearly, you need more floats than two to store any kind of curve, so there should be space enough ruling out any answer like «we want to conserve memory».

        Afraid of too many code branches? Then why don’t you sort your different curve types to avoid code branches between evaluations?