如何直接控制粒子,同时防止粒子系统做同样的事情?
How to control particles directly while preventing the particle system from doing the same?
我有很多影响游戏玩法的相同简单对象,数以千计!嗯,不是数以千计,但确实很多。因此,如果我将它们设为游戏对象,FPS 会降低,尤其是在生成它们时。即使有池。我应该尝试不同的方法。
您知道Unity3D 中的粒子系统可以非常快速地渲染许多粒子。它还会自动控制粒子、发射和移除粒子。但在我的例子中,对象的位置和生命周期由游戏逻辑管理,粒子系统不允许在没有我的命令的情况下做任何事情,甚至重新排序粒子。
我正在尝试使用SetParticles
方法来控制粒子。它在测试项目中工作,我首先使用 GetParticles
。我什至可以删除将生命周期设置为 -1 的粒子,但不能产生新粒子。也不会阻止粒子系统控制粒子。
我可以禁用发射,这样就不会自动创建粒子。
我可以将粒子速度设置为 0,这样它们就不会移动。
我可以将生命周期设置为巨大的数字,这样它们就不会被删除。
我有一个 Particle
实例池,以避免不必要的 GC 分配。对象在生成时接收对粒子的引用,在更新时更改它,并在删除时设置生命周期 -1 和 return 到池。池存储这个:
private ParticleSystem.Particle [] _unusedParticles;
private int _unusedCount;
private ParticleSystem.Particle [] _array;
_unused
池化需要数组和计数器,_array
存储所有使用和未使用的粒子,并在 SetParticles
调用中使用。
此方法的主要缺点是它不起作用,可能是因为 SetParticles
不会创建新粒子。另外我猜它对粒子绘制顺序没有任何作用,这就是为什么它不适合子弹模式应该看起来不错的子弹地狱游戏。
我应该怎么做才能正确禁用粒子的自动控制并正确设置直接控制,生成和删除?
创建一个 empty GameObject
,然后将 ParticleSystem
添加为子项。 将 playOnAwake
设为真。
现在,当您需要它时,将 GameObject.SetActive
设置为 true
,否则 false
。
要获得它们中的每一个,请使用 ParticleSystem.GetParticles
、修改它们并 ParticleSystem.GetParticles
。
希望这就是您正在寻找的what。
ParticleSystem m_System;
ParticleSystem.Particle[] m_Particles;
public float m_Drift = 0.01f;
private void LateUpdate()
{
InitializeIfNeeded();
// GetParticles is allocation free because we reuse the m_Particles buffer between updates
int numParticlesAlive = m_System.GetParticles(m_Particles);
// Change only the particles that are alive
for (int i = 0; i < numParticlesAlive; i++)
{
m_Particles[i].velocity += Vector3.up * m_Drift;
}
// Apply the particle changes to the Particle System
m_System.SetParticles(m_Particles, numParticlesAlive);
}
void InitializeIfNeeded()
{
if (m_System == null)
m_System = GetComponent<ParticleSystem>();
if (m_Particles == null || m_Particles.Length < m_System.main.maxParticles)
m_Particles = new ParticleSystem.Particle[m_System.main.maxParticles];
}
您要找的可能是
List<Matrix4x4> matrixes=new List<Matrix4x4>();
for (...)
{
matrixes.Add(Matrix4x4.TRS( position,rotation,scale));
}
Graphics.DrawMeshInstanced(mesh,0,material, matrixes);
在每一帧上,您只需更新位置、旋转和缩放,并在一次绘制调用中获取在 GPU 上渲染的所有实例(与单独的游戏对象相比,速度相当快)。您可以使用这种方式在一次调用中渲染多达 1000 个实例
在编辑器中创建粒子系统后,我们应该禁用发射和形状,因此只有核心部分和渲染器保持活动状态。
最重要的部分是模拟速度必须为零。粒子系统将不再自动发射、移除或处理粒子。现在只有您的代码可以管理它们。
我用这个class来控制粒子。它没有将粒子绑定到对象,而是使用 API 来注册对象,在本例中为 smoke。此外,它为粒子存储一个临时数组以避免 GC 分配和粒子计数,以避免使用粒子系统的 particleCount
属性。
在我的游戏逻辑调用的 Update
中,发生以下情况:
- 所有被游戏逻辑取消生成的对象都从列表中删除。
- 如果对象数大于数组长度,则调整数组大小。
- 如果对象数大于活动粒子数,则调用
_particleSystem.Emit
。如果我们在没有它的情况下调用 SetParticles
,则不会出现新的粒子。我们必须先发出它们。
GetParticles
被调用。简单但可能不是最有效的解决方案,但它保留了粒子数据。它可以通过在阵列创建和调整大小时设置 all 粒子数据来优化。如果这样做,请删除 GetParticles
调用并取消注释上面的行。此外,您的游戏逻辑应该更仔细地管理粒子。
- 对于每个对象,我们让该对象改变一个粒子。
- 每个没有物体的粒子都应该被移除,因此它们的生命周期被设置为负数。
SetParticles
更新系统中的粒子。
public class SmokeSystem {
private ParticleSystem _particleSystem;
private List <Smoke> _smoke = new List <Smoke> ();
private ParticleSystem.Particle [] _particles = new ParticleSystem.Particle[256];
private int _particleCount;
public SmokeSystem (ParticleSystem particleSystem) {
_particleSystem = particleSystem;
}
public void AddSmoke (Smoke smoke) => _smoke.Add (smoke);
public void Update () {
_smoke.RemoveAll (e => e.Despawned);
if (_smoke.Count > _particles.Length) {
int newSize = Max (_smoke.Count, 2 * _particles.Length);
Array.Resize (ref _particles, newSize);
}
int count = _smoke.Count;
if (count > _particleCount) {
_particleSystem.Emit (count - _particleCount);
// _particleCount = count;
}
_particleCount = _particleSystem.GetParticles (_particles);
for (int i = 0; i < count; i++) {
_smoke [i].UpdateParticle (ref _particles [i]);
}
for (int i = count; i < _particleCount; i++) {
_particles [i].remainingLifetime = -1;
}
_particleSystem.SetParticles (_particles, _particleCount);
_particleCount = count;
}
}
它不依赖于 GPU 实例化支持,因此它可以在 WebGL 上运行。
我有很多影响游戏玩法的相同简单对象,数以千计!嗯,不是数以千计,但确实很多。因此,如果我将它们设为游戏对象,FPS 会降低,尤其是在生成它们时。即使有池。我应该尝试不同的方法。
您知道Unity3D 中的粒子系统可以非常快速地渲染许多粒子。它还会自动控制粒子、发射和移除粒子。但在我的例子中,对象的位置和生命周期由游戏逻辑管理,粒子系统不允许在没有我的命令的情况下做任何事情,甚至重新排序粒子。
我正在尝试使用SetParticles
方法来控制粒子。它在测试项目中工作,我首先使用 GetParticles
。我什至可以删除将生命周期设置为 -1 的粒子,但不能产生新粒子。也不会阻止粒子系统控制粒子。
我可以禁用发射,这样就不会自动创建粒子。
我可以将粒子速度设置为 0,这样它们就不会移动。
我可以将生命周期设置为巨大的数字,这样它们就不会被删除。
我有一个 Particle
实例池,以避免不必要的 GC 分配。对象在生成时接收对粒子的引用,在更新时更改它,并在删除时设置生命周期 -1 和 return 到池。池存储这个:
private ParticleSystem.Particle [] _unusedParticles;
private int _unusedCount;
private ParticleSystem.Particle [] _array;
_unused
池化需要数组和计数器,_array
存储所有使用和未使用的粒子,并在 SetParticles
调用中使用。
此方法的主要缺点是它不起作用,可能是因为 SetParticles
不会创建新粒子。另外我猜它对粒子绘制顺序没有任何作用,这就是为什么它不适合子弹模式应该看起来不错的子弹地狱游戏。
我应该怎么做才能正确禁用粒子的自动控制并正确设置直接控制,生成和删除?
创建一个 empty GameObject
,然后将 ParticleSystem
添加为子项。 将 playOnAwake
设为真。
现在,当您需要它时,将 GameObject.SetActive
设置为 true
,否则 false
。
要获得它们中的每一个,请使用 ParticleSystem.GetParticles
、修改它们并 ParticleSystem.GetParticles
。
希望这就是您正在寻找的what。
ParticleSystem m_System;
ParticleSystem.Particle[] m_Particles;
public float m_Drift = 0.01f;
private void LateUpdate()
{
InitializeIfNeeded();
// GetParticles is allocation free because we reuse the m_Particles buffer between updates
int numParticlesAlive = m_System.GetParticles(m_Particles);
// Change only the particles that are alive
for (int i = 0; i < numParticlesAlive; i++)
{
m_Particles[i].velocity += Vector3.up * m_Drift;
}
// Apply the particle changes to the Particle System
m_System.SetParticles(m_Particles, numParticlesAlive);
}
void InitializeIfNeeded()
{
if (m_System == null)
m_System = GetComponent<ParticleSystem>();
if (m_Particles == null || m_Particles.Length < m_System.main.maxParticles)
m_Particles = new ParticleSystem.Particle[m_System.main.maxParticles];
}
您要找的可能是
List<Matrix4x4> matrixes=new List<Matrix4x4>();
for (...)
{
matrixes.Add(Matrix4x4.TRS( position,rotation,scale));
}
Graphics.DrawMeshInstanced(mesh,0,material, matrixes);
在每一帧上,您只需更新位置、旋转和缩放,并在一次绘制调用中获取在 GPU 上渲染的所有实例(与单独的游戏对象相比,速度相当快)。您可以使用这种方式在一次调用中渲染多达 1000 个实例
在编辑器中创建粒子系统后,我们应该禁用发射和形状,因此只有核心部分和渲染器保持活动状态。
最重要的部分是模拟速度必须为零。粒子系统将不再自动发射、移除或处理粒子。现在只有您的代码可以管理它们。
我用这个class来控制粒子。它没有将粒子绑定到对象,而是使用 API 来注册对象,在本例中为 smoke。此外,它为粒子存储一个临时数组以避免 GC 分配和粒子计数,以避免使用粒子系统的 particleCount
属性。
在我的游戏逻辑调用的 Update
中,发生以下情况:
- 所有被游戏逻辑取消生成的对象都从列表中删除。
- 如果对象数大于数组长度,则调整数组大小。
- 如果对象数大于活动粒子数,则调用
_particleSystem.Emit
。如果我们在没有它的情况下调用SetParticles
,则不会出现新的粒子。我们必须先发出它们。 GetParticles
被调用。简单但可能不是最有效的解决方案,但它保留了粒子数据。它可以通过在阵列创建和调整大小时设置 all 粒子数据来优化。如果这样做,请删除GetParticles
调用并取消注释上面的行。此外,您的游戏逻辑应该更仔细地管理粒子。- 对于每个对象,我们让该对象改变一个粒子。
- 每个没有物体的粒子都应该被移除,因此它们的生命周期被设置为负数。
SetParticles
更新系统中的粒子。
public class SmokeSystem {
private ParticleSystem _particleSystem;
private List <Smoke> _smoke = new List <Smoke> ();
private ParticleSystem.Particle [] _particles = new ParticleSystem.Particle[256];
private int _particleCount;
public SmokeSystem (ParticleSystem particleSystem) {
_particleSystem = particleSystem;
}
public void AddSmoke (Smoke smoke) => _smoke.Add (smoke);
public void Update () {
_smoke.RemoveAll (e => e.Despawned);
if (_smoke.Count > _particles.Length) {
int newSize = Max (_smoke.Count, 2 * _particles.Length);
Array.Resize (ref _particles, newSize);
}
int count = _smoke.Count;
if (count > _particleCount) {
_particleSystem.Emit (count - _particleCount);
// _particleCount = count;
}
_particleCount = _particleSystem.GetParticles (_particles);
for (int i = 0; i < count; i++) {
_smoke [i].UpdateParticle (ref _particles [i]);
}
for (int i = count; i < _particleCount; i++) {
_particles [i].remainingLifetime = -1;
}
_particleSystem.SetParticles (_particles, _particleCount);
_particleCount = count;
}
}
它不依赖于 GPU 实例化支持,因此它可以在 WebGL 上运行。