mirror of
https://github.com/godotengine/godot.git
synced 2024-10-22 21:21:53 +00:00
Switch to Dictionary return type to avoid allocating objects
This commit is contained in:
parent
52536bd7a2
commit
052d43c4c9
|
@ -1,107 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="CPUParticle3D" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
Contains information about an individual particle from a [CPUParticles3D] system.
|
||||
</brief_description>
|
||||
<description>
|
||||
Contains information about an individual particle from a [CPUParticles3D] system.
|
||||
[CPUParticle3D] is emitted as an [Array] of objects by the [signal CPUParticles3D.particles_updated] signal.
|
||||
This can be used to make nodes (such as [Light3D]s, [AudioStreamPlayer3D]s or other [CPUParticles3D]s) follow particles individually for advanced effects.
|
||||
[b]Note:[/b] To avoid performance issues, it's recommended to only use this feature with low numbers of particles (typically a few dozen at most).
|
||||
[b]Example of placing [OmniLight3D]s to follow particles automatically and change color over time:[/b]
|
||||
[codeblock]
|
||||
extends CPUParticles3D
|
||||
|
||||
# Used to keep track of light nodes added as children more easily.
|
||||
var lights = []
|
||||
|
||||
func _ready():
|
||||
for i in amount:
|
||||
var light = OmniLight3D.new()
|
||||
lights.push_back(light)
|
||||
add_child(light)
|
||||
|
||||
particles_updated.connect(_on_cpu_particles_3d_particles_updated)
|
||||
|
||||
|
||||
func _on_cpu_particles_3d_particles_updated(particles):
|
||||
# Only update light positions if all light nodes have been added first.
|
||||
if lights.size() >= particles.size():
|
||||
for particle_idx in particles.size():
|
||||
lights[particle_idx].visible = particles[particle_idx].is_active()
|
||||
|
||||
# Change the light's color over the particle's lifetime.
|
||||
lights[particle_idx].light_color.r = particles[particle_idx].get_phase()
|
||||
lights[particle_idx].light_color.g = 1.0 - particles[particle_idx].get_phase()
|
||||
lights[particle_idx].light_color.b = 1.0 - particles[particle_idx].get_phase()
|
||||
lights[particle_idx].light_energy = 2.0 - particles[particle_idx].get_phase() * 2.0
|
||||
|
||||
# Increase the light's range over the particle's lifetime.
|
||||
lights[particle_idx].omni_range = particles[particle_idx].get_phase() * 5.0
|
||||
|
||||
# Move lights to follow particles.
|
||||
if local_coords:
|
||||
lights[particle_idx].position = particles[particle_idx].get_transform().origin
|
||||
else:
|
||||
lights[particle_idx].global_transform.origin = particles[particle_idx].get_transform().origin
|
||||
[/codeblock]
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="get_color" qualifiers="const">
|
||||
<return type="Color" />
|
||||
<description>
|
||||
Returns the particle's current color. This is the color defined by [member CPUParticles3D.color] or [member CPUParticles3D.color_initial_ramp], which is then multiplied by [member CPUParticles3D.color_ramp] over the particle's lifetime.
|
||||
[b]Note:[/b] [method get_color] does not take the material albedo color or mesh's original vertex color into account.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_phase" qualifiers="const">
|
||||
<return type="float" />
|
||||
<description>
|
||||
Returns the particle's lifetime percentage. This is close to [code]0.0[/code] when the particle is freshly spawned, and close to [code]1.0[/code] when the particle is about to expire.
|
||||
[b]Note:[/b] If the particle is inactive ([method is_active] returns [code]false[/code]), [method get_phase] returns [code]0.0[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_seed" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns the particle's random seed (a 32-bit [i]unsigned[/i] integer) within the particle system. This can be used to uniquely identify a given particle within the particle system during its lifetime.
|
||||
Since the seed is uniformly distributed, [method get_seed] can be used to selectively apply effects to certain particles only. This can be useful to improve performance by applying expensive effects to lower amounts of particles:
|
||||
[codeblock]
|
||||
# `particles` is an array of CPUParticle3D results from CPUParticles3D's `particles_updated` signal.
|
||||
for particle in particles:
|
||||
# The maximum value of a 32-bit unsigned integer is (2 ^ 32) - 1, which is roughly 4.2 billion.
|
||||
if particle.get_seed() <= 2 << 31: # Lower than roughly 2.1 billion.
|
||||
# Apply an effect to roughly 50% of particles here (the exact amount is random and varies constantly).
|
||||
pass
|
||||
[/codeblock]
|
||||
[b]Note:[/b] When a particle respawns after expiring, it will generate and use a different seed. This means [method get_seed] cannot be used to identify a given particle after it respawns.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_transform" qualifiers="const">
|
||||
<return type="Transform3D" />
|
||||
<description>
|
||||
Returns the particle's transform. If you're only interested in the particle's position, use [code]get_transform().origin[/code].
|
||||
[b]Note:[/b] If [member CPUParticles3D.local_coords] is [code]true[/code], you'll want to set nodes' [i]local[/i] transform when moving them to match particle positions. If [member CPUParticles3D.local_coords] is [code]false[/code], you'll want to set nodes' [i]global[/i] transform when moving them to match particle positions.
|
||||
[b]Note:[/b] If the particle is inactive ([method is_active] returns [code]false[/code]), [method get_transform] returns an identity [code]Transform3D()[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_velocity" qualifiers="const">
|
||||
<return type="Vector3" />
|
||||
<description>
|
||||
Returns the particle's speed in units per second on each axis.
|
||||
[b]Note:[/b] If the particle is inactive ([method is_active] returns [code]false[/code]), [method get_velocity] returns [code]Vector3(0, 0, 0)[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_active" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the particle is currently alive, [code]false[/code] otherwise.
|
||||
Typically, all particles are active except in two scenarios:
|
||||
- The particle system has just started and its [member CPUParticles3D.explosiveness] is not equal to [code]1.0[/code]. Therefore, there are less active particles than the [member CPUParticles3D.amount] configured.
|
||||
- The particle system has just had [member CPUParticles3D.emitting] set to [code]false[/code]. After all particles have expired (plus some additional time), the signal will no longer be emitted.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
</class>
|
|
@ -7,6 +7,44 @@
|
|||
CPU-based 3D particle node used to create a variety of particle systems and effects.
|
||||
See also [GPUParticles3D], which provides the same functionality with hardware acceleration, but may not run on older devices.
|
||||
Unlike [GPUParticles3D], the CPU is aware of particles' individual transforms. This means particle transforms can be used for any purpose in scripting, including gameplay-affecting elements. See [signal particles_updated] for more information.
|
||||
CPUParticles3D can emit an [Array] of dictionaries with the [signal CPUParticles3D.particles_updated] signal. This can be used to make nodes (such as [Light3D]s, [AudioStreamPlayer3D]s or other [CPUParticles3D]s) follow particles individually for advanced effects. To avoid performance issues, it's recommended to only use this feature with low numbers of particles (typically a few dozen at most).
|
||||
[b]Example of placing [OmniLight3D]s to follow particles automatically and change color over time:[/b]
|
||||
[codeblock]
|
||||
extends CPUParticles3D
|
||||
|
||||
# Used to keep track of light nodes added as children more easily.
|
||||
var lights = []
|
||||
|
||||
func _ready():
|
||||
for i in amount:
|
||||
var light = OmniLight3D.new()
|
||||
lights.push_back(light)
|
||||
add_child(light)
|
||||
|
||||
particles_updated.connect(_on_cpu_particles_3d_particles_updated)
|
||||
|
||||
|
||||
func _on_cpu_particles_3d_particles_updated(particles):
|
||||
# Only update light positions if all light nodes have been added first.
|
||||
if lights.size() >= particles.size():
|
||||
for particle_idx in particles.size():
|
||||
lights[particle_idx].visible = particles[particle_idx].active
|
||||
|
||||
# Change the light's color over the particle's lifetime.
|
||||
lights[particle_idx].light_color.r = particles[particle_idx].phase
|
||||
lights[particle_idx].light_color.g = 1.0 - particles[particle_idx].phase
|
||||
lights[particle_idx].light_color.b = 1.0 - particles[particle_idx].phase
|
||||
lights[particle_idx].light_energy = 2.0 - particles[particle_idx].phase * 2.0
|
||||
|
||||
# Increase the light's range over the particle's lifetime.
|
||||
lights[particle_idx].omni_range = particles[particle_idx].phase * 5.0
|
||||
|
||||
# Move lights to follow particles.
|
||||
if local_coords:
|
||||
lights[particle_idx].position = particles[particle_idx].transform.origin
|
||||
else:
|
||||
lights[particle_idx].global_transform.origin = particles[particle_idx].transform.origin
|
||||
[/codeblock]
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -317,16 +355,32 @@
|
|||
</description>
|
||||
</signal>
|
||||
<signal name="particles_expired">
|
||||
<argument index="0" name="particles" type="CPUParticle3D[]" />
|
||||
<param index="0" name="particles" type="Dictionary[]" />
|
||||
<description>
|
||||
Emitted when one or more particles is about to expire on the next particle update. This can be used to make nodes (such as [Light3D]s, [AudioStreamPlayer3D]s or other [CPUParticles3D]s) spawn at individual particle expiration positions.
|
||||
Each particle has an associated dictionary in the array parameter with the following keys:
|
||||
- [code]color[/code]: The particle's current color. This is the color defined by [member CPUParticles3D.color] or [member CPUParticles3D.color_initial_ramp], which is then multiplied by [member CPUParticles3D.color_ramp] over the particle's lifetime. [code]color[/code] does [i]not[/i] take the material albedo color or mesh's original vertex color into account. If the particle is inactive ([code]active[/code] is [code]false[/code]), [code]color[/code] is [code]Color(0, 0, 0, 1)[/code].
|
||||
- [code]phase[/code]: The particle's lifetime ratio. This is close to [code]0.0[/code] when the particle is freshly spawned, and close to [code]1.0[/code] when the particle is about to expire. If the particle is inactive ([code]active[/code] is [code]false[/code]), [code]phase[/code] is [code]0.0[/code].
|
||||
- [code]seed[/code]: The particle's random seed (a 32-bit [i]unsigned[/i] integer) within the particle system. This can be used to uniquely identify a given particle within the particle system during its lifetime. Since the seed is uniformly distributed, [code]seed[/code] can be used to selectively apply effects to certain particles only. This can be useful to improve performance by applying expensive effects to lower amounts of particles:
|
||||
[codeblock]
|
||||
# `particles` is an array of CPUParticle3D results from CPUParticles3D's `particles_updated` signal.
|
||||
for particle in particles:
|
||||
# The maximum value of a 32-bit unsigned integer is (2 ^ 32) - 1, which is roughly 4.2 billion.
|
||||
if particle.seed <= 2 << 31: # Lower than roughly 2.1 billion.
|
||||
# Apply an effect to roughly 50% of particles here (the exact amount is random and varies constantly).
|
||||
pass
|
||||
[/codeblock]
|
||||
When a particle respawns after expiring, it will generate and use a different seed. This means [code]seed[/code] cannot be used to identify a given particle after it respawns. If the particle is inactive ([code]active[/code] is [code]false[/code]), [code]seed[/code] is [code]0[/code].
|
||||
- [code]transform[/code]: The particle's transform. If you're only interested in the particle's position, use [code]transform.origin[/code]. If [member CPUParticles3D.local_coords] is [code]true[/code], you'll want to set nodes' [i]local[/i] transform when moving them to match particle positions. If [member CPUParticles3D.local_coords] is [code]false[/code], you'll want to set nodes' [i]global[/i] transform when moving them to match particle positions. If the particle is inactive ([code]active[/code] is [code]false[/code]), [code]transform[/code] is an identity [code]Transform3D()[/code].
|
||||
- [code]velocity[/code]: The particle's linear velocity (movement speed) in units per second on each axis. This doesn't take the node's movement into account if [member local_coords] is [code]true[/code]. If the particle is inactive ([code]active[/code] is [code]false[/code]), [code]velocity[/code] is [code]Vector3(0, 0, 0)[/code].
|
||||
- [code]active[/code]: [code]true[/code] if the particle is currently alive, [code]false[/code] otherwise. Typically, all particles are active except in two scenarios: (1) The particle system has just started and its [member CPUParticles3D.explosiveness] is not equal to [code]1.0[/code]. Therefore, there are less active particles than the [member CPUParticles3D.amount] configured. (2) The particle system has just had [member CPUParticles3D.emitting] set to [code]false[/code]. After all particles have expired (plus some additional time), the signal will no longer be emitted.
|
||||
[b]Note:[/b] Only emitted when the particle system is currently [member emitting] or when at least 1 particle is still active.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="particles_updated">
|
||||
<argument index="0" name="particles" type="CPUParticle3D[]" />
|
||||
<param index="0" name="particles" type="Dictionary[]" />
|
||||
<description>
|
||||
Emitted when one or more particles are updated (every [code]1.0 /[/code] [member fixed_fps] seconds, or every rendered frame if [member fixed_fps] is [code]0[/code]). This can be used to make nodes (such as [Light3D]s, [AudioStreamPlayer3D]s or other [CPUParticles3D]s) follow particles individually for advanced effects. See [CPUParticle3D] to access information on each individual particle.
|
||||
Emitted when one or more particles are updated (every [code]1.0 /[/code] [member fixed_fps] seconds, or every rendered frame if [member fixed_fps] is [code]0[/code]). This can be used to make nodes (such as [Light3D]s, [AudioStreamPlayer3D]s or other [CPUParticles3D]s) follow particles individually for advanced effects. See [signal particles_expired]'s description for information on dictionary keys contained within the array parameter.
|
||||
[b]Note:[/b] Only emitted when the particle system is currently [member emitting] or when at least 1 particle is still active.
|
||||
</description>
|
||||
</signal>
|
||||
|
|
|
@ -679,7 +679,14 @@ void CPUParticles3D::_particles_process(double p_delta) {
|
|||
|
||||
for (int i = 0; i < pcount; i++) {
|
||||
Particle &p = parray[i];
|
||||
Ref<CPUParticle3D> cpu_particle = memnew(CPUParticle3D);
|
||||
Dictionary cpu_particle;
|
||||
// These will be overridden if the particle is active.
|
||||
cpu_particle["active"] = false;
|
||||
cpu_particle["transform"] = Transform3D();
|
||||
cpu_particle["color"] = Color();
|
||||
cpu_particle["velocity"] = Vector3();
|
||||
cpu_particle["phase"] = 0.0f;
|
||||
cpu_particle["seed"] = 0;
|
||||
|
||||
if (!emitting && !p.active) {
|
||||
cpu_particles.set(i, cpu_particle);
|
||||
|
@ -1151,12 +1158,12 @@ void CPUParticles3D::_particles_process(double p_delta) {
|
|||
|
||||
// If we got down here, we got past all the `continue`s from inactive particles.
|
||||
// Therefore, the particle is active by definition.
|
||||
cpu_particle->active = true;
|
||||
cpu_particle->transform = p.transform;
|
||||
cpu_particle->color = p.color;
|
||||
cpu_particle->velocity = p.velocity;
|
||||
cpu_particle->phase = p.time;
|
||||
cpu_particle->seed = p.seed;
|
||||
cpu_particle["active"] = true;
|
||||
cpu_particle["transform"] = p.transform;
|
||||
cpu_particle["color"] = p.color;
|
||||
cpu_particle["velocity"] = p.velocity;
|
||||
cpu_particle["phase"] = p.time;
|
||||
cpu_particle["seed"] = p.seed;
|
||||
cpu_particles.set(i, cpu_particle);
|
||||
|
||||
// Empirically determined to work at Fixed FPS set to 0 (depends on rendering framerate),
|
||||
|
@ -1486,8 +1493,8 @@ void CPUParticles3D::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("restart"), &CPUParticles3D::restart);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("particles_updated", PropertyInfo(Variant::ARRAY, "particles", PROPERTY_HINT_ARRAY_TYPE, "CPUParticle3D")));
|
||||
ADD_SIGNAL(MethodInfo("particles_expired", PropertyInfo(Variant::ARRAY, "particles", PROPERTY_HINT_ARRAY_TYPE, "CPUParticle3D")));
|
||||
ADD_SIGNAL(MethodInfo("particles_updated", PropertyInfo(Variant::ARRAY, "particles", PROPERTY_HINT_ARRAY_TYPE, "Dictionary")));
|
||||
ADD_SIGNAL(MethodInfo("particles_expired", PropertyInfo(Variant::ARRAY, "particles", PROPERTY_HINT_ARRAY_TYPE, "Dictionary")));
|
||||
|
||||
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");
|
||||
|
@ -1752,38 +1759,3 @@ CPUParticles3D::~CPUParticles3D() {
|
|||
ERR_FAIL_NULL(RenderingServer::get_singleton());
|
||||
RS::get_singleton()->free(multimesh);
|
||||
}
|
||||
|
||||
// CPUParticle3D
|
||||
|
||||
bool CPUParticle3D::is_active() const {
|
||||
return active;
|
||||
}
|
||||
|
||||
Transform3D CPUParticle3D::get_transform() const {
|
||||
return transform;
|
||||
}
|
||||
|
||||
Color CPUParticle3D::get_color() const {
|
||||
return color;
|
||||
}
|
||||
|
||||
Vector3 CPUParticle3D::get_velocity() const {
|
||||
return velocity;
|
||||
}
|
||||
|
||||
float CPUParticle3D::get_phase() const {
|
||||
return phase;
|
||||
}
|
||||
|
||||
uint32_t CPUParticle3D::get_seed() const {
|
||||
return seed;
|
||||
}
|
||||
|
||||
void CPUParticle3D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("is_active"), &CPUParticle3D::is_active);
|
||||
ClassDB::bind_method(D_METHOD("get_transform"), &CPUParticle3D::get_transform);
|
||||
ClassDB::bind_method(D_METHOD("get_color"), &CPUParticle3D::get_color);
|
||||
ClassDB::bind_method(D_METHOD("get_velocity"), &CPUParticle3D::get_velocity);
|
||||
ClassDB::bind_method(D_METHOD("get_phase"), &CPUParticle3D::get_phase);
|
||||
ClassDB::bind_method(D_METHOD("get_seed"), &CPUParticle3D::get_seed);
|
||||
}
|
||||
|
|
|
@ -320,31 +320,4 @@ VARIANT_ENUM_CAST(CPUParticles3D::Parameter)
|
|||
VARIANT_ENUM_CAST(CPUParticles3D::ParticleFlags)
|
||||
VARIANT_ENUM_CAST(CPUParticles3D::EmissionShape)
|
||||
|
||||
/**
|
||||
* Higher-level version of the Particle struct. Unlike the Particle struct,
|
||||
* CPUParticle3D is exposed to the scripting API for use by the `particles_updated` signal.
|
||||
*/
|
||||
class CPUParticle3D : public RefCounted {
|
||||
GDCLASS(CPUParticle3D, RefCounted);
|
||||
|
||||
friend class CPUParticles3D;
|
||||
|
||||
bool active = false;
|
||||
Transform3D transform;
|
||||
Color color;
|
||||
Vector3 velocity;
|
||||
float phase = 0.0f;
|
||||
uint32_t seed = 0;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
bool is_active() const;
|
||||
Transform3D get_transform() const;
|
||||
Color get_color() const;
|
||||
Vector3 get_velocity() const;
|
||||
float get_phase() const;
|
||||
uint32_t get_seed() const;
|
||||
};
|
||||
|
||||
#endif // CPU_PARTICLES_3D_H
|
||||
|
|
|
@ -543,7 +543,6 @@ void register_scene_types() {
|
|||
GDREGISTER_CLASS(GPUParticlesAttractorSphere3D);
|
||||
GDREGISTER_CLASS(GPUParticlesAttractorVectorField3D);
|
||||
GDREGISTER_CLASS(CPUParticles3D);
|
||||
GDREGISTER_CLASS(CPUParticle3D);
|
||||
GDREGISTER_CLASS(Marker3D);
|
||||
|
||||
GDREGISTER_CLASS(RootMotionView);
|
||||
|
|
Loading…
Reference in New Issue
Block a user