In this tutorial we shall control the visibility of some
scene elements by scripts through their $visibility controller. Step by step, we
shall advance from a single element to a synchronized multi-component system.
The example problem from this tutorial is simple, but not trivial. We’ll make
a traffic light, then we’ll implement a timer for switching the lights on and
off automatically. Finally, we’ll synchronize the traffic lights in a 4-way
intersection!
Download the support file
(visibility.an8). You may use it for completing the tutorial, but it would
be better for you to enter the scripts yourself to your own model, rather than
to just watch how the ready made scripts perform!
I used the simplest possible modeling, as we are focused on scripting! The
lights of the traffic light are emissive cylinders (as different objects,
assembled in the scene with the body of the traffic light). To switch a light
on, we make its visibility controller 1. To switch it of, we make it 0.
Scene01 of the demo file contains a traffic light with only the yellow light
active. Let’s make it a pulsing light! On one frame the yellow will be on, on
the next frame will be off, then again on, then off… If we were to do it with
keys, we had to make a key frame for each frame in the scene, alternating 1 and
0 as values for the visibility key. We’ll do it with a script of a single
line! We are going to use the modulus operator ‘%’. This gives the rest of
the integer division of its left operand by its right operand (both must be
integers). For example, 7 % 5 evaluates to 2, because 7 divided by 5 is 1, with
rest 2. Similarly, 7 % 3 is 1, because 7 divided by 3 is 2 and the rest is 1. If
a number divides the other exactly (there is no rest), then the ‘%’ operator
will yield zero as result. When the right operand of the modulus operator is 2,
there are only two possible results: 0 if the left operand is an even number, or
1 if it’s an odd number.
We also need the predefined ‘frame’ variable. This variable has the value of
the current frame number and is an integer variable (declared inside Anim8or).
Now we have everything needed to write the script. You may model a much better
traffic light, or use the one in scene01 of the demo file. Double-click the
yellow light element (named “TL1_yellow” in the demo file), click the
“…” button at the right of the “Visibility” check box, then click
“Edit” to enter the script editor (If you are using Terranim8or, select the
yellow light element and click Edit > Controller Script > Visibility).
Enter for our controller script the following line:
| $visibility = frame % 2; |
The result of the expression ‘frame % 2’ is either 0,
or 1, depending on the current frame being even, or odd.
Play the animation step by step (with the arrow keys)! Now, if we want to have
the light on for even frames (and off on odd frames), all we have to do is
adding 1 to the right operand of the modulus operator. Modify the script to:
| $visibility = (frame + 1) % 2; |
Now, if frame is even, frame + 1 will be odd and vice versa.
In scene02 there is a more functional traffic light, with
red-yellow-green all active. Let’s make it work like this:
- A predefined number of frames the red light stays on, alone.
- Then, for a shorter number of frames, let’s also have the yellow light on,
together with the red. This prepares the drivers for starting on the green that
follows.
- Then, a predefined number of frames let’s have the green light on, alone
This is a cycle that should repeat indefinitely. I decided to use the following
(arbitrary) values in the demo file: 5 frames red light alone, 1 frame red +
yellow, 5 frames green alone. This makes up to a cycle of 11 frames. Once we
agreed on this rule, let’s implement it!
We notice that for each cycle the lights should behave identically, or in other
words, things have to repeat on the same frame of different cycles! This
suggests to use the modulus operator with the expression ‘frame % $cycle’,
where $cycle should be an integer variable initialized with our value for the
cycle length (11). On the first frame of a cycle, the expression ‘frame %
$cycle’ evaluates to 0, on the second frame to 1, on the last frame of the
cycle evaluates to $cycle – 1 (that is 10). Now, let’s review our rules:
- The red light is on for the first 6 frames (first 5
frames alone, on the 6th frame together with the yellow)
- The yellow light is on only on the sixth frame of a cycle
- The green light is on beginning with the 7th frame of a cycle
The last thing to note is that the frames of a cycle begin
with 0, which is the first frame. The sixth frame is therefore frame 5, the 7th
frame is frame 6, etc..
Now, we rephrase our rules, to be even closer to coding:
- Red light (element ”L1_red” from scene02): If the
frame of the cycle is less, or equal to 5, then make the red light’s
visibility 1. Else, make it 0. That is almost a code sequence! We may write the
red light’s visibility script like this:
| int $cycle; /*
Declare a variable that will hold the frame length of a cycle */ $cycle = 11; /* Initialize it to 11 */ if(frame%$cycle <= 5) /* If the frame of the cycle is less, or equal to 5, */ $visibility = 1; /* then make the visibility 1 */ else $visibility = 0; /* Else, make it 0 */ |
- Yellow light (element ”L1_yellow” from scene02): If
the frame of the cycle equals 5, then make the yellow light’s visibility 1.
Else, make it 0. We code it like this:
| int $cycle; /* Declare a variable that will hold the
frame length of a cycle */ $cycle = 11; /* Initialize it to 11 */ if(frame%$cycle == 5) /* If the frame of the cycle equals 5 (note the use of ‘==’ comparing for equality), */ $visibility = 1; /* then make the visibility 1 */ else $visibility = 0; /* Else, make it 0 */ |
- Green light (element ”L1_green” from scene02): If the frame of the cycle is greater than 5, then make the green light’s visibility 1. Else, make it 0. The corresponding code is:
| int $cycle; /* Declare a variable that will hold the
frame length of a cycle */ $cycle = 11; /* Initialize it to 11 */ if(frame%$cycle > 5) /* If the frame of the cycle is greater than 5, */ $visibility = 1; /* then make the visibility 1 */ else $visibility = 0; /* Else, make it 0 */ |
Play the animation step by step. Now, we are ready to equip
a crossing with traffic lights!
Scene03 of the demo file shows a road crossing with four
traffic lights. Use camera view for a better sight! I deliberately made the
traffic lights visible from their both sides.
The idea is to have a master light (element “TL1”) which controls all the
other traffic lights in the crossing. This is the traffic light we just set up
in scene02. Our mission is to set up the other three traffic lights to be
synchronized with the master light. We could repeat the same algorithm as for
TL1 delayed, but that would be boring and we wouldn’t learn new things!
Element “TL2” controls the traffic on the perpendicular direction relative to “TL1”. In order to find out the rules needed to operate this traffic light, let’s make a table showing how should be the state of the lights on TL1 and TL2 in the same time:
|
Case |
|
TL2 |
|
|
TL1 |
|
||
|
1 |
|
|
|
|
|
|
||
|
2 |
|
|
|
|
|
|
||
|
3 |
|
|
|
|
|
|
||
|
4 |
|
|
|
|
|
|
This table shows that for cases 1 and 2, one knows precisely the state of lights on TL2 without seeing them, only seeing TL1. For cases 3 and 4, the state of lights on TL1 is the same, so there is no way of knowing the state of TL2 only from seeing TL1! That means that we cannot set up TL2 using only the values of TL1, we need one more clue to decide between case 3 and case 4. The extra information is the fact that the yellow light on TL2 is ‘on’ only for the first frame of a cycle (when the master light starts its red phase). Now we can state the rules for TL2:
- The yellow is on only on the first frame of a cycle
- The red light is on when TL1 has yellow on
- The red light is also on when TL1’s green is on
- The red light is on also when the yellow of TL2 is on!
- Green light is on when TL2 red is off.
The code for the yellow light (element “TL2_yellow”) is straight-forward now, when we know how to use the modulus operator.
| int $cycle; $cycle = 11; $visibility = !(frame%$cycle); |
We could have written
| if(frame%$cycle == 0) $visibility = 1; else $visibility = 0; |
but $visibility = !(frame%$cycle); makes the same thing and
is simpler. The NOT operator ‘!’ reverses 0 and 1. On the first frame of a
cycle frame%$cycle evaluates to 0. Prefacing it with ‘!’, becomes 1. So, on
the first frame of a cycle the yellow light of TL2 is on. On all other frames,
frame%$cycle has a nonzero value, which prefaced by ‘!’ makes it zero. The
yellow light is switched off on any frame of a cycle but the first!
To set the controller for the red light, we have to know the values of other elements’ visibility controllers. The GetAttribute functions are meant to do this kind of job. We’ll need the values of the visibility controllers of elements “TL1_green”, "TL1_yellow” and “TL2_yellow” For the first element, we’ll use GetAttributeInt("TL1_green", "visibility") function. The script will look like this:
| int $TL1_green, $TL1_yellow, $TL2_yellow;
/* we declare three integer variables to keep the values for the
named elements’ visibility controllers */ $TL1_green = GetAttributeInt("TL1_green", "visibility"); /* Get the controller’s value */ $TL1_yellow = GetAttributeInt("TL1_yellow", "visibility"); /* Get the ctrl. value */ $TL2_yellow = GetAttributeInt("TL2_yellow", "visibility"); /* Get the ctrl. value */ $visibility = $TL1_green || $TL1_yellow || $TL2_yellow; /* Logical OR */ |
The last line sets the value of the controller, making logical OR operation with all the variables assigned above. The result of the OR operation is zero if all the operands are zero. If any of the operands is nonzero, the result is 1. That means that the red light is on if any of the mentioned element’s visibility is 1.
The green light’s controller should be the opposite of the red light’s visibility controller. Knowing that the NOT operator makes the opposite of a logical value, we can write the script like below:
| int $TL2_red; /* Will hold the value of the red
light’s visibility controller */ $TL2_red = GetAttributeInt("TL2_red", "visibility"); /* Get the controller’s value */ $visibility = !$TL2_red; /* Negate the value of the red light’s visibility */ |
The other traffic lights will be set to just copy the switching state of TL1, or TL2. TL3 copies TL1’s values:
| TL3_red element’s script will be: $visibility =GetAttributeInt("TL1_red",
"visibility"); TL3_yellow element’s script: $visibility =GetAttributeInt("TL1_yellow", "visibility"); TL3_green element’s script: $visibility =GetAttributeInt("TL1_green", "visibility"); |
TL4 copies TL2 in exactly the same manner. Set it yourself and play the animation step by step. Enjoy!