VERC: Controllable Looping Last edited 2 years ago2022-09-29 07:54:19 UTC

In Yesukai's Looping Multimanager tutorial, Yesukai explained that a simple yet effective way to create a looping multi_manager. The nice thing about his method is that once turned on, you can just leave the multi_manager to do it's job. With some clever use of triggers, you could even control the progress of a level. Forcing the player to move quickly by destroying the level around him.

This method isn't without its flaws, however. What if you need to stop the multi_manager sequence? Ah, I hear you say, you just use a killtarget somewhere to do away with the multi_manager. Good suggestion, however, ultimately limited since that would totally obliterate any chance for you to re-trigger the multi_manager. What's needed is a relayed command with which you can check whether the multi_manager is allowed to continue or not. That and a keen sense of timing.

multi_manager Behavior

Before we get our hands dirty, I first want to explain a bit of theory about the multi_manager. You can totally disregard this section if you wish, but I firmly believe that one should at least know why a certain object works in a certain way to use it to its full advantage.

When a multi_manager is not multithreaded it will simply register itself as on and busy, so any trigger events directed to that multi_manager will be ignored (Hence the inability to turn it off). However, when it is mutlithreaded, it will create a clone of itself when triggered, and will do so for every subsequental trigger event.

Knowing this, it becomes quite clear why looping multi_managers work the way they do -- when not multithreaded, a multi_manager won't be able to trigger itself.

Another point I'd like to bring up is the multi_managers timescale. I found in some occasions a value of 0 (zero, immediate) gave some unexpected results and made timing a complicated sequence quite unclear. On the other end, a value of 1 second in game time is quite long. The minimum amount of time workable in such sequences was 0.001. That being the case one might as well get in the habit of using 0.001 as a minimum in the multi_managers timescale. This is, of course, personal preference.

Lastly it's good to observe that when adding a key to the multi_manager it'll transform all uppercase to lowercase letters. Make sure you don't include capitals in your naming-convention.

In regards to the issue at hand, we have established that we:
  1. Cannot tigger/shut off the multi_manager when it's active and not multithreaded
  2. Can only create another instance of the multi_manager when multithreaded.

States

The first logical step would be to create a trigger_relay. Let the multi_manager target the trigger_relay and vice-versa. But this only works for the looping, And once turned on it's impossible to shut it down without also removing one of the main entities.

What we need is a State function (State as in am I on? or am I off?). For this we use a combination: the env_global and the multisource.

The env_global, which we call mainloopglobal contains a variable which is either off or on. In this case we call it mainloopactive. Now there's several modes to set the env_global, but since we want to "toggle" things we set the Trigger Mode to Toggle. Because we want the loop to be inactive when we start the level, we set the Initial State to Off, after which we check the flag Set Initial State to activate the state as the level starts.

The multisource, which we call mainloopstate, is nothing more than a if-then gate. It checks wether its state master is on, in which case it will return yes to any query made. This particular multisource has the env_global's name as its Global State to Set, which of course is mainloopactive.

Looping

Okay, so now we have a "state" function we can check... then what? We should make sure the multi_manager, from now on mainloop, relays itself through a trigger entity. After some testing I found the func_button the most suitable since it has 'master', reset and render properties. The render properties we use to hide the button from player view for it will only be used by the multi_manager. We connect this func_button, which we aptly name mainloopgo, by it's "master" property with the multisource, mainloopstate and set its renderproperties to Texture/0. Its target, ofcourse to the multi_manager mainloop and its Delay Before Reset very low, like 0.01, since we might want to access this button mutiple times over a short period. Now, only thing to do now is to let the multi_manager mainloop target the new button as its last action and the looping is set.

On and On and On and On...

Ah, and now the moment where you have all been waiting for: Turning On And Off.

Of course, there's several ways to set the whole thing in motion. Most important is to FIRST set the env_global mainloopglobal to "On" after which you'd trigger the func_button mainloopgo. To turn it off again later just set the env_global mainloopglobal to "Off", and when the multi_manager mainloop tries to trigger the func_button mainloopgo it will not succeed, effectively ending the loop.

What I would generally suggest is to have two more func_buttons (loopon and loopoff) either target their own multi_manager (mainloopon and mainloopoff) which in turn trigger the env_global mainloopglobal after which only the multi_manager mainloopon has to trigger the func_button mainloopgo. This multi_manager relaying, especially that of the loopoff button, may seem a bit overdone, but I'd imagine you want to do a bit more with an on and off function than just loop something. (powerup/powerdown sequence, sounds, lights)

Wrapping it up

When you have implemented this into your map, you'll be able to toggle any series of sequences on and/or off. Pretty nifty, eh? Of course, there's some challenges left. Smart ones among you might have already noticed the obvious pitfall here: what if the player first uses the "Off" button? Why are there buttons? Shouldn't there be more? But I have to say... that's for another time. You've learned to switch a looping multi_manager off by sheer willpower using a creative entity setup. Now go forth and multi_manage.

Cray.

PS. Included for your pleasure is a simple map which shows the setup in a test-situation. Also a PDF with a flowchart of what is happening in said map.
The entity setup

1. buttonon. func_button.
target:(MM) mainloopon

2. buttonoff. func_button .
target:(MM) mainloopoff

3. mainloopon. multi_manager
not multithreaded
     (EG)mainloopglobal - 0.01     (set to "On")
     (FB)mainloopgo - 0.1

4. mainloopoff. multi_manager
not multithreaded
     (EG)mainloopglobal - 0.01     (reset to "Off")

5. mainloopgo. func_button
target: (MM)mainloop
master: (MS)mainloopstate     only trigger target when master = "On"
rendermode/fx: Texture/0     not an interface button, don't show
Delay before reset: 0.01

6. mainloop. multi_manager
not multithreaded
     well, here's where you put your loop
     end it with:
     (FB)mainloopgo - N

7. mainloopglobal. env_global
Global State To Set:     mainloopactive
Trigger Mode: Toggle
Initial state: Off
Set Initial State: flag=on

8. mainloopstate. multisource
Global State Master: mainloopactive
This article was originally published on Valve Editing Resource Collective (VERC).
The archived page is available here.
TWHL only publishes archived articles from defunct websites, or with permission. For more information on TWHL's archiving efforts, please visit the TWHL Archiving Project page.

Comments

You must log in to post a comment. You can login or register a new account.