Syncing two particle effects

I have two particle effects–a “steady dripping” effect, and a “splash” effect–and I want to synchronise them such that the latter happens at the end of the former.

If their birth-intervals matched the duration of the former effect, then they should be in sync–but those values don’t match, and I’d rather not force them to do so.

If I could just offset the start of the latter effect, or manually update its “_tics_since_birth” value–or even if “_tics_since_birth” counted down instead of up–it would, I think, not be too hard to sync the two effects. However, “_tics_since_birth” counts up, is private, and there is no apparent means of offsetting the first “birth” of a particle effect.

(I did try first disabling the latter effect, then using a “doMethodLater” call to start it again after an appropriate amount of time. However, this doesn’t seem to work, even with a call to the relevant system’s “clearToInitial” method. That said, there might be some particular method to getting this approach to work… And that said again, I’m not sure of how reliable “doMethodLater” is when the frame-rate varies, as might be the case just after loading a level…)

Does anyone have any suggestions as to how to do this?

Hmm… It’s looking like perhaps “doMethodLater” is indeed proving unreliable.

In a recent test, I called “doMethodLater” with a specified time of 100.25.

Observing the particle effect within the level, the effect did indeed seem to start as the “doMethodLater” task fired, judging by the timing of the effect and printout from the called method.

However, I also printed out the current time, first just before the call to “doMethodLater”, and second in the method called by “doMethodLater”. The following output is what I got:

(The middle two lines are standard editor output; the numbers are the times reported by “getRealTime”.)

29.629832
Editing...
Playing...
108.47807900000001

According to my calculator, 108.47807900000001 - 29.629832 equals 78.848247–rather short of the 100.25 seconds requested of “doMethodLater”.

So, whether because of frame-rate issues at the start of the level or something else, “doMethodLater” seems to be unreliable for this purpose. :/

Is there another way to do this? Is it perhaps worth my filing a pull-request for some means of offsetting a systems “_tics_since_birth” value, whether on starting the effect or just at-will?

[edit] Regarding the latter, I’m imagining this offset as something that might be set per-system in a particle editor, alongside the birth-rate. When the system is started, it then starts at -offset, allowing the current logic to incorporate the offset without affecting the time between subsequent births.

I don’t know anything about the particle system, but perhaps you can use ParticleInterval objects in conjunction with Sequence and Parallel intervals?

Hmm… I’m not a huge fan of intervals for the most part, but that might work indeed–as long as the timing of an interval is more reliable than that of “doMethodLater” under these circumstances. (I imagine that I’d be using a “Wait” interval to delay the second particle effect.)

That said, looking at the manual entry for ParticleIntervals, I don’t see a parameter for the particle effect itself–does it load the effect from the string that’s passed into the first parameter? If so, then that’s a minor issue: I’m not using the PTF format for my particle-files.

No, it looks like the first argument to ParticleInterval is a ParticleEffect object.

I can’t help but be curious about why you are not a big fan of intervals; if there are usability concerns with a particular feature, it’d help me to know about it.

Ah, you’re quite right–I was looking at the manual (which gives the first parameter as “Particle Effect Name”), and didn’t think to check that against the API. ^^;

(The manual also indicates a “loop” parameter, which doesn’t seem to be present in the API.)

I fear that I’m a bit tired and out of it today to be confident in relating my impression well right now. ^^;

However, I don’t think that it’s an issue that really lends itself to fixing: I’ve never been entirely comfortable with them on a near-fundamental level. I think that, while logically they’re little different to other things, I feel as though I have less control, and less idea of the state of things, when dealing with intervals.

(I almost never use them; I prefer to have “update” methods that run the logic that I have in mind, be it movement, timers, and so on.)

I kind of expected that answer and do understand that perspective; they’re generally things you create once and most of the time they are kind of fire-and-forget and have no more control over it, except perhaps to pause it prematurely. In that sense they are a lot like animations that are set up once, as opposed to tasks, which feel like they give you greater control because you can adjust their behaviour on the fly. On the other hand, intervals make it really easy to quickly set up transitions, but indeed, most of the things you can do with intervals you can also do with tasks.

I plan on further fleshing out Panda’s coroutine support for intervals in the future so that it will be possible to await an interval in a task (ie. the task will pause at the await instruction and continue when the interval has finished), but that wouldn’t take away the fact that task will have no control over the interval during its execution.

Anyway, use what makes the most sense to you; thank you for sharing your perspective.

1 Like

It’s my pleasure; thank you for asking, and for being understanding of my perspective. :slight_smile:

I do intend to try out the ParticleInterval presently for the purpose of the issue in this thread; we’ll see whether that works where “doMethodLater” didn’t…

(I am tempted to create a pull-request for the fairly minor changes that I think would allow the offsetting of a particle effect’s first birth–but since that code is C++, I’d have to recompile to test, and I don’t feel like doing that right now. Maybe when I’m less tired! ^^; )

