Искать на сайте Unity

Охваченные темы
Поделиться

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

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

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

Пример

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

using UnityEngine;

public class AccessingAParticleSystemModule : MonoBehaviour
{
	// Use this for initialization
	void Start ()
	{
		// Get the emission module.
		var forceModule = GetComponent<ParticleSystem>().forceOverLifetime;

		// Enable it and set a value
		forceModule.enabled = true;
		forceModule.x = new ParticleSystem.MinMaxCurve(15.0f);
	}
}

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

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

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

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

public sealed class ParticleSystem : Component
{
	.....

	public EmissionModule emission { get { return new EmissionModule(this); } }

	....
}

public partial struct EmissionModule
{
	private ParticleSystem m_ParticleSystem; // Direct access to the particle system that owns this module.

	EmissionModule(ParticleSystem particleSystem)
	{
		m_ParticleSystem = particleSystem;
	}

	public MinMaxCurve rate
	{
		set
		{
			// In here we call down to the c++ side and perform what amounts to this:
			m_ParticleSystem->GetEmissionModule()->SetRate(value);
		}
	}
	......
}

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

MinMaxCurve

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

Константы

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

1

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

using UnityEngine;

public class MinMaxCurveConstantMode : MonoBehaviour
{
	ParticleSystem myParticleSystem;
	ParticleSystem.EmissionModule emissionModule;

	void Start()
	{
		// Get the system and the emission module.
		myParticleSystem = GetComponent<ParticleSystem>();
		emissionModule = myParticleSystem.emission;

		GetValue();
		SetValue();
	}

	void GetValue()
	{
		print("The constant value is " + emissionModule.rate.constantMax);
	}

	void SetValue()
	{
		emissionModule.rate = new ParticleSystem.MinMaxCurve(10.0f);
	}
}

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

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

1

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

using UnityEngine;

public class ParticleModuleExamples : MonoBehaviour
{
	ParticleSystem myParticleSystem;
	ParticleSystem.EmissionModule emissionModule;

	void Start()
	{
		// Get the system and the emission module.
		myParticleSystem = GetComponent<ParticleSystem>();
		emissionModule = myParticleSystem.emission;

		GetRandomConstantValues();
		SetRandomConstantValues();
	}

	void GetRandomConstantValues()
	{
		print(string.Format("The constant values are: min {0} max {1}.", emissionModule.rate.constantMin, emissionModule.rate.constantMax));
	}

	void SetRandomConstantValues()
	{
		// Assign the curve to the emission module
		emissionModule.rate =new ParticleSystem.MinMaxCurve(0.0f, 1.0f);
	}
}

Кривая

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

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

1

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

1

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

using UnityEngine;

public class MinMaxCurveCurveMode : MonoBehaviour
{
	ParticleSystem myParticleSystem;
	ParticleSystem.EmissionModule emissionModule;

	// We can "scale" the curve with this value. It gets multiplied by the curve.
	public float scalar = 1.0f;

	AnimationCurve ourCurve;

	void Start()
	{
		// Get the system and the emission module.
		myParticleSystem = GetComponent<ParticleSystem>();
		emissionModule = myParticleSystem.emission;

		// A simple linear curve.
		ourCurve = new AnimationCurve();
		ourCurve.AddKey(0.0f, 0.0f);
		ourCurve.AddKey(1.0f, 1.0f);

		// Apply the curve
		emissionModule.rate = new ParticleSystem.MinMaxCurve(scalar, ourCurve);

		// In 5 seconds we will modify the curve.
		Invoke("ModifyCurve", 5.0f);
	}

	void ModifyCurve()
	{
		// Add a key to the current curve.
		ourCurve.AddKey(0.5f, 0.0f);

		// Apply the changed curve
		emissionModule.rate = new ParticleSystem.MinMaxCurve(scalar, ourCurve);
	}
}

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

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

1

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

using UnityEngine;

public class MinMaxCurveRandom2CurvesMode : MonoBehaviour
{
	ParticleSystem myParticleSystem;
	ParticleSystem.EmissionModule emissionModule;

	AnimationCurve ourCurveMin;
	AnimationCurve ourCurveMax;

	// We can "scale" the curves with this value. It gets multiplied by the curves.
	public float scalar = 1.0f;

	void Start()
	{
		// Get the system and the emission module.
		myParticleSystem = GetComponent<ParticleSystem>();
		emissionModule = myParticleSystem.emission;

		// A horizontal straight line at value 1
		ourCurveMin = new AnimationCurve();
		ourCurveMin.AddKey(0.0f, 1.0f);
		ourCurveMin.AddKey(1.0f, 1.0f);

		// A horizontal straight line at value 0.5
		ourCurveMax = new AnimationCurve();
		ourCurveMax.AddKey(0.0f, 0.5f);
		ourCurveMax.AddKey(1.0f, 0.5f);

		// Apply the curves
		emissionModule.rate = new ParticleSystem.MinMaxCurve(scalar, ourCurveMin, ourCurveMax);

		// In 5 seconds we will modify the curve.
		Invoke("ModifyCurve", 5.0f);
	}

	void ModifyCurve()
	{
		// Create a "pinch" point.
		ourCurveMin.AddKey(0.5f, 0.7f);
		ourCurveMax.AddKey(0.5f, 0.6f);

		// Apply the changed curve
		emissionModule.rate = new ParticleSystem.MinMaxCurve(scalar, ourCurveMin, ourCurveMax);
	}
}

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

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

1
20 апреля 2016 г. через Технологии | 8 мин. читать
Охваченные темы
Партнеры
Unity, логотипы Unity и другие торговые знаки Unity являются зарегистрированными торговыми знаками компании Unity Technologies или ее партнеров в США и других странах (подробнее здесь). Остальные наименования и бренды являются торговыми знаками соответствующих владельцев.