From d5eeeb45539ed543a4b7cc60f7d968ffbbd74027 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 22 Mar 2021 18:56:29 -0500 Subject: [PATCH] Create an early particle system --- examples/CMakeLists.txt | 3 +- examples/particles/CMakeLists.txt | 15 +++ examples/particles/src/main.cpp | 61 ++++++++++ include/simpleengine/particle/emitter.h | 104 ++++++++++++++++++ include/simpleengine/particle/particle.h | 94 ++++++++++++++++ .../simpleengine/particle/particle_property.h | 25 +++++ .../properties/random_lifetime_property.h | 37 +++++++ .../properties/random_velocity_property.h | 41 +++++++ include/simpleengine/range.h | 27 +++++ include/simpleengine/range_2.h | 29 +++++ 10 files changed, 435 insertions(+), 1 deletion(-) create mode 100644 examples/particles/CMakeLists.txt create mode 100644 examples/particles/src/main.cpp create mode 100644 include/simpleengine/particle/emitter.h create mode 100644 include/simpleengine/particle/particle.h create mode 100644 include/simpleengine/particle/particle_property.h create mode 100644 include/simpleengine/particle/properties/random_lifetime_property.h create mode 100644 include/simpleengine/particle/properties/random_velocity_property.h create mode 100644 include/simpleengine/range.h create mode 100644 include/simpleengine/range_2.h diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7916283..9a5d44f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,3 +1,4 @@ # Add examples as a subdirectory add_subdirectory(snake) -add_subdirectory(animation) \ No newline at end of file +add_subdirectory(animation) +add_subdirectory(particles) \ No newline at end of file diff --git a/examples/particles/CMakeLists.txt b/examples/particles/CMakeLists.txt new file mode 100644 index 0000000..7218a29 --- /dev/null +++ b/examples/particles/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required (VERSION 3.6) +project(ParticlesExample) + +add_executable(particles src/main.cpp) + +# Link headers and source files. +file(GLOB_RECURSE source_list src/*.cpp) +target_sources(particles PRIVATE ${source_list}) +target_include_directories(particles PUBLIC include) + +# Link simpleengine +target_link_libraries(particles PUBLIC simpleengine) + +# Set standard to C++17 +set_target_properties(particles PROPERTIES CXX_STANDARD 17 CXX_EXTENSIONS OFF) \ No newline at end of file diff --git a/examples/particles/src/main.cpp b/examples/particles/src/main.cpp new file mode 100644 index 0000000..a5721ad --- /dev/null +++ b/examples/particles/src/main.cpp @@ -0,0 +1,61 @@ +// +// Created by SeanOMik on 3/21/2021. +// Github: https://github.com/SeanOMik +// Email: seanomik@gmail.com +// + +#include +#include +#include + +#include +#include +#include +#include + +// Particle +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace se = simpleengine; + +class ParticleEmitter : public se::particle::ParticleEmitter { +private: + using super = se::particle::ParticleEmitter; + + sf::Sprite sprite; + sf::Texture texture; + + super::ParticlePropertyVector properties = { + std::make_shared(se::Range2f(-1.5f, 1.5f, -1.5f, 1.5f)), + std::make_shared(se::Rangef(1'500, 9'500)) + }; +public: + ParticleEmitter() : super(texture, sf::Vector2f(350, 350), 0.4, 5, 1500, se::Range2f(-15, 15, -15, -15), se::particle::ParticleAttributes{0, sf::Vector2f(), 5, sf::Vector2f(2, 2)}, properties) { + texture.loadFromFile("particle.png"); // The particle I tested with was 5x5 pixels + texture.setSmooth(true); + } + + void Update(const float& delta_time) override { + super::Update(delta_time); + } + + void Render(sf::RenderTarget* target) override { + super::Render(target); + } +}; + +int main(int argc, char *argv[]) { + simpleengine::Game game(700, 700, "SimpleEngine - Particles Example"); + game.AddEvent(new ParticleEmitter()); + + return game.Run(); +} \ No newline at end of file diff --git a/include/simpleengine/particle/emitter.h b/include/simpleengine/particle/emitter.h new file mode 100644 index 0000000..9356246 --- /dev/null +++ b/include/simpleengine/particle/emitter.h @@ -0,0 +1,104 @@ +// +// Created by SeanOMik on 3/21/2021. +// Github: https://github.com/SeanOMik +// Email: seanomik@gmail.com +// + +#ifndef SIMPLEENGINE_PARTICLE_EMITTER_H +#define SIMPLEENGINE_PARTICLE_EMITTER_H + +#include "../event.h" +#include "particle_property.h" +#include "particle.h" +#include "../random.h" +#include "../range_2.h" + +#include +#include +#include +#include + +#include +#include + +namespace simpleengine { + namespace particle { + struct ParticleAttributes { + uint32_t lifetime_ms; + sf::Vector2f start_velocity; + float rotation_velocity = 0; + sf::Vector2f scale = sf::Vector2f(1, 1); + + /* ParticleAttributes(uint32_t lifetime_ms, sf::Vector2f start_velocity, float rotation_velocity) : lifetime_ms(lifetime_ms), start_velocity(start_velocity), rotation_velocity(rotation_velocity) { + + } */ + }; + + class ParticleEmitter : public simpleengine::Event { + protected: + using ParticlePropertyPtr = std::shared_ptr; + using ParticlePropertyVector = std::vector; + public: + explicit ParticleEmitter(sf::Texture& texture, sf::Vector2f position, double emit_variance, uint32_t emit_count, uint32_t particle_count, Range2f particle_range, ParticleAttributes attributes, ParticlePropertyVector& properties) + : texture(texture), position(position), emit_variance(emit_variance), emit_count(emit_count), particle_count(particle_count), particle_range(particle_range), attributes(attributes), properties(properties) { + time.restart(); + } + + void SetProperties(ParticlePropertyVector& properties) { + this->properties = properties; + } + + virtual void Update(const float& delta_time) override { + for (std::vector::iterator it = particles.begin(); it != particles.end(); ) { + it->Update(delta_time); + + if (it->IsDestroying()) { + it = particles.erase(it); + } else { + it++; + } + } + + if (particles.size() < particle_count) { + if (random.NextInRange(0, 1) < emit_variance) { + for (uint32_t i = 0; i < emit_count; i++) { + Particle particle(texture, attributes.start_velocity, attributes.lifetime_ms, attributes.rotation_velocity, properties); + + // Offset the position of the particle randomly + sf::Vector2f new_pos; + new_pos.x = random.NextInRange(particle_range.min_x, particle_range.max_x); + new_pos.y = random.NextInRange(particle_range.min_y, particle_range.max_y); + new_pos += position; + particle.GetSprite().setPosition(new_pos); + + particle.GetSprite().setScale(attributes.scale.x, attributes.scale.y); + + particles.emplace_back(std::move(particle)); + } + } + } + } + + virtual void Render(sf::RenderTarget* target) override { + for (simpleengine::particle::Particle particle : particles) { + particle.Render(target); + } + } + protected: + sf::Texture& texture; + sf::Vector2f position; + double emit_variance; + uint32_t emit_count; + uint32_t particle_count; + Range2f particle_range; + ParticleAttributes attributes; + ParticlePropertyVector& properties; + + std::vector particles; + sf::Clock time; + simpleengine::Random random; + }; + } +} + +#endif //SIMPLEENGINE_PARTICLE_EMITTER_H diff --git a/include/simpleengine/particle/particle.h b/include/simpleengine/particle/particle.h new file mode 100644 index 0000000..37af348 --- /dev/null +++ b/include/simpleengine/particle/particle.h @@ -0,0 +1,94 @@ +// +// Created by SeanOMik on 3/21/2021. +// Github: https://github.com/SeanOMik +// Email: seanomik@gmail.com +// + +#ifndef SIMPLEENGINE_PARTICLE_H +#define SIMPLEENGINE_PARTICLE_H + +#include "../event.h" +#include "particle_property.h" + +#include +#include +#include +#include + +#include + +namespace simpleengine { + namespace particle { + class Particle : public simpleengine::Event { + private: + using super = simpleengine::Event; + protected: + using ParticlePropertyPtr = std::shared_ptr; + using ParticlePropertyVector = std::vector; + public: + Particle(sf::Texture& texture, sf::Vector2f velocity, uint32_t lifetime_ms, float rotation_velocity, + ParticlePropertyVector properties) : simpleengine::Event(nullptr), velocity(velocity), lifetime_ms(lifetime_ms), + rotation_velocity(rotation_velocity), properties(properties) { + age_clock.restart(); + sprite.setTexture(texture); + + for (ParticlePropertyPtr property : properties) { + property->OnParticleSpawn(*this); + } + } + + virtual void Update(const float& delta_time) override { + if (age_clock.getElapsedTime().asMilliseconds() >= lifetime_ms) { + super::Destroy(); + return; + } + + for (ParticlePropertyPtr property : properties) { + property->Update(*this); + } + + sprite.move(velocity.x, velocity.y); + } + + virtual void Render(sf::RenderTarget* target) override { + target->draw(sprite); + } + + sf::Sprite& GetSprite() { + return sprite; + } + + /* void SetVelocity(const sf::Vector2f& velocity) { + this->velocity = velocity; + } + + const sf::Vector2f& GetVelocity() { + return velocity; + } + + void SetRotationVelocity(const float& rotation_velocity) { + this->rotation_velocity = rotation_velocity; + } + + const float& GetRotationVelocity() { + return rotation_velocity; + } + private: */ + + const sf::Time GetAge() const { + return age_clock.getElapsedTime(); + } + + sf::Sprite sprite; + sf::Vector2f velocity; + uint32_t lifetime_ms; + float rotation_velocity; + ParticlePropertyVector properties; + + private: + sf::Clock age_clock; + }; + } +} + +#endif //SIMPLEENGINE_PARTICLE_H diff --git a/include/simpleengine/particle/particle_property.h b/include/simpleengine/particle/particle_property.h new file mode 100644 index 0000000..20a6874 --- /dev/null +++ b/include/simpleengine/particle/particle_property.h @@ -0,0 +1,25 @@ +// +// Created by SeanOMik on 3/21/2021. +// Github: https://github.com/SeanOMik +// Email: seanomik@gmail.com +// + +#ifndef SIMPLEENGINE_PARTICLE_PROPERTY_H +#define SIMPLEENGINE_PARTICLE_PROPERTY_H + +#include "../event.h" + +#include + +namespace simpleengine { + namespace particle { + class Particle; + class ParticleProperty { + public: + virtual void OnParticleSpawn(simpleengine::particle::Particle& particle) = 0; + virtual void Update(simpleengine::particle::Particle& particle) = 0; + }; + } +} + +#endif //SIMPLEENGINE_PARTICLE_PROPERTY_H diff --git a/include/simpleengine/particle/properties/random_lifetime_property.h b/include/simpleengine/particle/properties/random_lifetime_property.h new file mode 100644 index 0000000..5567579 --- /dev/null +++ b/include/simpleengine/particle/properties/random_lifetime_property.h @@ -0,0 +1,37 @@ +// +// Created by SeanOMik on 3/22/2021. +// Github: https://github.com/SeanOMik +// Email: seanomik@gmail.com +// + +#ifndef SIMPLEENGINE_RANDOM_LIFETIME_PROPERTY_H +#define SIMPLEENGINE_RANDOM_LIFETIME_PROPERTY_H + +#include "../particle_property.h" +#include "../../range.h" +#include "../../random.h" +#include "../particle.h" + +namespace simpleengine { + namespace particle { + class RandomLifetimeParticleProperty : public ParticleProperty { + public: + RandomLifetimeParticleProperty(simpleengine::Rangef range) : range(range) { + + } + + void OnParticleSpawn(simpleengine::particle::Particle& particle) override { + simpleengine::Random rand; + particle.lifetime_ms = rand.NextInRange(range.min, range.max); + } + + void Update(simpleengine::particle::Particle& particle) override { + + } + private: + simpleengine::Rangef range; + }; + } +} + +#endif //SIMPLEENGINE_RANDOM_LIFETIME_PROPERTY_H diff --git a/include/simpleengine/particle/properties/random_velocity_property.h b/include/simpleengine/particle/properties/random_velocity_property.h new file mode 100644 index 0000000..d4f8972 --- /dev/null +++ b/include/simpleengine/particle/properties/random_velocity_property.h @@ -0,0 +1,41 @@ +// +// Created by SeanOMik on 3/22/2021. +// Github: https://github.com/SeanOMik +// Email: seanomik@gmail.com +// + +#ifndef SIMPLEENGINE_RANDOM_VELOCITY_PROPERTY_H +#define SIMPLEENGINE_RANDOM_VELOCITY_PROPERTY_H + +#include "../particle_property.h" +#include "../../range_2.h" +#include "../../random.h" +#include "../particle.h" + +namespace simpleengine { + namespace particle { + class RandomVelocityParticleProperty : public ParticleProperty { + public: + RandomVelocityParticleProperty(simpleengine::Range2f range) : range(range) { + + } + + void OnParticleSpawn(simpleengine::particle::Particle& particle) override { + simpleengine::Random rand; + sf::Vector2f velocity; + velocity.x = rand.NextInRange(range.min_x, range.max_x); + velocity.y = rand.NextInRange(range.min_y, range.max_y); + + particle.velocity = velocity; + } + + void Update(simpleengine::particle::Particle& particle) override { + + } + private: + simpleengine::Range2f range; + }; + } +} + +#endif //SIMPLEENGINE_RANDOM_VELOCITY_PROPERTY_H diff --git a/include/simpleengine/range.h b/include/simpleengine/range.h new file mode 100644 index 0000000..bef45ca --- /dev/null +++ b/include/simpleengine/range.h @@ -0,0 +1,27 @@ +// +// Created by SeanOMik on 3/22/2021. +// Github: https://github.com/SeanOMik +// Email: seanomik@gmail.com +// + +#ifndef SIMPLEENGINE_RANGE_H +#define SIMPLEENGINE_RANGE_H + +#include + +namespace simpleengine { + template + class Range { + public: + Range() = default; + Range(T min, T max) : min(min), max(max) {} + + T min = std::numeric_limits::min(); + T max = std::numeric_limits::max(); + }; + + typedef Range Rangef; + typedef Range Rangei; +} + +#endif //SIMPLEENGINE_RANGE_H diff --git a/include/simpleengine/range_2.h b/include/simpleengine/range_2.h new file mode 100644 index 0000000..5f9b2fe --- /dev/null +++ b/include/simpleengine/range_2.h @@ -0,0 +1,29 @@ +// +// Created by SeanOMik on 3/22/2021. +// Github: https://github.com/SeanOMik +// Email: seanomik@gmail.com +// + +#ifndef SIMPLEENGINE_RANGE_2_H +#define SIMPLEENGINE_RANGE_2_H + +#include + +namespace simpleengine { + template + class Range2 { + public: + Range2() = default; + Range2(T min_x, T max_x, T min_y, T max_y) : min_x(min_x), max_x(max_x), min_y(min_y), max_y(max_y) {} + + T min_x = std::numeric_limits::min(); + T max_x = std::numeric_limits::max(); + T min_y = std::numeric_limits::min(); + T max_y = std::numeric_limits::max(); + }; + + typedef Range2 Range2f; + typedef Range2 Range2i; +} + +#endif //SIMPLEENGINE_RANGE_H