replit / kaboom

💥 JavaScript game library
https://kaboomjs.com
MIT License
2.67k stars 228 forks source link

Proposal: simple particle systems using "emitter()" method #226

Open vascanera opened 3 years ago

vascanera commented 3 years ago

This is a proposal to add simple particle emitters for game objects. When applied to a sprite object, this method would create a particle emitter with all required config options (like widt/height of the bounding emitter area, particle lifetime, speed, gravity, angle, angleVariation, sizeVariation, initial and final particle sizes, wind force/direction, sprite frames to use randomly - or continuously, etc.).

Example: emitter({ width: 100, height: 100, angle: 90, gravity: 10, speed: 100, frames: [0, 1, 4], size: vec2(4, 4), lifetime: 2 }

I have no idea how hard it would be to implement for Kaboom, but I think it would be a super addition to the engine/library. Cheers!

slmjkdbtl commented 3 years ago

It should be pretty easy to compose and more flexible with kaboom's existing component system:

here's an example:

loop(0.1, () => {
    add([
        pos(mousePos()),
        sprite(choose(sprites)),
        origin("center"),
        scale(rand(0.5, 1)),
        body({ solid: false, }),
        lifespan(1, { fade: 0.5 }),
        move(choose([LEFT, RIGHT]), rand(60, 240)),
    ]);
});
vascanera commented 3 years ago

Is this feasible, performance-wise? I'm thinking of a full-view rain effect (rain drops) + some smoke / fire spots. I'll try it anyway, see how things come along. Thanks for the hints!

slmjkdbtl commented 3 years ago

This should be the fastest way possible in Kaboom, since with the component system you're already only paying for stuff that you use, optimizing this would be optimizing the general component system and rendering. As long as you don't let the particles participate in any physics events (don't give them solid(), area(), and specify body({solid: false}) if you're using gravity), it should be fine.

One possible thing that might be slow is, every objects will be sorted by their layer and z value per frame, so if there's a ton of particles they might slow down the sorting, where internally they might don't really need sorting

vascanera commented 3 years ago

Alright. Thanks for explaining it. Reading the particles demo code, I can see the jump() method is used for each particle. I think it would be nice to also have a passive method (attached to another object - the "emitter" object), something like throw() - maybe not a good name, since we have Throw as reserved keyword in the language - but, you get the idea. Something like:

some_game_object.throw(another_game_object_or_sprite_etc, { force: 100, angle: 90});

This would really sound more real-world-like. It describes something that "throws" other things, and not things that "throw" themselves. What do you think?

slmjkdbtl commented 3 years ago

Yeah it's something I've been wanting to do. Currently the body component maintains its own velocity, I'm planning a small refactor to let objects have global .velocity property by the pos() component which will make this easier.

But I'm not following what you meant obj throwing another, how is it mechanically different from throwing themselves? Or it's just semantics

vascanera commented 3 years ago

Hello. Regarding objects that "throw themselves", you would (at least mentally) need to keep track of said objects, but. as we all know, [usually] particles are non-interactive (non-bodies), mostly used for visual effects and decoration, so they are something like "fire and forget" entities that should not take up "slots" among the "real" body objects. Overall, yeah, you could say it's just [inverted] semantics. I see them more as side effects rather than "real" world entities.

slmjkdbtl commented 3 years ago

Yeah I think semantically it definitely makes more sense to have some other "throw" them instead of throwing themselves. Just to be clear about what you're thinking, by throwing others, the thrower's own properties like the position or size won't affect the throwing behavior right, everything is just defined by the angle and force passed to throw()?

vascanera commented 3 years ago

Correct. Angle, Force and maybe other parameters like wieght, variation, lifetime, decay, etc.