diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml index d573b9fe6e48..1d914497af77 100644 --- a/doc/classes/CPUParticles2D.xml +++ b/doc/classes/CPUParticles2D.xml @@ -97,7 +97,12 @@ - Number of particles emitted in one emission cycle. + The number of particles to emit in one emission cycle. The effective emission rate is [code](amount * amount_ratio) / lifetime[/code] particles per second. Higher values will increase processing load, even if not all particles are visible at a given time or if [member amount_ratio] is decreased. + [b]Note:[/b] Changing this value will cause the particle system to restart. To avoid this, change [member amount_ratio] instead. + + + The ratio of particles that should actually be emitted. If set to a value lower than [code]1.0[/code], this will set the amount of emitted particles throughout the lifetime to [code]amount * amount_ratio[/code]. Unlike changing [member amount], changing [member amount_ratio] while emitting does not affect already-emitted particles and doesn't cause the particle system to restart. [member amount_ratio] can be used to create effects that make the number of emitted particles vary over time. + [b]Note:[/b] Reducing the [member amount_ratio] has no performance benefit, since resources need to be allocated and processed for the total [member amount] of particles regardless of the [member amount_ratio]. If you don't intend to change the number of particles emitted while the particles are emitting, make sure [member amount_ratio] is set to [code]1[/code] and change [member amount] to your liking instead. Each particle's rotation will be animated along this [Curve]. Should be a unit [Curve]. diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml index 170f684638b8..2e23e1b073f9 100644 --- a/doc/classes/CPUParticles3D.xml +++ b/doc/classes/CPUParticles3D.xml @@ -103,7 +103,12 @@ - Number of particles emitted in one emission cycle. + The number of particles to emit in one emission cycle. The effective emission rate is [code](amount * amount_ratio) / lifetime[/code] particles per second. Higher values will increase processing load, even if not all particles are visible at a given time or if [member amount_ratio] is decreased. + [b]Note:[/b] Changing this value will cause the particle system to restart. To avoid this, change [member amount_ratio] instead. + + + The ratio of particles that should actually be emitted. If set to a value lower than [code]1.0[/code], this will set the amount of emitted particles throughout the lifetime to [code]amount * amount_ratio[/code]. Unlike changing [member amount], changing [member amount_ratio] while emitting does not affect already-emitted particles and doesn't cause the particle system to restart. [member amount_ratio] can be used to create effects that make the number of emitted particles vary over time. + [b]Note:[/b] Reducing the [member amount_ratio] has no performance benefit, since resources need to be allocated and processed for the total [member amount] of particles regardless of the [member amount_ratio]. If you don't intend to change the number of particles emitted while the particles are emitting, make sure [member amount_ratio] is set to [code]1[/code] and change [member amount] to your liking instead. Each particle's rotation will be animated along this [Curve]. Should be a unit [Curve]. diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 9aae3a11316a..d4528e6b21dd 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -80,6 +80,11 @@ void CPUParticles2D::set_amount(int p_amount) { RS::get_singleton()->multimesh_allocate_data(multimesh, p_amount, RS::MULTIMESH_TRANSFORM_2D, true, true); particle_order.resize(p_amount); + set_amount_ratio(amount_ratio); +} + +void CPUParticles2D::set_amount_ratio(float p_amount_ratio) { + amount_ratio = p_amount_ratio; } void CPUParticles2D::set_lifetime(double p_lifetime) { @@ -138,6 +143,10 @@ int CPUParticles2D::get_amount() const { return particles.size(); } +float CPUParticles2D::get_amount_ratio() const { + return amount_ratio; +} + double CPUParticles2D::get_lifetime() const { return lifetime; } @@ -771,12 +780,22 @@ void CPUParticles2D::_particles_process(double p_delta) { velocity_xform[2] = Vector2(); } + float amount_ratio_accumulator = 0.0; double system_phase = time / lifetime; bool should_be_active = false; for (int i = 0; i < pcount; i++) { Particle &p = parray[i]; + amount_ratio_accumulator += amount_ratio; + bool active_by_ratio = false; + if (amount_ratio_accumulator >= 1.0) { + active_by_ratio = true; + amount_ratio_accumulator -= 1.0; + } else if (!p.active) { + continue; + } + if (!emitting && !p.active) { continue; } @@ -835,6 +854,11 @@ void CPUParticles2D::_particles_process(double p_delta) { float tv = 0.0; if (restart) { + if (!active_by_ratio) { + p.active = false; + continue; + } + if (!emitting) { p.active = false; continue; @@ -1415,6 +1439,7 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) { void CPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &CPUParticles2D::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &CPUParticles2D::set_amount); + ClassDB::bind_method(D_METHOD("set_amount_ratio", "amount_ratio"), &CPUParticles2D::set_amount_ratio); ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &CPUParticles2D::set_lifetime); ClassDB::bind_method(D_METHOD("set_one_shot", "enable"), &CPUParticles2D::set_one_shot); ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &CPUParticles2D::set_pre_process_time); @@ -1429,6 +1454,7 @@ void CPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_emitting"), &CPUParticles2D::is_emitting); ClassDB::bind_method(D_METHOD("get_amount"), &CPUParticles2D::get_amount); + ClassDB::bind_method(D_METHOD("get_amount_ratio"), &CPUParticles2D::get_amount_ratio); ClassDB::bind_method(D_METHOD("get_lifetime"), &CPUParticles2D::get_lifetime); ClassDB::bind_method(D_METHOD("get_one_shot"), &CPUParticles2D::get_one_shot); ClassDB::bind_method(D_METHOD("get_pre_process_time"), &CPUParticles2D::get_pre_process_time); @@ -1456,6 +1482,7 @@ void CPUParticles2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting", PROPERTY_HINT_ONESHOT), "set_emitting", "is_emitting"); ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); // FIXME: Evaluate support for `exp` in integer properties, or remove this. + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "amount_ratio", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_amount_ratio", "get_amount_ratio"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); ADD_GROUP("Time", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime"); @@ -1647,6 +1674,7 @@ CPUParticles2D::CPUParticles2D() { set_emitting(true); set_amount(8); + set_amount_ratio(1); set_use_local_coordinates(false); set_seed(Math::rand()); diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index dc034d1a25b6..e280275aa2e4 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -132,6 +132,7 @@ class CPUParticles2D : public Node2D { bool one_shot = false; + float amount_ratio = 1; double lifetime = 1.0; double pre_process_time = 0.0; double _requested_process_time = 0.0; @@ -228,6 +229,7 @@ class CPUParticles2D : public Node2D { public: void set_emitting(bool p_emitting); void set_amount(int p_amount); + void set_amount_ratio(float p_amount_ratio); void set_lifetime(double p_lifetime); void set_one_shot(bool p_one_shot); void set_pre_process_time(double p_time); @@ -239,6 +241,7 @@ class CPUParticles2D : public Node2D { bool is_emitting() const; int get_amount() const; + float get_amount_ratio() const; double get_lifetime() const; bool get_one_shot() const; double get_pre_process_time() const; diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 107bb7de85c5..847fa9200d20 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -85,6 +85,11 @@ void CPUParticles3D::set_amount(int p_amount) { RS::get_singleton()->multimesh_allocate_data(multimesh, p_amount, RS::MULTIMESH_TRANSFORM_3D, true, true); particle_order.resize(p_amount); + set_amount_ratio(amount_ratio); +} + +void CPUParticles3D::set_amount_ratio(float p_amount_ratio) { + amount_ratio = p_amount_ratio; } void CPUParticles3D::set_lifetime(double p_lifetime) { @@ -134,6 +139,10 @@ int CPUParticles3D::get_amount() const { return particles.size(); } +float CPUParticles3D::get_amount_ratio() const { + return amount_ratio; +} + double CPUParticles3D::get_lifetime() const { return lifetime; } @@ -742,12 +751,22 @@ void CPUParticles3D::_particles_process(double p_delta) { velocity_xform = emission_xform.basis; } + float amount_ratio_accumulator = 0.0; double system_phase = time / lifetime; bool should_be_active = false; for (int i = 0; i < pcount; i++) { Particle &p = parray[i]; + amount_ratio_accumulator += amount_ratio; + bool active_by_ratio = false; + if (amount_ratio_accumulator >= 1.0) { + active_by_ratio = true; + amount_ratio_accumulator -= 1.0; + } else if (!p.active) { + continue; + } + if (!emitting && !p.active) { continue; } @@ -806,6 +825,11 @@ void CPUParticles3D::_particles_process(double p_delta) { float tv = 0.0; if (restart) { + if (!active_by_ratio) { + p.active = false; + continue; + } + if (!emitting) { p.active = false; continue; @@ -1504,6 +1528,7 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) { void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &CPUParticles3D::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &CPUParticles3D::set_amount); + ClassDB::bind_method(D_METHOD("set_amount_ratio", "amount_ratio"), &CPUParticles3D::set_amount_ratio); ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &CPUParticles3D::set_lifetime); ClassDB::bind_method(D_METHOD("set_one_shot", "enable"), &CPUParticles3D::set_one_shot); ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &CPUParticles3D::set_pre_process_time); @@ -1518,6 +1543,7 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_emitting"), &CPUParticles3D::is_emitting); ClassDB::bind_method(D_METHOD("get_amount"), &CPUParticles3D::get_amount); + ClassDB::bind_method(D_METHOD("get_amount_ratio"), &CPUParticles3D::get_amount_ratio); ClassDB::bind_method(D_METHOD("get_lifetime"), &CPUParticles3D::get_lifetime); ClassDB::bind_method(D_METHOD("get_one_shot"), &CPUParticles3D::get_one_shot); ClassDB::bind_method(D_METHOD("get_pre_process_time"), &CPUParticles3D::get_pre_process_time); @@ -1549,6 +1575,7 @@ void CPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting", PROPERTY_HINT_ONESHOT), "set_emitting", "is_emitting"); ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); // FIXME: Evaluate support for `exp` in integer properties, or remove this. + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "amount_ratio", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_amount_ratio", "get_amount_ratio"); ADD_GROUP("Time", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); @@ -1774,6 +1801,7 @@ CPUParticles3D::CPUParticles3D() { set_emitting(true); set_amount(8); + set_amount_ratio(1); set_seed(Math::rand()); rng.instantiate(); diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index 748a0e75468f..f931aaf68852 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -133,6 +133,7 @@ class CPUParticles3D : public GeometryInstance3D { bool one_shot = false; + float amount_ratio = 1; double lifetime = 1.0; double pre_process_time = 0.0; double _requested_process_time = 0.0; @@ -219,6 +220,7 @@ class CPUParticles3D : public GeometryInstance3D { void set_emitting(bool p_emitting); void set_amount(int p_amount); + void set_amount_ratio(float p_amount_ratio); void set_lifetime(double p_lifetime); void set_one_shot(bool p_one_shot); void set_pre_process_time(double p_time); @@ -231,6 +233,7 @@ class CPUParticles3D : public GeometryInstance3D { bool is_emitting() const; int get_amount() const; + float get_amount_ratio() const; double get_lifetime() const; bool get_one_shot() const; double get_pre_process_time() const;