diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml
index 314e46d9d018..445f9d06615a 100644
--- a/doc/classes/CPUParticles2D.xml
+++ b/doc/classes/CPUParticles2D.xml
@@ -89,6 +89,11 @@
Number of particles emitted in one emission cycle.
+
+ Amount Ratio multiplied by [member amount] is how many particles will be emmitted each cycle. This value is to be adjusted while emitting particles as it will not clear the existing particles.
+ When not used leave this ratio at 1 and adjust amount accordingly.
+ Setting this value to zero will clear [member emitting] flag, increasing it from zero will set emitting.
+
Each particle's rotation will be animated along this [Curve].
diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml
index 3a15a117f5f1..9a8cf62b2a24 100644
--- a/doc/classes/CPUParticles3D.xml
+++ b/doc/classes/CPUParticles3D.xml
@@ -88,6 +88,11 @@
Number of particles emitted in one emission cycle.
+
+ Amount Ratio multiplied by [member amount] is how many particles will be emmitted each cycle. This value is to be adjusted while emitting particles as it will not clear the existing particles.
+ When not used leave this ratio at 1 and adjust amount accordingly.
+ Setting this value to zero will clear [member emitting] flag, increasing it from zero will set emitting.
+
Each particle's rotation will be animated along this [Curve].
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index c6296f4732e4..8e5f1ac52f4a 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -61,6 +61,32 @@ 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) {
+ if (p_amount_ratio == 0) {
+ set_emitting(false);
+ } else if (!emitting && amount_ratio == 0) {
+ set_emitting(true);
+ }
+
+ amount_ratio = p_amount_ratio;
+
+ float tot = 1;
+ int pcount = particles.size();
+ Particle *w = particles.ptrw();
+
+ for (int i = 0; i < pcount; i++) {
+ tot += amount_ratio;
+ w[i].stopped = false;
+ if (tot >= 1) {
+ w[i].stopping = false;
+ tot -= 1;
+ } else {
+ w[i].stopping = true;
+ }
+ }
}
void CPUParticles2D::set_lifetime(double p_lifetime) {
@@ -105,6 +131,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;
}
@@ -653,6 +683,10 @@ void CPUParticles2D::_particles_process(double p_delta) {
for (int i = 0; i < pcount; i++) {
Particle &p = parray[i];
+ if (p.stopped) {
+ continue;
+ }
+
if (!emitting && !p.active) {
continue;
}
@@ -711,6 +745,12 @@ void CPUParticles2D::_particles_process(double p_delta) {
float tv = 0.0;
if (restart) {
+ if (p.stopping) {
+ p.stopped = true;
+ p.active = false;
+ continue;
+ }
+
if (!emitting) {
p.active = false;
continue;
@@ -1249,6 +1289,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);
@@ -1262,6 +1303,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);
@@ -1284,6 +1326,7 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
+ 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,suffix:s"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
@@ -1469,6 +1512,7 @@ CPUParticles2D::CPUParticles2D() {
set_emitting(true);
set_amount(8);
+ set_amount_ratio(1);
set_use_local_coordinates(false);
set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0);
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index 141e5f913942..9d8749b48d9c 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -86,6 +86,8 @@ class CPUParticles2D : public Node2D {
real_t rotation = 0.0;
Vector2 velocity;
bool active = false;
+ bool stopping = false;
+ bool stopped = false;
real_t angle_rand = 0.0;
real_t scale_rand = 0.0;
real_t hue_rot_rand = 0.0;
@@ -131,6 +133,7 @@ class CPUParticles2D : public Node2D {
bool one_shot = false;
+ float amount_ratio = 1;
double lifetime = 1.0;
double pre_process_time = 0.0;
real_t explosiveness_ratio = 0.0;
@@ -198,6 +201,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);
@@ -209,6 +213,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 5ac8535bb62f..b74a6d3a1d77 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -73,6 +73,32 @@ 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) {
+ if (p_amount_ratio == 0) {
+ set_emitting(false);
+ } else if (!emitting && amount_ratio == 0) {
+ set_emitting(true);
+ }
+
+ amount_ratio = p_amount_ratio;
+
+ float tot = 1;
+ int pcount = particles.size();
+ Particle *w = particles.ptrw();
+
+ for (int i = 0; i < pcount; i++) {
+ tot += amount_ratio;
+ w[i].stopped = false;
+ if (tot >= 1) {
+ w[i].stopping = false;
+ tot -= 1;
+ } else {
+ w[i].stopping = true;
+ }
+ }
}
void CPUParticles3D::set_lifetime(double p_lifetime) {
@@ -116,6 +142,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;
}
@@ -673,6 +703,10 @@ void CPUParticles3D::_particles_process(double p_delta) {
for (int i = 0; i < pcount; i++) {
Particle &p = parray[i];
+ if (p.stopped) {
+ continue;
+ }
+
if (!emitting && !p.active) {
continue;
}
@@ -731,6 +765,12 @@ void CPUParticles3D::_particles_process(double p_delta) {
float tv = 0.0;
if (restart) {
+ if (p.stopping) {
+ p.stopped = true;
+ p.active = false;
+ continue;
+ }
+
if (!emitting) {
p.active = false;
continue;
@@ -1410,6 +1450,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);
@@ -1423,6 +1464,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);
@@ -1445,6 +1487,7 @@ void CPUParticles3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
+ 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");
@@ -1658,6 +1701,7 @@ CPUParticles3D::CPUParticles3D() {
set_emitting(true);
set_amount(8);
+ set_amount_ratio(1);
set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0);
set_param_min(PARAM_ANGULAR_VELOCITY, 0);
diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h
index 6b7013773664..0990a5d8d350 100644
--- a/scene/3d/cpu_particles_3d.h
+++ b/scene/3d/cpu_particles_3d.h
@@ -88,6 +88,8 @@ class CPUParticles3D : public GeometryInstance3D {
real_t custom[4] = {};
Vector3 velocity;
bool active = false;
+ bool stopping = false;
+ bool stopped = false;
real_t angle_rand = 0.0;
real_t scale_rand = 0.0;
real_t hue_rot_rand = 0.0;
@@ -132,6 +134,7 @@ class CPUParticles3D : public GeometryInstance3D {
bool one_shot = false;
+ float amount_ratio = 1;
double lifetime = 1.0;
double pre_process_time = 0.0;
real_t explosiveness_ratio = 0.0;
@@ -205,6 +208,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);
@@ -216,6 +220,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;