Difference between revisions of "Deep Buffering"
Jesuszilla (talk | contribs) (Added readability help) |
(→States) |
||
(11 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
=Introduction= | =Introduction= | ||
Deep Buffering is a slightly more advanced, more fine-grained approach to handling [[command buffering]] in the M.U.G.E.N engine co-developed by Vans and Jesuszilla. It is a direct descendant of the [[Tiny Buffering]] system by Vans. What makes Deep Buffering unique is that it completely bypasses M.U.G.E.N's command sequence interpreter in favor of its own implementation through use of bitwise arithmetic. It is based on Capcom vs. SNK 2's command buffering and interpreter. | Deep Buffering (originally known as "Buffering Step 2") is a slightly more advanced, more fine-grained approach to handling [[command buffering]] in the M.U.G.E.N engine co-developed by Vans and Jesuszilla. It is a direct descendant of the [[Tiny Buffering]] system by Vans. What makes Deep Buffering unique is that it completely bypasses M.U.G.E.N's command sequence interpreter in favor of its own implementation through use of bitwise arithmetic. It is based on Capcom vs. SNK 2's command buffering and interpreter. | ||
=Differences from Tiny Buffering= | =Differences from Tiny Buffering= | ||
Line 15: | Line 15: | ||
<ul> | <ul> | ||
<li>Press - The initial state. The corresponding bit is set to 1 as soon as M.U.G.E.N interprets the press for the button. This uses the button press | <li>Press - The initial state. The corresponding bit is set to 1 as soon as M.U.G.E.N interprets the press for the button. This uses the button hold directly provided by M.U.G.E.N. and the direction hold/press directly provided by M.U.G.E.N.</li> | ||
<li>Hold - Calculated at the same time as the initial press. This is determined by taking the bitwise OR of the last command in the Hold variable after its elements have been shifted by 1 tick (4 bits right for directions, and 7 bits right for buttons) and the current Hold state. This also uses the button press/hold directly provided by M.U.G.E.N.</li> | <li>Hold - Calculated at the same time as the initial press. This is determined by taking the bitwise OR of the last command in the Hold variable after its elements have been shifted by 1 tick (4 bits right for directions, and 7 bits right for buttons) and the current Hold state. This also uses the button press/hold directly provided by M.U.G.E.N.</li> | ||
<li>Release - Calculated 1 tick later than the last hold. This is calculated by checking the bits of the last command in the Hold variable after its elements have been shifted by 1 tick (4 bits right for directions, and 7 bits right for buttons) and the current Hold state.</li> | <li>Release - Calculated 1 tick later than the last hold. This is calculated by checking the bits of the last command in the Hold variable after its elements have been shifted by 1 tick (4 bits right for directions, and 7 bits right for buttons) and the current Hold state.</li> | ||
Line 103: | Line 103: | ||
Check if any of the inputs in any of the variables are active for the current sequence in the full command, then: | Check if any of the inputs in any of the variables are active for the current sequence in the full command, then: | ||
<ul> | <ul> | ||
<li>If it's active, | <li>If it's active, set the upper 28 bits to the new sequence number, and set the lower 4 bits to <math>t_e</math>.</li> | ||
<li>If the time has expired for the current sequence (all the first 4 bits are 0), then reset the entire command check to 0.</li> | <li>If the time has expired for the current sequence (all the first 4 bits are 0), then reset the entire command check to 0.</li> | ||
</ul> | </ul> | ||
</li> | </li> | ||
<li>Check for whether or not the | <li>Check for whether or not the sequence number matches the final element number. If it does, then the command is complete.</li> | ||
</ol> | </ol> | ||
Line 123: | Line 123: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
With that said, it is possible in this system to store and interpret any command sequence with | With that said, it is possible in this system to store and interpret any command sequence with 268,435,455 or fewer elements with each up to 15 ticks of buffer time. | ||
The sequence for interpreting a Hadouken (<code>/D,DF,F,x</code>) could go as follows, if the buffering times for buttons and directions are both 2 (<math>t_b = 2</math> and <math>t_d = 2</math>) and the element buffering time is 9 (<math>t_e = 9</math>). All bits go from least significant bit on the left to most significant bit on the right. Thus, the first bit is 1, the second is 2, the third is 4, etc. | The sequence for interpreting a Hadouken (<code>/D,DF,F,x</code>) could go as follows, if the buffering times for buttons and directions are both 2 (<math>t_b = 2</math> and <math>t_d = 2</math>) and the element buffering time is 9 (<math>t_e = 9</math>). All bits go from least significant bit on the left to most significant bit on the right. Thus, the first bit is 1, the second is 2, the third is 4, etc. | ||
Line 238: | Line 238: | ||
Var(20) (Check): | Var(20) (Check): | ||
1001 | 1001 1100000000000000000000000000 ; Reset to 9, next element check (command considered complete) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 257: | Line 257: | ||
Var(20) (Check): | Var(20) (Check): | ||
0001 | 0001 1100000000000000000000000000 ; Still counting down | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 276: | Line 276: | ||
Var(20) (Check): | Var(20) (Check): | ||
1110 | 1110 1100000000000000000000000000 ; Still counting down | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 283: | Line 283: | ||
The logic for the Hadouken example (helper side) can thus be constructed as follows: | The logic for the Hadouken example (helper side) can thus be constructed as follows: | ||
<syntaxhighlight lang="INI"> | <syntaxhighlight lang="INI"> | ||
[State | [State 10372, QCF Init] | ||
type = Null | type = Null | ||
trigger1 = !Var(20) && ((var(4)&240) = 32 || (var(4)&15) = 2) | trigger1 = !Var(20) && ((var(4)&240) = 32 || (var(4)&15) = 2) | ||
trigger1 = e||(var(20) := 9 + (2**4)) | trigger1 = e||(var(20) := 9 + (2**4)*1) | ||
trigger2 = (Var(20) | trigger2 = (Var(20)/(2**4)) = 1 && ((var(3)&240) = 160 || (var(3)&15) = 10) | ||
trigger2 = e||(var(20) := 9 + (2** | trigger2 = e||(var(20) := 9 + (2**4)*2) | ||
trigger3 = (Var(20) | trigger3 = (Var(20)/(2**4)) = 2 && ((var(3)&240) = 128 || (var(3)&15) = 8) | ||
trigger3 = e||(var(20) := 9 + (2** | trigger3 = e||(var(20) := 9 + (2**4)*3) | ||
trigger4 = Var(20) && (Var(20)&15) = 0 | trigger4 = Var(20) && (Var(20)&15) = 0 | ||
Line 301: | Line 301: | ||
... and this can be used along with the following trigger: | ... and this can be used along with the following trigger: | ||
<syntaxhighlight lang="INI"> | <syntaxhighlight lang="INI"> | ||
triggerall = (helper( | triggerall = (helper(10372), Var(20)/(2**4)) = 3 | ||
triggerall = (helper( | triggerall = (helper(10372), var(0)&129)) > 0 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 311: | Line 311: | ||
value = 1000 | value = 1000 | ||
triggerall = !AILevel | triggerall = !AILevel | ||
triggerall = numHelper( | triggerall = numHelper(10372) | ||
triggerall = (helper( | triggerall = (helper(10372), Var(20)/(2**4)) = 3 ; Command detect | ||
triggerall = (helper( | triggerall = (helper(10372), Var(0)&16383) > 0 || ((helper(10372), Var(2)&16383) > 0 && !Var(30)) ; Button detect (x,y,z,~x,~y, or ~z) | ||
triggerall = statetype != A | triggerall = statetype != A | ||
triggerall = roundstate = 2 | triggerall = roundstate = 2 | ||
Line 337: | Line 337: | ||
Another disadvantage is that some games (particularly older ones) may not use this algorithm for interpreting commands, opting instead to use a full window check. For such use cases, [[Tiny Buffering]] is recommended. | Another disadvantage is that some games (particularly older ones) may not use this algorithm for interpreting commands, opting instead to use a full window check. For such use cases, [[Tiny Buffering]] is recommended. | ||
Because only cardinal directions and buttons need to be checked, only the most basic commands need to be defined in the .CMD, making Deep Buffering a prime candidate for use in a [[tag]] system. | |||
Like all buffering helpers, this trivializes moves that require a target to go back to their own set of states when they press a button, such as Felicia's Cat Ride. | |||
==Simplifying Triggers== | ==Simplifying Triggers== | ||
By using one extra variable, readability of characters that utilize Deep Buffering can be improved. The idea is to store each command as its own bit in a separate variable, that way only one single variable needs to be checked for the command itself in the .CMD. Bitwise operators are still required, but access is much simpler than creating large masks of arbitrary command lengths. The following example is taken from Jesuszilla's | By using one extra variable, readability of characters that utilize Deep Buffering can be improved. The idea is to store each command as its own bit in a separate variable, that way only one single variable needs to be checked for the command itself in the .CMD. Bitwise operators are still required, but access is much simpler than creating large masks of arbitrary command lengths. The following example is taken from Jesuszilla's CvS2 Eagle buffering.vns: | ||
<syntaxhighlight lang="INI"> | <syntaxhighlight lang="INI"> | ||
[State | [State 10372, Command Active] | ||
type = Null | type = Null | ||
trigger1 = e||(var(59) := 0) ; INIT | trigger1 = e||(var(59) := 0) ; INIT | ||
trigger1 = e||(var(59) := (var(59)|((2** | trigger1 = e||(var(59) := (var(59)|((2**29)*((Var(54)/(2**4)) = 1)))) ; DU | ||
trigger1 = e||(var(59) := (var(59)|((2**30)*((Var(55)/(2**4)) = 2)))) ; FF | |||
trigger1 = e||(var(59) := (var(59)|((2** | trigger1 = e||(var(59) := (var(59)|(-2147483648*((Var(56)/(2**4)) = 2)))) ; BB | ||
trigger1 = e||(var(59) := (var(59)|( | trigger1 = !root, numProjID(131035) ; Chizuru seal | ||
trigger1 = | trigger1 = e||(var(59) := (var(59)|((2**0)*((Var(20)/(2**4)) = 3)))) ; QCF | ||
trigger1 = e||(var(59) := (var(59)|((2** | trigger1 = e||(var(59) := (var(59)|((2**1)*((Var(21)/(2**4)) = 3)))) ; QCB | ||
trigger1 = e||(var(59) := (var(59)|((2** | trigger1 = e||(var(59) := (var(59)|((2**2)*((Var(22)/(2**4)) = 3)))) ; DP | ||
trigger1 = e||(var(59) := (var(59)|((2** | trigger1 = e||(var(59) := (var(59)|((2**3)*((Var(23)/(2**4)) = 3)))) ; rDP | ||
trigger1 = e||(var(59) := (var(59)|((2** | trigger1 = e||(var(59) := (var(59)|((2**4)*((Var(24)/(2**4)) = 5)))) ; HCB | ||
trigger1 = e||(var(59) := (var(59)|((2** | trigger1 = e||(var(59) := (var(59)|((2**5)*((Var(25)/(2**4)) = 5)))) ; HCF | ||
trigger1 = e||(var(59) := (var(59)|((2**6)*((Var(40)/(2**4)) = 5)))) ; QCFx2 | |||
trigger1 = e||(var(59) := (var(59)|((2** | trigger1 = e||(var(59) := (var(59)|((2**7)*((Var(41)/(2**4)) = 5)))) ; QCBx2 | ||
trigger1 = e||(var(59) := (var(59)|( | |||
trigger1 = e||(var(59) := (var(59)|( | |||
ignorehitpause = 1 | ignorehitpause = 1 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 364: | Line 366: | ||
Each special command is given its own bit and reversed as necessary to indicate flipped commands. The QCF command can be checked with one simple trigger with this: | Each special command is given its own bit and reversed as necessary to indicate flipped commands. The QCF command can be checked with one simple trigger with this: | ||
<syntaxhighlight lang="INI"> | <syntaxhighlight lang="INI"> | ||
triggerall = (helper( | triggerall = (helper(10372), Var(59)&(2**0)) > 0 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
As usual, 32 bits can be utilized, and thus, up to 32 unique commands can be detected with one variable. This is an addition to Deep Buffering, not a complete replacement, so the base system is still required for this to function. | As usual, 32 bits can be utilized, and thus, up to 32 unique commands can be detected with one variable. This is an addition to Deep Buffering, not a complete replacement, so the base system is still required for this to function. | ||
This is completely optional, but is recommended for Street Fighter II characters as the original game uses something similar to indicate and randomize specials. | |||
=Commerical Games= | =Commerical Games= | ||
Deep Buffering is heavily inspired by Capcom vs. SNK 2's command interpreter and buffer, and in fact, uses the exact same algorithm to perform its calculations. Given the industry standard of reusing code, many other games utilize this same method. | Deep Buffering is heavily inspired by Capcom vs. SNK 2's command interpreter and buffer, and in fact, uses the exact same algorithm to perform its calculations. Given the industry standard of reusing code, many other games utilize this same method. The history of this implementation goes as far back as Street Fighter II: The World Warrior. | ||
<ul> | <ul> | ||
<li>Vampire | <li>Street Fighter II Series</li> | ||
<li>Street Fighter Zero | <li>Vampire Series</li> | ||
<li> | <li>Street Fighter Zero Series</li> | ||
<li>Capcom vs. SNK | <li>Marvel Series</li> | ||
<li>Street Fighter III Series</li> | |||
<li>Capcom vs. SNK Series</li> | |||
<li>Capcom Fighting Jam</li> | <li>Capcom Fighting Jam</li> | ||
</ul> | </ul> |
Latest revision as of 21:07, 7 August 2021
Introduction[edit]
Deep Buffering (originally known as "Buffering Step 2") is a slightly more advanced, more fine-grained approach to handling command buffering in the M.U.G.E.N engine co-developed by Vans and Jesuszilla. It is a direct descendant of the Tiny Buffering system by Vans. What makes Deep Buffering unique is that it completely bypasses M.U.G.E.N's command sequence interpreter in favor of its own implementation through use of bitwise arithmetic. It is based on Capcom vs. SNK 2's command buffering and interpreter.
Differences from Tiny Buffering[edit]
The main difference between Deep Buffering and Tiny Buffering is that Deep Buffering assigns an internal timer to each cardinal direction or button rather than one for the entire command. This means that the entire input window for the command is extended with each button or direction that is added to the sequence. This is accomplished by interpreting only cardinal directions and button presses individually, rather than relying on M.U.G.E.N's command interpreter to indicate when the command sequence has been completed. For that reason, Deep Buffering can be seen as more of a complete command interpreter rather than strictly a buffering implementation.
Structure[edit]
There are three variables for both directions and buttons, adding up to a total of 6 variables to indicate the various states for each of the two "primitives": Press, Release, and Hold.
Primitives[edit]
In the Deep Buffering system, directions and buttons are separated and considered their own "primitives." Due to lack of analog support in M.U.G.E.N (and 2D fighters in general), each button and direction can be implemented as one bit indicating whether the input is on or off. This makes for a compact, memory efficient and logically efficient system.
States[edit]
There are three states for each primitive: Press, Hold, and Release. Each state is calculated as follows:
- Press - The initial state. The corresponding bit is set to 1 as soon as M.U.G.E.N interprets the press for the button. This uses the button hold directly provided by M.U.G.E.N. and the direction hold/press directly provided by M.U.G.E.N.
- Hold - Calculated at the same time as the initial press. This is determined by taking the bitwise OR of the last command in the Hold variable after its elements have been shifted by 1 tick (4 bits right for directions, and 7 bits right for buttons) and the current Hold state. This also uses the button press/hold directly provided by M.U.G.E.N.
- Release - Calculated 1 tick later than the last hold. This is calculated by checking the bits of the last command in the Hold variable after its elements have been shifted by 1 tick (4 bits right for directions, and 7 bits right for buttons) and the current Hold state.
As an example, consider the following scenario of a player pressing the a
button. Contrary to the structure and calculations, Hold is actually calculated first.
Assumptions:
- Button buffering time is 2 ticks.
- Direction buffering time is 2 ticks.
Tick 1: No input Tick 2 Tick 1 Var(1) - 0000000 0000000 ; Hold Var(0) - 0000000 0000000 ; Press Var(2) - 0000000 0000000 ; Release
Tick 2: User presses 'a' Tick 2 Tick 1 Var(1) - 0000000 0000100 ; Hold Var(0) - 0000000 0000100 ; Press Var(2) - 0000000 0000000 ; Release
Tick 3: No input Tick 2 Tick 1 Var(1) - 0000100 0000000 ; Hold Var(0) - 0000100 0000000 ; Press Var(2) - 0000000 0000100 ; Release
Note that the XOR for the Release is only calculated for the upper 7 bits, which corresponds to the current state of the command. This is done so that it doesn't interfere with the lower bits, which only store the previous state exactly as it was the tick before.
Buttons[edit]
Vars 0-2 are used for button states. Var(0) is used to indicate the initial press, Var(1) is used to indicate hold, and Var(2) is used to indicate release.
The binary structure for the button variables is as follows:
Button: xyzsabc Bits: 0000000 Bit #: 1234567
Due to the storage mechanism, it is only possible in this system to hold up to 4 ticks of buffering data for all buttons.
Directions[edit]
Vars 3-5 are used for direction states. Var(3) is used to indicate the initial press, Var(4) is used to indicate hold, and Var(5) is used to indicate release.
The binary structure for the direction variables is as follows:
Button: UDBF Bits: 0000 Bit #: 1234
Due to the storage mechanism, it is only possible in this system to hold up to 8 ticks of buffering data for all directions.
Algorithm[edit]
The algorithm involved in interpreting a command is very straightforward and uses only bitwise operations (or approximations of them, due to lack of bitwise shift operators in the CNS language).
- Let represent the number of ticks to buffer buttons.
- Let represent the number of ticks to buffer directions.
- Let represent the number of ticks to buffer each element.
The basic algorithm operates every tick as follows:
- Store the current hold state in the upper bits of the hold variables.
- Store the current press state in the upper bits of the press variables.
- Compare the lower bits of the hold variable with the upper bits in the release variable to get the current release state. A button is considered to be released when the uppermost bits corresponding to the command are 0 bits, and any of the lower bits are non-zero.
- Divide every button variable by to shift the bits down by 7 bits.
- Divide every direction variable by to shift the bits down by 4 bits.
-
Check if any of the inputs in any of the variables are active for the current sequence in the full command, then:
- If it's active, set the upper 28 bits to the new sequence number, and set the lower 4 bits to .
- If the time has expired for the current sequence (all the first 4 bits are 0), then reset the entire command check to 0.
- Check for whether or not the sequence number matches the final element number. If it does, then the command is complete.
Command Interpretation[edit]
An element of a command is defined as an individual component normally separated by commas. in /D,DF,F+x
, there are 3 elements: /D
, DF
, and F+x
. Conversely, /D,DF,F,x
contains 4 elements
Each command sequence is stored and checked in its own variable with the first 4 bits reserved for , which can store an element for up to 15 ticks. The binary structure of a command is as follows:
T: Element buffer time (t_e) E: Element to check TTTTEEEEEEEEEEEEEEEEEEEEEEEEEEEE 00000000000000000000000000000000
With that said, it is possible in this system to store and interpret any command sequence with 268,435,455 or fewer elements with each up to 15 ticks of buffer time.
The sequence for interpreting a Hadouken (/D,DF,F,x
) could go as follows, if the buffering times for buttons and directions are both 2 ( and ) and the element buffering time is 9 (). All bits go from least significant bit on the left to most significant bit on the right. Thus, the first bit is 1, the second is 2, the third is 4, etc.
Tick 1: No input Var(0) (Button Press): 0000000 0000000 Var(1) (Button Hold): 0000000 0000000 Var(2) (Button Release): 0000000 0000000 Var(3) (Direction Press): 0000 0000 Var(4) (Direction Hold): 0000 0000 Var(5) (Direction Release): 0000 0000 Var(20) (Check): 0000 0000000000000000000000000000
Tick 2: User inputs D Var(0) (Button Press): 0000000 0000000 Var(1) (Button Hold): 0000000 0000000 Var(2) (Button Release): 0000000 0000000 Var(3) (Direction Press): 0000 0100 Var(4) (Direction Hold): 0000 0100 Var(5) (Direction Release): 0000 0000 Var(20) (Check): 1001 1000000000000000000000000000
Tick 3: No input Var(0) (Button Press): 0000000 0000000 Var(1) (Button Hold): 0000000 0000000 Var(2) (Button Release): 0000000 0000000 Var(3) (Direction Press): 0100 0000 Var(4) (Direction Hold): 0100 0000 Var(5) (Direction Release): 0000 0100 Var(20) (Check): 0001 1000000000000000000000000000
Tick 4: No input Var(0) (Button Press): 0000000 0000000 Var(1) (Button Hold): 0000000 0000000 Var(2) (Button Release): 0000000 0000000 Var(3) (Direction Press): 0000 0000 Var(4) (Direction Hold): 0000 0000 Var(5) (Direction Release): 0100 0000 Var(20) (Check): 1110 1000000000000000000000000000
Tick 5: User inputs DF Var(0) (Button Press): 0000000 0000000 Var(1) (Button Hold): 0000000 0000000 Var(2) (Button Release): 0000000 0000000 Var(3) (Direction Press): 0000 0101 Var(4) (Direction Hold): 0000 0101 Var(5) (Direction Release): 0000 0000 Var(20) (Check): 1001 0100000000000000000000000000 ; Reset to 9, next element check
Tick 6: User inputs F Var(0) (Button Press): 0000000 0000000 Var(1) (Button Hold): 0000000 0000000 Var(2) (Button Release): 0000000 0000000 Var(3) (Direction Press): 0101 0001 Var(4) (Direction Hold): 0101 0001 Var(5) (Direction Release): 0000 0100 Var(20) (Check): 1001 1100000000000000000000000000 ; Reset to 9, next element check (command considered complete)
Tick 7: User inputs x Var(0) (Button Press): 0000000 1000000 Var(1) (Button Hold): 0000000 1000000 Var(2) (Button Release): 0000000 0000000 Var(3) (Direction Press): 0001 0000 Var(4) (Direction Hold): 0001 0000 Var(5) (Direction Release): 0100 0000 Var(20) (Check): 0001 1100000000000000000000000000 ; Still counting down
Tick 8: No input Var(0) (Button Press): 1000000 0000000 Var(1) (Button Hold): 1000000 0000000 Var(2) (Button Release): 0000000 1000000 Var(3) (Direction Press): 0000 0000 Var(4) (Direction Hold): 0000 0000 Var(5) (Direction Release): 0000 0000 Var(20) (Check): 1110 1100000000000000000000000000 ; Still counting down
Note how in this system, the last press is not interpreted by the system itself. This is so individual buttons can still buffer as usual without any special cases given to the last possible command. The final input in the sequence is used as a trigger to perform the next action (stage change, animation change, etc.) in conjunction with the command check variable. This means that the base command need only be defined once, with the only necessary change being the trigger to detect the final input. This allows motions to be reused and interpreted as one, which means fewer variables are necessary to interpret each command.
The logic for the Hadouken example (helper side) can thus be constructed as follows:
[State 10372, QCF Init] type = Null trigger1 = !Var(20) && ((var(4)&240) = 32 || (var(4)&15) = 2) trigger1 = e||(var(20) := 9 + (2**4)*1) trigger2 = (Var(20)/(2**4)) = 1 && ((var(3)&240) = 160 || (var(3)&15) = 10) trigger2 = e||(var(20) := 9 + (2**4)*2) trigger3 = (Var(20)/(2**4)) = 2 && ((var(3)&240) = 128 || (var(3)&15) = 8) trigger3 = e||(var(20) := 9 + (2**4)*3) trigger4 = Var(20) && (Var(20)&15) = 0 trigger4 = e||(var(20) := 0) ignorehitpause = 1
... and this can be used along with the following trigger:
triggerall = (helper(10372), Var(20)/(2**4)) = 3 triggerall = (helper(10372), var(0)&129)) > 0
... to detect the whole command. In most cases, this detection would be done in the .CMD for the ChangeState:
[State -1, Hadouken] type = ChangeState value = 1000 triggerall = !AILevel triggerall = numHelper(10372) triggerall = (helper(10372), Var(20)/(2**4)) = 3 ; Command detect triggerall = (helper(10372), Var(0)&16383) > 0 || ((helper(10372), Var(2)&16383) > 0 && !Var(30)) ; Button detect (x,y,z,~x,~y, or ~z) triggerall = statetype != A triggerall = roundstate = 2 trigger1 = ctrl || (StateNo = 100 && AnimElemTime(2) >1) || StateNo = 101 || StateNo = 40 || (StateNo = 52 && Anim = 47 && Time >= 2) ignorehitpause = 0
Note how the second triggerall
was changed from var(0)&129
to var(0)&16383
. This was done in order to show how bit masks can be utilized to check for any of x,y,z,~x,~y,~z
rather than checking each one individually.
Bitmasking[edit]
As shown in the example code, a mask can be used with the bitwise AND operator to check for several buttons at once. The mask depends on which buttons should be checked as well as and . The more ticks that are utilized to buffer a button or direction, the larger the number in the mask will be.
For example, with and , these common masks could be used:
- 14448 - All kicks (all ticks)
- 903 - All punches (all ticks)
- 16383 - All punches & kicks (all ticks)
- 2193 - x+a (all ticks)
Advantages & Disadvantages[edit]
The main advantage of Deep Buffering is that it provides a complete implementation for both command interpretation as well as buffering. Users have control over each individual element and how much time should be given to complete it, and as mentioned before, the overall command window expands with each element added to the sequence. However, this can be hard for users to read, and as such, tools exist such as Jesuszilla's online Deep Buffering Converter to make defining commands in this system trivial. However, some knowledge of bitwise functions is still required in order to use it to its fullest potential.
Another disadvantage is that some games (particularly older ones) may not use this algorithm for interpreting commands, opting instead to use a full window check. For such use cases, Tiny Buffering is recommended.
Because only cardinal directions and buttons need to be checked, only the most basic commands need to be defined in the .CMD, making Deep Buffering a prime candidate for use in a tag system.
Like all buffering helpers, this trivializes moves that require a target to go back to their own set of states when they press a button, such as Felicia's Cat Ride.
Simplifying Triggers[edit]
By using one extra variable, readability of characters that utilize Deep Buffering can be improved. The idea is to store each command as its own bit in a separate variable, that way only one single variable needs to be checked for the command itself in the .CMD. Bitwise operators are still required, but access is much simpler than creating large masks of arbitrary command lengths. The following example is taken from Jesuszilla's CvS2 Eagle buffering.vns:
[State 10372, Command Active] type = Null trigger1 = e||(var(59) := 0) ; INIT trigger1 = e||(var(59) := (var(59)|((2**29)*((Var(54)/(2**4)) = 1)))) ; DU trigger1 = e||(var(59) := (var(59)|((2**30)*((Var(55)/(2**4)) = 2)))) ; FF trigger1 = e||(var(59) := (var(59)|(-2147483648*((Var(56)/(2**4)) = 2)))) ; BB trigger1 = !root, numProjID(131035) ; Chizuru seal trigger1 = e||(var(59) := (var(59)|((2**0)*((Var(20)/(2**4)) = 3)))) ; QCF trigger1 = e||(var(59) := (var(59)|((2**1)*((Var(21)/(2**4)) = 3)))) ; QCB trigger1 = e||(var(59) := (var(59)|((2**2)*((Var(22)/(2**4)) = 3)))) ; DP trigger1 = e||(var(59) := (var(59)|((2**3)*((Var(23)/(2**4)) = 3)))) ; rDP trigger1 = e||(var(59) := (var(59)|((2**4)*((Var(24)/(2**4)) = 5)))) ; HCB trigger1 = e||(var(59) := (var(59)|((2**5)*((Var(25)/(2**4)) = 5)))) ; HCF trigger1 = e||(var(59) := (var(59)|((2**6)*((Var(40)/(2**4)) = 5)))) ; QCFx2 trigger1 = e||(var(59) := (var(59)|((2**7)*((Var(41)/(2**4)) = 5)))) ; QCBx2 ignorehitpause = 1
Each special command is given its own bit and reversed as necessary to indicate flipped commands. The QCF command can be checked with one simple trigger with this:
triggerall = (helper(10372), Var(59)&(2**0)) > 0
As usual, 32 bits can be utilized, and thus, up to 32 unique commands can be detected with one variable. This is an addition to Deep Buffering, not a complete replacement, so the base system is still required for this to function.
This is completely optional, but is recommended for Street Fighter II characters as the original game uses something similar to indicate and randomize specials.
Commerical Games[edit]
Deep Buffering is heavily inspired by Capcom vs. SNK 2's command interpreter and buffer, and in fact, uses the exact same algorithm to perform its calculations. Given the industry standard of reusing code, many other games utilize this same method. The history of this implementation goes as far back as Street Fighter II: The World Warrior.
- Street Fighter II Series
- Vampire Series
- Street Fighter Zero Series
- Marvel Series
- Street Fighter III Series
- Capcom vs. SNK Series
- Capcom Fighting Jam