aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaro <jarorutjes07@gmail.com>2024-11-08 19:20:05 +0100
committerJaro <jarorutjes07@gmail.com>2024-11-08 19:20:05 +0100
commitd69adb5666fd6f73edbc6d410afcdf23c24e7c6b (patch)
tree00dd0280e603be5f67753fa1171582109b1065c4
parent0feda3d123ff99a1b9e41837482268bebfd9140a (diff)
particle updated
-rw-r--r--src/crepe/Particle.cpp25
-rw-r--r--src/crepe/Particle.h24
-rw-r--r--src/crepe/api/ParticleEmitter.cpp35
-rw-r--r--src/crepe/api/ParticleEmitter.h72
-rw-r--r--src/crepe/system/ParticleSystem.cpp159
-rw-r--r--src/crepe/system/ParticleSystem.h10
-rw-r--r--src/example/particles.cpp46
7 files changed, 262 insertions, 109 deletions
diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp
index 4810e80..d2b1880 100644
--- a/src/crepe/Particle.cpp
+++ b/src/crepe/Particle.cpp
@@ -1,20 +1,29 @@
#include "Particle.h"
-using namespace crepe;
+#include "api/Transform.h"
-Particle::Particle() { this->active = false; }
+using namespace crepe;
-void Particle::reset(float lifespan, Position position, Position velocity) {
+void Particle::reset(uint32_t lifespan, Vector2 position, Vector2 velocity, double angle) {
this->time_in_life = 0;
this->lifespan = lifespan;
this->position = position;
this->velocity = velocity;
this->active = true;
+ this->angle = angle;
+}
+
+void Particle::update() {
+ time_in_life++;
+ if (time_in_life >= lifespan)
+ {
+ this->active = false;
+ return;
+ }
+ velocity += force_over_time;
+ position += velocity;
}
-void Particle::update(float deltaTime) {
- time_in_life += deltaTime;
- position.x += velocity.x * deltaTime;
- position.y += velocity.y * deltaTime;
- if (time_in_life >= lifespan) this->active = false;
+void Particle::stop_movement() {
+ this->velocity = {0,0};
}
diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h
index 21e691d..f52196c 100644
--- a/src/crepe/Particle.h
+++ b/src/crepe/Particle.h
@@ -1,22 +1,24 @@
#pragma once
-#include "Position.h"
+#include "api/Transform.h"
namespace crepe {
class Particle {
public:
- Position position;
- // FIXME: `Position` is an awkward name for a 2D vector. See FIXME comment in
- // api/Transform.h for fix proposal.
- Position velocity;
- float lifespan;
- bool active;
+ Vector2 position;
+ Vector2 velocity;
+ Vector2 force_over_time;
+ uint32_t lifespan;
+ bool active = false;
+ uint32_t time_in_life = 0;
+ double angle;
- Particle();
- void reset(float lifespan, Position position, Position velocity);
- void update(float deltaTime);
- float time_in_life;
+ Particle() = default;
+ void reset(uint32_t lifespan, Vector2 position, Vector2 velocity,double angle);
+ void update();
+ void stop_movement();
+
};
} // namespace crepe
diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp
index 3b2e2f2..f585a81 100644
--- a/src/crepe/api/ParticleEmitter.cpp
+++ b/src/crepe/api/ParticleEmitter.cpp
@@ -1,36 +1,19 @@
-#include <ctime>
-#include <iostream>
-
#include "Particle.h"
#include "ParticleEmitter.h"
using namespace crepe;
-ParticleEmitter::ParticleEmitter(game_object_id_t id, uint32_t max_particles,
- uint32_t emission_rate, uint32_t speed,
- uint32_t speed_offset, uint32_t angle,
- uint32_t angleOffset, float begin_lifespan,
- float end_lifespan)
- : Component(id), max_particles(max_particles), emission_rate(emission_rate),
- speed(speed), speed_offset(speed_offset), position{0, 0},
- begin_lifespan(begin_lifespan), end_lifespan(end_lifespan) {
- std::srand(
- static_cast<uint32_t>(std::time(nullptr))); // initialize random seed
- std::cout << "Create emitter" << std::endl;
- // FIXME: Why do these expressions start with `360 +`, only to be `% 360`'d
- // right after? This does not make any sense to me.
- min_angle = (360 + angle - (angleOffset % 360)) % 360;
- max_angle = (360 + angle + (angleOffset % 360)) % 360;
- position.x = 400; // FIXME: what are these magic values?
- position.y = 400;
- for (size_t i = 0; i < max_particles; i++) {
- this->particles.emplace_back();
- }
+ParticleEmitter::ParticleEmitter(uint32_t game_object_id, const ParticleEmitterData& data) : Component(game_object_id),data(data) {
+ for (size_t i = 0; i < this->data.max_particles; i++) {
+ this->data.particles.emplace_back();
+ }
}
+
ParticleEmitter::~ParticleEmitter() {
- std::vector<Particle>::iterator it = this->particles.begin();
- while (it != this->particles.end()) {
- it = this->particles.erase(it);
+ std::vector<Particle>::iterator it = this->data.particles.begin();
+ while (it != this->data.particles.end()) {
+ it = this->data.particles.erase(it);
}
}
+
diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h
index 5939723..f931e8c 100644
--- a/src/crepe/api/ParticleEmitter.h
+++ b/src/crepe/api/ParticleEmitter.h
@@ -5,38 +5,58 @@
#include "Component.h"
#include "Particle.h"
+#include "Transform.h"
+
+class Sprite;
namespace crepe {
class ParticleEmitter : public Component {
public:
- ParticleEmitter(game_object_id_t id, uint32_t max_particles,
- uint32_t emission_rate, uint32_t speed,
- uint32_t speed_offset, uint32_t angle, uint32_t angleOffset,
- float begin_lifespan, float end_lifespan);
- ~ParticleEmitter();
-
- //! position of the emitter
- Position position;
- //! maximum number of particles
- uint32_t max_particles;
- //! rate of particle emission
- uint32_t emission_rate;
- //! base speed of the particles
- uint32_t speed;
- //! offset for random speed variation
- uint32_t speed_offset;
- //! min angle of particle emission
- uint32_t min_angle;
- //! max angle of particle emission
- uint32_t max_angle;
- //! begin Lifespan of particle (only visual)
- float begin_lifespan;
- //! begin Lifespan of particle
- float end_lifespan;
+ struct ParticleBoundary{
+ //! boundary width (midpoint is emitter location)
+ double boundary_width = 0.0;
+ //! boundary height (midpoint is emitter location)
+ double boundary_height = 0.0;
+ //! boundary offset from particle emitter location
+ Vector2 boundary_offset;
+ //! reset on exit or stop velocity and set max postion
+ bool reset_on_exit = false;
+ };
- //! collection of particles
- std::vector<Particle> particles;
+ struct ParticleEmitterData{
+ //! position of the emitter
+ Vector2 position;
+ //! maximum number of particles
+ uint32_t max_particles = 0;
+ //! rate of particle emission per update (Lowest value = 0.001 any lower is ignored)
+ double emission_rate = 0;
+ //! min speed of the particles
+ double min_speed = 0;
+ //! min speed of the particles
+ double max_speed = 0;
+ //! min angle of particle emission
+ double min_angle = 0;
+ //! max angle of particle emission
+ double max_angle = 0;
+ //! begin Lifespan of particle (only visual)
+ double begin_lifespan = 0.0;
+ //! end Lifespan of particle
+ double end_lifespan = 0.0;
+ //! force over time (physics)
+ Vector2 force_over_time;
+ //! particle boundary
+ ParticleBoundary boundary;
+ //! collection of particles
+ std::vector<Particle> particles;
+ //! sprite reference
+ const Sprite* sprite;
+ };
+public:
+ ParticleEmitter(uint32_t game_object_id, const ParticleEmitterData& data);
+ ~ParticleEmitter();
+public:
+ ParticleEmitterData data;
};
} // namespace crepe
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index 397b586..23534a3 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -1,62 +1,149 @@
#include <cmath>
#include <ctime>
+#include <cstdlib>
-#include "../ComponentManager.h"
-#include "../api/ParticleEmitter.h"
+#include "api/ParticleEmitter.h"
+#include "api/Vector2.h"
+#include "ComponentManager.h"
#include "ParticleSystem.h"
using namespace crepe;
-ParticleSystem::ParticleSystem() : elapsed_time(0.0f) {}
+ParticleSystem::ParticleSystem() {}
void ParticleSystem::update() {
+
+ // Get all emitters
ComponentManager & mgr = ComponentManager::get_instance();
std::vector<std::reference_wrapper<ParticleEmitter>> emitters
= mgr.get_components_by_type<ParticleEmitter>();
- float delta_time = 0.10;
+
for (ParticleEmitter & emitter : emitters) {
- float update_amount = 1 / static_cast<float>(emitter.emission_rate);
- for (float i = 0; i < delta_time; i += update_amount) {
- emit_particle(emitter);
+
+ // Get transform linked to emitter
+ const Transform& transform = mgr.get_components_by_id<Transform>(emitter.GAME_OBJECT_ID).front().get();
+
+ // Check if within boundary
+ check_bounds(emitter,transform);
+
+ // Emit particles based on emission_rate
+ int updates = calculate_update(this->update_count,emitter.data.emission_rate);
+ for (size_t i = 0; i < updates; i++)
+ {
+ emit_particle(emitter,transform);
}
- for (size_t j = 0; j < emitter.particles.size(); j++) {
- if (emitter.particles[j].active) {
- emitter.particles[j].update(delta_time);
+
+ // Update all particles
+ for (size_t j = 0; j < emitter.data.particles.size(); j++) {
+ if (emitter.data.particles[j].active) {
+ emitter.data.particles[j].update();
}
}
}
+ update_count++;
+ if(update_count == MAX_UPDATE_COUNT) update_count = 0;
}
-void ParticleSystem::emit_particle(ParticleEmitter & emitter) {
- Position initial_position = {emitter.position.x, emitter.position.y};
- float random_angle = 0.0f;
- if (emitter.max_angle < emitter.min_angle) {
- random_angle = ((emitter.min_angle
- + (std::rand()
- % (static_cast<uint32_t>(emitter.max_angle + 360
- - emitter.min_angle + 1))))
- % 360);
+void ParticleSystem::emit_particle(ParticleEmitter & emitter,const Transform& transform) {
+ constexpr double DEG_TO_RAD = M_PI / 180.0;
+
+ Vector2 initial_position = emitter.data.position + transform.position;
+ double min_angle = emitter.data.min_angle;
+ double max_angle = emitter.data.max_angle;
+ double random_angle;
+
+ if (min_angle <= max_angle) {
+ // Standard range (e.g., 10 to 20 degrees)
+ double angle_offset = max_angle - min_angle;
+ random_angle = min_angle + static_cast<double>(std::rand() % static_cast<uint32_t>(angle_offset));
} else {
- random_angle = emitter.min_angle
- + (std::rand()
- % (static_cast<uint32_t>(emitter.max_angle
- - emitter.min_angle + 1)));
+ // Wrap-around range (e.g., 350 to 10 degrees)
+ double angle_offset = (360 - min_angle) + max_angle;
+ random_angle = min_angle + static_cast<double>(std::rand() % static_cast<uint32_t>(angle_offset));
+
+ // Wrap around to keep random_angle within 0-360 degrees
+ if (random_angle >= 360) {
+ random_angle -= 360;
+ }
}
- float angle_in_radians = random_angle * (M_PI / 180.0f);
- float random_speed_offset = (static_cast<float>(std::rand()) / RAND_MAX)
- * (2 * emitter.speed_offset)
- - emitter.speed_offset;
- float velocity_x
- = (emitter.speed + random_speed_offset) * std::cos(angle_in_radians);
- float velocity_y
- = (emitter.speed + random_speed_offset) * std::sin(angle_in_radians);
- Position initial_velocity = {velocity_x, velocity_y};
- for (size_t i = 0; i < emitter.particles.size(); i++) {
- if (!emitter.particles[i].active) {
- emitter.particles[i].reset(emitter.end_lifespan, initial_position,
- initial_velocity);
+
+ // Generate a random speed between min_speed and max_speed
+ double speed_offset = emitter.data.max_speed - emitter.data.min_speed;
+ double random_speed = emitter.data.min_speed + static_cast<double>(std::rand() % static_cast<uint32_t>(speed_offset));
+
+ // Convert random_angle to radians
+ double angle_radians = random_angle * DEG_TO_RAD;
+
+ Vector2 velocity = {
+ random_speed * std::cos(angle_radians),
+ random_speed * std::sin(angle_radians)
+ };
+
+
+ for (size_t i = 0; i < emitter.data.particles.size(); i++) {
+ if (!emitter.data.particles[i].active) {
+ emitter.data.particles[i].reset(emitter.data.end_lifespan, initial_position,velocity,random_angle);
break;
}
}
}
+
+int ParticleSystem::calculate_update(int count, double emission) {
+
+ //get interger part of the emission
+ double integer_part = std::floor(emission);
+
+ // Get the fractional part of the emission
+ double fractional_part = emission - integer_part;
+
+ // Convert the fractional part to a denominator value
+ int denominator = static_cast<int>(1.0 / fractional_part);
+
+ // For emissions like 0.01, 0.1, 0.5, etc., calculate the update frequency
+ if (fractional_part > 0) {
+ // Calculate how often the update should be triggered based on the fractional part
+ if (count % denominator == 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ // For integer emissions, return the emission directly
+ return static_cast<int>(emission);
+}
+
+void ParticleSystem::check_bounds(ParticleEmitter & emitter,const Transform& transform)
+{
+ Vector2 offset = emitter.data.boundary.boundary_offset + transform.position + emitter.data.position;
+ double half_width = emitter.data.boundary.boundary_width / 2.0;
+ double half_height = emitter.data.boundary.boundary_height / 2.0;
+
+ // Define boundary edges
+ const double left = offset.x - half_width;
+ const double right = offset.x + half_width;
+ const double top = offset.y - half_height;
+ const double bottom = offset.y + half_height;
+
+ std::vector<Particle>& particles = emitter.data.particles;
+ for (Particle& particle : particles)
+ {
+ const Vector2& position = particle.position;
+
+ // Check if particle is within bounds
+ bool within_bounds = (position.x >= left && position.x <= right && position.y >= top && position.y <= bottom);
+ if (!within_bounds)
+ {
+ if (emitter.data.boundary.reset_on_exit)
+ {
+ particle.active = false;
+ }
+ else
+ {
+ particle.velocity = {0, 0};
+ //todo add that particle goes back to boundary
+ }
+ }
+ }
+}
diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h
index 3ac1d3f..3b9cb54 100644
--- a/src/crepe/system/ParticleSystem.h
+++ b/src/crepe/system/ParticleSystem.h
@@ -10,9 +10,15 @@ public:
void update();
private:
- void emit_particle(ParticleEmitter & emitter); //emits a new particle
+ void emit_particle(ParticleEmitter & emitter,const Transform& transform);
+ int calculate_update(int count, double emission);
+ void check_bounds(ParticleEmitter & emitter,const Transform& transform);
- float elapsed_time; //elapsed time since the last emission
+private:
+ //! counter to count updates to determine how many times emit_particle is called.
+ uint32_t update_count = 0;
+ //! determines the lowest amount of emissionrate (1000 = 0.001 = 1 particle per 1000 updates).
+ const uint32_t MAX_UPDATE_COUNT = 100;
};
} // namespace crepe
diff --git a/src/example/particles.cpp b/src/example/particles.cpp
new file mode 100644
index 0000000..a56ec5c
--- /dev/null
+++ b/src/example/particles.cpp
@@ -0,0 +1,46 @@
+#include <crepe/ComponentManager.h>
+#include <crepe/system/RenderSystem.h>
+#include <crepe/system/CollisionSystem.h>
+#include <crepe/api/AssetManager.h>
+#include <crepe/system/PhysicsSystem.h>
+#include <crepe/api/loopManager.h>
+
+#include <crepe/Component.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Transform.h>
+#include <crepe/api/ParticleEmitter.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/api/Texture.h>
+#include <crepe/api/Color.h>
+
+
+using namespace crepe;
+using namespace std;
+
+int main(int argc, char * argv[]) {
+
+ Color color(0, 0, 0, 0);
+ GameObject *game_object = new GameObject(0, "Name", "Tag", Vector2{0,0},0,0);
+ game_object->add_component<ParticleEmitter>(ParticleEmitter::ParticleEmitterData{
+ .position = {0,0},
+ .max_particles = 0,
+ .emission_rate = 0,
+ .min_speed = 0,
+ .max_speed = 0,
+ .min_angle = 0,
+ .max_angle = 0,
+ .begin_lifespan = 0,
+ .end_lifespan = 0,
+ .force_over_time = 0,
+ .boundary{
+ .boundary_width = 0,
+ .boundary_height = 0,
+ .boundary_offset = {0,0},
+ .reset_on_exit = false,
+ },
+ .sprite = nullptr,
+ });
+
+ return 0;
+}