The problem is that the state machine will never start over, so you can only launch one missile.
The smallest change that can fix this is to call state_machine_.reset_state_machine() when you set missilelaunch_ to true.
However, there are other, better ways to do it.
missilelaunch_ is kind of redundant here, because the state machine itself can keep track of what we’re doing. In this scenario, we need to add state_machine_.stop() in the class constructor so that the state machine won’t start the first time we call PerformMissileLaunchSequence(). Then we’ll change loop() so that PerformMisslleLaunchSequence() is called every loop. (No if statement). Finally, when we want to start the sequence, we’ll just call state_machine_.reset_state_machine().
Another way to do this is to have the state machine run in a loop, like this:
void PerformSequences() {
STATE_MACHINE_BEGIN();
while (true) {
if (missilelaunch_) {
// sequence here
missilelaunch_ = false;
}
YIELD(); // this means "sleep one loop"
}
STATE_MACHINE_END();
The cool part about doing it this way is that you can have the same loop handle other sequences as well, sort of like:
void PerformSequences() {
STATE_MACHINE_BEGIN();
while (true) {
if (missilelaunch_) {
// sequence here
missilelaunch_ = false;
}
if (burp_sequence_) {
// sequence here
burp_sequence_ = false;
}
YIELD(); // this means "sleep one loop"
}
STATE_MACHINE_END();
Note that doing it this way means that sequences will generally always run through to the end before another sequence can begin, which might be a good or a bad thing, depending on what you prefer…
Even if I cycle the kill switch ? Obviously it is not what I want, as I would like to be able to launch the sequence as many time as I want, but just for the sake of theory & knowledge.
Not that I want to change the subject but don’t jetpacks only have one missile ?
I was pretty confused most of the day, but I think I understand now:
void PerformMissileLaunchSequence() {
STATE_MACHINE_BEGIN();
while (true) {
if (missilelaunch_) {
PVLOG_NORMAL << "Starting Missile Launch Sequence\n";
SaberBase::DoEffect(EFFECT_AIMING,0);
SLEEP(SaberBase::sound_length * 1000 + 10);
SaberBase::DoEffect(EFFECT_TARGETTING,0);
SLEEP(SaberBase::sound_length * 1000);
SaberBase::DoEffect(EFFECT_MISSILELAUNCH,0);
SLEEP(SaberBase::sound_length * 1000);
SaberBase::DoEffect(EFFECT_MISSILEGOESBOOM,0);
SLEEP(SaberBase::sound_length * 1000 + 10);
if (!flight_) { // Perform "Mando Talk" if not in flight mode!
SaberBase::DoEffect(EFFECT_MANDOTALK,0);
SLEEP(SaberBase::sound_length * 1000 + 10);
PVLOG_NORMAL << "Mando: Nice shot!\nBoba: I was aiming for the other one!\n";
}
SaberBase::DoEffect(EFFECT_DISARM,0);
SLEEP(SaberBase::sound_length * 1000);
PVLOG_NORMAL << "Missile Launch Sequence Completed!\n";
missilelaunch_ = false;
}
YIELD(); // this means "sleep one loop"
}
STATE_MACHINE_END();
}
void Loop() override {
// Perform the missile launch sequence
PerformMissileLaunchSequence();
// other stuff that goes on loop here
}
bool Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override {
switch (EVENTID(button, event, modifiers)) {
case EVENTID(stuff here ...):
more stuff here;
return true;
more case ...
case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_OFF):
case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON):
missilelaunch_ = true;
return true;
the rest of the cases ...
} // switch (EVENTID)
return false; // No action
} // Event2
Like that State_Machine will keep on running “all the time” but the sequence will only run if missilelaunch_ is true. Is this correct?
Do I still need to add: state_machine_.reset_state_machine() where I set missilelaunch_ to true?
And if I understand correctly, I don’t need to add state_machine_.stop() in the class constructor, correct?
If I wanted to “abort” the sequence, would this work (assuming I got my EVENTID’s correct):
case EVENTID(BUTTON_AUX2, EVENT_FIRST_CLICK_LONG, MODE_OFF):
case EVENTID(BUTTON_AUX, EVENT_FIRST_CLICK_LONG, MODE_OFF):
// (Stop missile sequence, maybe?)
missilelaunch_ = false;
SaberBase::DoEffect(disarm);
return true;
Thank you for your continued information and support.
No, but you could.
Doing that would interrupt whatever the state machine is doing and start it over.
Some care would have to be taken to make sure that doesn’t cause weird things. For instance, setting missilelaunch_ to false should happen right after if (missilelaunch_), or resetting might cause multiple launches…
Yeah, that was only for the case where we eliminated the boolean variables in favor of starting and stopping the state machine instead.
Your snippet of code would not abort the sequence.
You would either need to add code for that inside the state machine (an if after each sleep) or, you would need to use state_machine_.reset_state_machine() to make it stop what it’s doing.
It’s also possible to create an improved SLEEP that aborts immediately if some condition is true.
This would work.
However, it depends on how you actually want it to work.
When you do this:
It will abort any missile launch (or any other sequence the state machine is doing) and start the state machine over from the top, which will start the missile sequence immediately. even if some sounds are still playing from the aborted sequence.
Then there is this:
This will abort the sequence. It will also stop the state machine. Note that stopping the state machine doesn’t save any CPU, it just puts it in an infinite while(true) YIELD(); loop. Doing this means that you have to reset the state machine to start new sequences.
I think it might be better to do this instead:
missilelaunch_ = false;
state_machine_.reset();
This means that the sequence is aborted, and the state machine starts over from the top. This means that the state machine is running, and ready to handle new sequences, and it means that you can choose whether you want to reset the state machine or not when you start a new sequence. If you don’t reset (just set missilelaunch_=true) sequences will not be aborted, instead sequences will either be ignored (because they are already running) or they will start after the current running sequence. (If you have multiple, different types of sequences.)
As much as I like the idea of ALT_SOUND, I dislike very much the idea that my different “engine_sounds.wav” would need to be named “inxx.wav”, “outxx.wav” & “humxx.wav”.
That being said, I would like to know more about EFFECT2(hum, hum); &
#define EFFECT2(X, Y) Effect SFX_##X(#X, &SFX_##Y)
So if X = Y, X will loop to itself.
Would it be crazy to imagine EFFECT3(X, Y, Z) where if Y = Z, X would play and seemlessly play Y looping to Y ? #define EFFECT3(X, Y, Z) Effect SFX_##X(#X, &SFX_##Y, &SFX_##Z)
What is the command/syntax to start “playing” EFFECT2(X, Y) ? I know it’s not SaberBase::DoEffect2(X, Y) because that doesn’t exits. Over the past week, I did find SB_Effect2 but I am not sure how/if it ties in? I also found void RestartHum(...) but I was hoping to find a “start playing(hum)” function?
As always, any help always highly appreciated. Thank you for reading.
Generally speaking, when there are two use cases for the same code, it’s often very difficult to come up with good generic terms the the functions, methods, classes (or wav files) involved. I find that, when that happens, it’s better to just use the terms from the most prolific use case, and then explain in a comment or something how terms maps between use cases.
At first it can cause some cognitive dissonance, but it’s usually pretty easy to get used to.
Example: A blade in ProffieOS is really anything with lights on it. Or a motor. Or a servo… etc. If I had to come up with a generic term for that, it would be called an OutputDriver or something, which would require even more explanation I think…