Ah! A ParticleInterval preceded by a Wait did work! :smiley:

The weird thing is: the same thing, but with a Func calling one of my own methods to start the particle effect didn’t work. This implies that there may be some difference in how I was restarting my particle effect (it’s automatically started when loaded, so I stop it and restart it at a later point).

On the other hand, this doesn’t explain the odd values that I was getting from “globalClock.getRealTime()”–my best guess being that I was misunderstanding what they signified. o_0

I am a bit concerned about the “duration” parameter: I intend for this effect to continue indefinitely. If I recall correctly, leaving it at “0” seemed to result in the effect not apparently running (perhaps having died by the time that I got to it in the level). Any other time would presumably result in it eventually stopping. (I could just give it a ridiculously huge duration, but that’s not my preference.)

So, I intend to try going back to using “doMethodLater” for timing, and modifying my particle-handling so that it doesn’t necessarily start immediately on being loaded. Then I can just start it as usual–hopefully!

*sigh* This is proving frustrating. My attempts to get a non-ParticleInterval version to work have thus far failed. :/

Looking at ParticleInterval, it appears that it takes a fair bit of control over the running of the particle effect, even updating it manually. On the other hand, I’m nevertheless not seeing how it’s managing to be accurate–is it the specific set of particle-related calls that it makes, and perhaps the order thereof? Is it something to do with how intervals are handled that’s causing the timing to line up, where frame-rate issues are mis-aligning my attempts? Is it something else entirely? I really don’t know. :/

(I might just go with the interval solution if it weren’t for the issue of it seeming to require a finite duration.)

If I may ask, is there a new version of the engine due soon, and is there any chance of a pull-request being included in it? I’m sorely tempted to try the change that I have in mind for offsetting a particle-effect’s initial birth, but I don’t want to be left without the ability to create a distributable build that incorporates it. I could compile myself, of course–I think that I recall that the build system can be directed to use a custom build–but that complicates matters, between making sure that I have the right build-settings and incorporating engine-updates.

1.10.5 is due soon, but because it’s such a high-numbered bugfix release, additional features will only make it in if they (1) have no chance of breaking existing behaviour, and (2) come with unit tests to prove that. If you can meet these criteria, I’m happy to entertain a pull request before the release. It would be best to make the pull request against the release/1.10.x branch, then.

Fair enough!

I’m reasonably confident of not breaking existing behaviour–the changes that I have in mind are simple, and will default to existing values, I intend. However, if I studied unit-tests then I’ve forgotten them by now, so I’ll likely look into those if I do go forward with this change!

Thank you for being open to doing so! :slight_smile:

[edit]
Do you have some examples of unit-tests, please? I’m honestly not sure of what tests to write–most of what I’m doing doesn’t involve functions that produce outputs that can be checked. The best idea that I have in mind right now is just to create a test-program that spawns a simple particle effect, and then another with my changes…

(This aside from checking some simple getters and setters, at any rate.)

[edit 2]
The main of the pull-request is done, I think! It just calls for me to add unit-tests. (Depending on feedback, of course.)

You should find it here:

Trying it out in my game, it seems to work quite well, and to have solved my syncing problem! :slight_smile:

Here’s an odd thing: With that solved, it turned out to be surprisingly tricky to then sync up a sound to the effects, despite giving that sound the appropriate length to just loop with the particles and having the actual noise occur at the appropriate point in that duration.

I solved it by setting up a once-off task–as I did when I was trying to do the same to sync the particle effects–and then getting the “tics since birth” of one of the particle effects (this functionality being exposed in the pull request). I then set the sound’s time to that value via a call to “setTime”.

But that alone didn’t quite solve it–I had to call the sound’s “play” method afterwards for the time-offset to take the expected effect!

But all that said, it did take the expected effect, and the sound seems to sync with the particle effects now. :slight_smile:

Ah, I did not notice your edits, sorry about that.

Examples of unit tests can be found in the tests folder in the GitHub repository. You would write one by creating a function that first sets up the bare minimum (ie. the simplest possible ParticleSystem), and then calls the method in question, and then validates some getters to ensure that it behaves properly using the assert statement.

Some tests you could write, off the top of my head (I am not familiar with the particle system, so this may not make sense):

  • Calling softStart without an argument, and then querying the “tics since birth” should yield 0.
  • Calling your version with offset, and then calling “tics since birth” should yield the tics since birth.
  • Sets up a particle effect as normal with softStart without argument, then calls system.update(dt) to simulate running the system for some time and ensures that it births as normal.
  • Sets up a particle effect with softStart and calls system.update(dt) and verifies that it birthed more or fewer particles as modified by the given birth offset.

Ah, fair enough, and thanks! I’ve responded regarding them in the pull-request on GitHub; if more discussion is called for, I’ll take it there, I think!