YesNoOk
avatar

Tiny Buffering! An easy, simple and compact buffering system!  (Read 16565 times)

Started by Vans, February 25, 2017, 04:04:10 pm
Share this topic:
Tiny Buffering! An easy, simple and compact buffering system!
#1  February 25, 2017, 04:04:10 pm
  • ****
    • Japan
Good evening!

In this topic I will be showing how to implement a very minimal and compact version of my buffering system to a MUGEN character.

The Tiny Buffering system is a much simpler version of more complex systems, and it can be updated over time to cover more complex functions. By itself, Tiny Buffering offers a wide variety of features:

  • User definable buffering windows.
  • User definable shortcut buttons.
  • Fixes the reversed commands bug in MUGEN.
  • Provides an easy way of canceling command detection.
  • Dramatically improves detection of commands with negative edge.
  • Dramatically reduces the amount of unique command labels used, useful for keeping your own .CMD files standard!
  • Allows expansion for more technical features!

Tiny Buffering is compact and stands as its own coding file. Defining new commands and working with it is as natural as defining commands in the CMD file.

Implementing Tiny Buffering in one character allows for further expansion to do one or several of these more advanced features:

  • Restricting commands based on the number of buttons pressed (KOF).
  • Sharing commands between player and partner, to allow for true tagging (KOFXI, RotD, Marvel Vs.)
  • Completely rewriting MUGEN's command parser, to fix the release bug.
  • The ability to manually define joystick buffering, for the hardcoded movement (jumps, walk, etc).
  • Sky is the limit! It's very powerful and not all of the possibilities have been explored yet.

In this thread I will be documenting the process of implementing Tiny Buffering to one MUGEN character.

In my case, I will be working with my old Geese Howard, found in this link..

Everyone is welcome to take their favorite character and work with me.

You can skip to any section of this tutorial by clicking the following links:

Quick Start:
Phase 1: Getting started.
Understanding the Joystick.
Coding Priority and Recursion.
Phase 2: Buffering and Timers.
Phase 3: Working with Buttons.
Phase 4: Multiple button presses and shortcuts.
The Reversed Commands Bug.
Phase 5: Defining Joystick Commands.

First Expansions:
Last Edit: August 10, 2017, 10:25:56 pm by Vans
Re: Tiny Buffering! An easy, simple and compact buffering system!
#2  February 25, 2017, 04:47:54 pm
  • ******
    • www.justnopoint.com/
Would you mind explaining the differences between the tiny version and full version 2.0 recently updated by JZ and that has tool support?

I'm very much into buffer 2.0!
Re: Tiny Buffering! An easy, simple and compact buffering system!
#3  February 25, 2017, 05:14:18 pm
  • ****
    • Japan
Sure thing!

Tiny Buffering still uses MUGEN's command parser to read joystick commands, this means that you would still define commands in the .CMD but only commands that check the joystick.

Buffering Step 2 gets rid of MUGEN's command parser entirely: Step 2 manually detects the motions of the joystick and detects when a particular motion is completed.

People who use Tiny Buffering would only need to add a couple of modifications and upgrade their triggers to adopt Step 2. In addition, having experience with Tiny Buffering greatly helps in understanding what Step 2 is doing.

That's the main difference!
Phase 1: Getting started.
#4  February 25, 2017, 06:02:31 pm
  • ****
    • Japan
We'll begin by adding the necessary ingredients for Tiny Buffering.

Things we'll need:

  • The tiny buffering template.
  • An empty animation with no sprite data or collision data (with the number of your choosing).
  • Your character.

First download the Tiny Buffering template from this link.

Afterwards, place this file in the root file of your character and append it as a coding file in the .def, like so:

,

With this, the Tiny Buffering coding is accessible in your project and is easily editable using IDEs such as Fighter Factory.

Before we begin, let's set the basic stuff first like the header

Code:
;==============================================================================;
;Command Buffering File for: Geese                                             ;
;Tiny Buffering! An easy, simple and compact buffering system!                 ;
;by Vans                                                                       ;
;http://www.trinitymugen.net/                                                  ;
;==============================================================================;

I like to specify the character that owns this file. Afterwards, let's move on to the Statedef:

Code:
[Statedef 10371] ;Define a state number for the helper here. 
anim = 1 ;This can be any animation that is empty.
ctrl = 0

The statedef number can theoretically be any number you want, but this number was chosen as to not clash with commonly used ones.

The line for anim checks for the empty animation I asked you to have in requirements.



Now that we have these things, I'll explain a couple of things.

In Tiny Buffering we only need to define commands in the Buffer Definition section, and we follow the same logic as the .CMD file.

Tiny Buffering already provides checking for the 7 normal buttons (XYZ, ABC, Start) as well as the negative edge versions of these buttons. In order to detect double, triple or more button presses at once, we need to define those manually as well. We will be covering those at a later time.



Preparing our .CMD file:

First of all, I recommend everyone to delete all the special move commands from the .CMD and start with the very basics. This is scary, I know, but trust me, it'll be worth it.

KFM (sans the special commands) is a good blank slate. The CMD I will be working with, looks like this in the first portion:

Spoiler, click to toggle visibilty

Some very important points: the buffer.time = 1 lines in the buttons are very important, since we're gonna be handling this with Tiny Buffering now.

Once you begin with a clean .CMD, take all your code in [Statedef -1] and make a backup.

We shall be adding your code back little by little, but this time the code will make use of Tiny Buffering.

This concludes the preparation.

We shall continue in Phase 2!
Understanding the Joystick
#5  April 13, 2017, 03:57:36 pm
  • ****
    • Japan
Alright! In this mini section we will try to understand the joystick.

For this purpose let's think of a joystick for moment.


OUR GOOD FRIEND THE JOYSTICK

For the purpose of fighting games, we are concerned with detecting a total of 8 directions, the ones that form the (two) cross map(s):

UBUUF123
BNF456
DBBDF123

I will be using the notation to the left, although some of you might be used to numerical notation. The previous map should show what I'm referring to in any case.

For the remainder of the tokens and symbols, I will be using the MUGEN standard of notation, because I think it is appropriate. This is a quick summary of the symbols I'll be using and their meaning:

Tilde (~): Detection of a button or a direction release of the joystick.

Dollar ($): Cardinal-only. In a command sequence, presenting $U,$D,$F or $B means that any diagonal input within the range of that cardinal is accepted as a valid input in the sequence.

For example, detecting a command as the sequence {F,$D,B} means that the subsequences {F,D,B}, {F,DF,B}, {F,DB,B} are all valid for activating the command.
 
Understanding the joystick.

Fighting games were born as arcade games, so it is no surprise that fighting games have mainly been designed to make use of joysticks as the main source of input. Originally most games did not require many precise movements other than moving vertically and horizontally, so joysticks used to be 4-way, this means diagonals were either mostly muted or completely out of range.

When fighting games came around, the more popular 8-way was starting to get its big break, and fighting game design started to take advantage of that. That's where the special move was created.


X-MEN just assumes you don't know your movelist.

Now that diagonals are available to use, how can we make good use of them? Simple! We can detect joystick revolutions! In a certain sense, it is like drawing. Detecting revolutions was the first step to establishing what we know as special moves, but detecting revolutions is not entirely a trivial problem.

In this regard, we can actually think that there's two very special schools of detecting joystick motions: the Capcom school and the SNK school. To illustrate, let's look at this picture:


Images borrowed from SlagCoin

What you see is the area representation of two distinct joystick restrictor gates, on the left we have a square gate and on the right we have an octagonal gate. These two gates co-existed in arcade cabinets during the golden era of fighting games, and each developer tailored their input detection and games towards the specific gates the games were produced for.

The square gate (on the left) allows the joystick to map an equal area or region to every possible direction (marked as 11%). In a square gate each direction is given the same amount of priority in terms of detection.

One particular aspect of the square gate is that it is highly difficult to perform inputs by riding the gate, this means that for the most part the joystick should never be touching the "walls" of the gate. This added a layer of difficulty to beginners and people who were not used to playing arcade games.

Capcom, in particular, was a company that tailored their games for square gates, as their games would mostly be featured in cabinets with the Seimitsu LS-32 joystick, which came equipped with a square gate. This type of joystick restriction gate is now the standard used for consumer arcade panels and arcade cabinets in Japan.

The octagonal gate (on the right) modifies the shape of the walls to form an octagon and thus creates an uneven distribution for our 9 possible joystick positions.

While the octagonal gate restricts the area of diagonals by 5%, it also holds one special perk: the octagonal shape! By using an octagon shaped restrictor, this type of joystick would allow new comers to have a "guide" for accurately moving the joystick. In this type of joystick, one can verify the position of the lever at all times by touching the nearest wall. This makes playing games more welcoming!

SNK, in particular, was a company that tailored their games for octagonal gates, as their joystick for AES and MVS systems was a modified version of an LS-40, with an octagonal (and sometimes circular) gate. Octagonal gates are often recommended to beginners who haven't used arcade sticks before.

When designing a game, and especially a fighting game, it is very important to keep in mind the type of hardware you're tailoring inputs for.

What's the most important aspect about any game? Well, being able to fucking play it! - James Rolfe as The Angry Video Game Nerd.

Coke or Pepsi? What input design school to choose?

The way a game responds to user input is one of the most important aspects for leaving a lasting impression to a player. What's the best way to go about it? When thinking of the golden era of fighting games we obviously have to examine what Capcom and SNK did back in the day.

Let's discuss a little how Capcom does it.


I just wanted to post this broken stance Guile gif.

Capcom designed their games with the square gate in mind, that is, they designed their games with the idea that the joystick would have an even distribution in regions for every possible direction. This carried over to their command design too!

There's one particular motion that demonstrates this more clearly to us: the half circle motion.

For a Capcom game, the half circle motion is parsed as follows:

Code:
~B,DB,D,DF,F

This means that, the diagonals are all checked and are a necessary aspect of the motion. Since the distribution of our diagonals is even, performing this motion with 100% accuracy is not too much to ask as it is just one swift motion of our wrist.

But what happens when we go over half a revolution? That's where things get interesting.

Code:
~D,F,U,B or ~F,U,B,D or ~U,B,D,F or ~B,D,F,U or ~D,B,U,F or ~B,U,F,D or ~U,F,D,B or ~F,D,B,U

This is the command for detecting a 360 motion, in this case full accuracy with the 4 diagonals is no longer asked, only cardinal accuracy is important. One important factor for this is that muscle memory for controlling the upper portion of a joystick is not usually developed as fighting games are focused on the lower half most of the time.

One of the nice aspects of square gates is that since the gate is a square, it is very easy for us to confirm the location of the cardinals , this is why they're chosen as the pivots for greater motions such as a 360.

In short, Capcom keeps a very strict case of observing diagonals throughout an input and relies on cardinals to define simplified greater revolutions.

Now let's look at SNK!


Let's not get too hasty now, we are almost done.

SNK is a very special case, as they turned what used to be a limitation into a quirk or personality.

In the very beginning, SNK made commands the exact same way as Capcom did, which meant that each direction in a sequence would receive equal priority in every step of the way. That is, until one game changed it all.


This bad boy.

What do we do when we must define a Power Geyser? Power Geyser is a motion that requires a quarter revolution backwards followed by half revolution forwards.

If we recall that SNK favored octagonal gates, then we have to observe that the areas assigned to diagonals are smaller than they would be compared to a square gate. This means that by doing two wrist flicks in opposite directions is very likely to produce an unintended error during the input.

The solution? Let's make it more lenient!
Code:
~D,DB,B,DB,F

This is the original sequence definition for a Power Geyser in Fatal Fury 2. Notice that for the final half circle, only one direction is checked instead of three. This is the basis of what SNK would later do with their commands.

A modern Power Geyser or Hou'ou Kyaku now has this form:

Code:
~D,DB,B,$D,F

This is a now modern and evolved version of the original solution they designed back in the old days. This command design was chosen to eliminate the constant execution errors by missing one diagonal by mistake (often due to the diagonal areas being small), but it carries over all the way to modern times, even without octagonal gates being standard.

Half circles and 360 motions also follow this same principle:

Code:
~B,$D,F
~F,$D,B,$U or ~D, $B, U, $F or ~B, $U, F, $D or ~U, $F, D, $B or ~F, $U, B, $D or ~U, $B, D, $F or ~B, $D, F, $U or ~D, $F, U, $B

Conclusion.

Knowing the existence and differences between the design styles of commands is a very important aspect of creating (or emulating!) fighting games.

Not all the rules are set in stone, but it is a good idea to sit down and think who the target audience of your game is, based on your target audience they might appreciate a little more to find that the personality behind the inputs is something that rings close to home.

Now that fighting games are more popular and diverse, it is not surprising to see games that mix rules and ideas of both styles, but not without careful study of the reason and the ideas behind their design.

Before jumping in to any paradigm altering buffering things, I invite everyone to pay more attention to commands and inputs, as this is an area that is often not given enough care.

This concludes this mini section, let's discuss more code in the next one!
Last Edit: April 13, 2017, 05:20:24 pm by Vans
Re: Tiny Buffering! An easy, simple and compact buffering system!
#6  April 13, 2017, 04:17:06 pm
  • ******
  • 日本は素晴らしい国です。
cool aarticle vans, ill share it with my coworkers that are getting into fighting games.
Re: Tiny Buffering! An easy, simple and compact buffering system!
#7  April 13, 2017, 04:19:46 pm
  • ******
    • www.justnopoint.com/
Yeah I had no idea about much of this! Very nice!!!
Re: Tiny Buffering! An easy, simple and compact buffering system!
#8  April 13, 2017, 06:42:15 pm
  • ******
  • Loyal to the Game
    • USA
    • http://jesuszilla.trinitymugen.net/
Very cool read! I had no idea SNK optimized for octagonal gates. This explains why Capcom changed certain SNK commands in the CvS games.

[placeholder for full command later after I check it because I cannot recall if they left out the B in the QCB portion]

SNK is not alone in simplifying non-cardinals. Capcom does it too, particularly for supers.

Consider a QCFx2. Your intuition may lead you to believe the command would be:
Code:
~D,DF,F,D,DF,F

However, this is actually not the case! The actual command is:
Code:
~D,DF,F,D,DF

Note that the last F is left out of the command sequence.
Last Edit: April 13, 2017, 06:46:19 pm by Jesuszilla
Re: Tiny Buffering! An easy, simple and compact buffering system!
#9  April 13, 2017, 09:03:46 pm
  • *****
  • Sprite Whore
    • USA
    • www.facebook.com/lee.tuttle.7
All of this seems a little over my head to a degree, but as far as the 360 motions go, it was found, at least for Capcom games, that you just have to hit $U,$D,$F, and $B in any order. The example I watched a while ago was from SF4 with Gief.

EDIT: I don't know if this is entirely useful, but it might be worth noting.
Last Edit: April 13, 2017, 09:07:13 pm by Hoshi
Re: Tiny Buffering! An easy, simple and compact buffering system!
#10  April 18, 2017, 09:10:04 pm
  • *****
    • Peru
All this explains a lot of the shortcuts present in many games. Now a lot of crazy shit, -specially from SF4 engine- makes sense. But I can't figure out how some commands to get a Kara Karakusa work :/

Oh god, after years I want to code again.
Coding structure, priority and recursion.
#11  June 03, 2017, 06:42:47 pm
  • ****
    • Japan
In this section I'll talk a little about coding structure, coding priority and recursion.

Coding priority.

The Tiny Buffering system was designed to make efficient use of the way MUGEN handles coding. Remember that the code in each state is parsed from top to bottom in a single frame. This implies that the order in which we structure the coding is important when we try to optimize or achieve certain types of behavior.

The concept of protecting code order is not a new one, this is actually really important when we are trying to define the .CMD file. If we place two very similar commands in the wrong order, then we could run into trouble.

For example, consider the case where you have a dragon punch (F,D,DF + P) and a fireball (D,DF,F + P). On the .CMD file, you would require [ChangeStates] in order to use both of these moves. Let's say that the state for the dragon punch is 1000, and the state for the fireball is 1100.

Let's observe what happens with the two different code orders.

CASE 1. In our .CMD file we write the two changestates in this order:

Code:
[Changestate, to state 1100]
trigger1 = ...   ;The dots mean the rest of the coding goes here.
...

[Changestate, to state 1000]
trigger1 = ...
...

If we write in this order, then the engine will read the [Changestate] for state 1100 before the [Changestate] for state 1000. That is, the dragon punch changestate code takes priority over the fireball changestate code.

Let's say then that you input F,D,DF,F + P very quickly to get a dragon punch. At the time we press punch, MUGEN will have understood that your joystick inputs were exactly "F,D,DF,F".

Since "D,DF,F" is a part of those inputs, and the changestate for 1100 takes priority over the other one, then this changestate will trigger first. This means, we will perform a fireball instead of a dragon punch.  As you can already tell, this does not happen in commercial fighting games.

So what happens in the other case?

CASE 2. In our .CMD file we write the two changestates with the opposite order

Code:
[Changestate, to state 1000]
trigger1 = ...   ;The dots mean the rest of the coding goes here.
...

[Changestate, to state 1100]
trigger1 = ...
...

In this case, state 1000 takes priority over state 1100, and thus, the test we had before will now work as it should! This is the right order we wanted!

When we are deciding the order of changestates for commands we usually call this "coding through order of complexity". Some people also refer to this as "putting the complicated commands on the top". Just as the name implies, the more complex the command, then the more coding priority it will require, otherwise more simple commands will take precedence over it.

This type of coding concept can be generalized to all the coding that is done in MUGEN, and it is very good practice to carefully decide the order in which certain types of behavior are programmed.

Thinking outside the box about the box.

Now that we are starting to pay attention to the coding order, we can have a clear picture in our mind of the way coding is read. So let's just put it into an actual picture now:


This concept is very simple and very fundamental in MUGEN coding, but not many coders make full use of the power and possibilities good order optimization can provide. MUGEN provides a very powerful coding style that is well tuned for programming things recursively. Good coding order can expand simple ideas, like displaying digits on-screen, to very complicated ideas, like programming an entire system for handling text.

So when it comes to working with a system such as Tiny Buffering, then it is a very good idea to change our perspective a little. We can think of Tiny Buffering as a box, this is an invisible magic box that contains code that is checked every single frame. Since this box is a helper, then this box is executing its coding, independently from the player, every single frame!

This means that we have created a piece of coding that, regardless of the state of the root, will be running its own vital pieces of code independently. We can also communicate and trade information with this box. This means that we are working with a compact system, its definitions and behavior are all self-contained in their own states and it runs independently from the root.

Recursion and phantom data.

So now that we are working with a compact system, why is it so important to take care of the order we program things? That is so we can keep our options open for recursion. Recursion is a technique to define and execute things following a specific order.

We can think of a simple example: using only one variable, how would we display on-screen the individual digits of a 4-digit positive integer? This could come up if we are trying to define a system for displaying hit points or damage. Let's think about this problem and write down a logical solution to this (you can then attempt to code it as an exercise).

I will leave the solution as a spoiler in case you wanna try it yourself before reading!

Spoiler, click to toggle visibilty

So what happens in this example? Recall that mugen is reading our code from top to bottom in a single frame. So what happens to the values we kept feeding to var(0)? They certainly existed, but they are nowhere to be seen in the next frame. This is transitional or phantom data.

In MUGEN we can tailor many different types of phantom data, they could be constants (like in the example) or even entire states! A transitional state would be a state that is completely parsed by MUGEN during a frame, but it is not a state that is active at the time of a frame being drawn.  Plenty of MUGEN bugs are caused by people not realizing the existence of phantom states, their properties, and how to account for them. Even Elecbyte themselves have bugs caused by phantom states. Particularly, a big mistake is making use of the statement "trigger1 = 1" instead of "trigger1 = time >=0" but this is a discussion for a different time.

For Tiny Buffering, we shall not be requiring the use of any phantom states, but we will be taking care of having good coding order so we can make use of recursion. When it comes to variable-heavy systems or implementations, utilizing transitional data with variables is an extremely simple and efficient way of optimizing their use and simplifying the coding.
Re: Tiny Buffering! An easy, simple and compact buffering system!
#12  June 26, 2017, 01:53:38 am
  • avatar
  • *
    • edwin@disenowebcostarica.net
Greetings Vans.

One thing that is happening to me in my mugen KOF project is when I use "a" and "y" together  (a is weak kick, y is strong punch) to activate cancel bar after a normal (KOF 2002 style ), the character launch a normal attack, all the times is the weak kick (which corresponds to the "a" button).
To avoid this I have to set cancel bar activation to  button "c", when activating the bar the character does a dash or stay neutral depending of the action of the player, which is the intended behavior. Sadly having players pressing an extra button to activate that bar doesnt feel "KOF" and I was wondering if this buffer system can help me on that.
By the way, when is phase 2?
Re: Tiny Buffering! An easy, simple and compact buffering system!
#13  June 29, 2017, 10:14:18 am
  • ****
    • Japan
Indeed, normally that behavior is difficult to replicate, especially with characters that have an extremely short running animation. This system can reproduce that behavior because it allows you to instantly reset the buffer time of all the buttons in a single frame, like KOF does.

This is also useful for implementing roman cancels and any feature that requires very precise control over the buffer windows.
Phase 2: Buffering and Timers.
#14  June 30, 2017, 03:03:49 pm
  • ****
    • Japan
Alright, today we shall be learning about how to work with the buttons. This will also help us understand the main idea behind buffering.

Understanding a buffer.

I figured we'd need good motivation for this section.

A fighting game engine has two very important pieces to deal with user inputs, these two parts are the command parser and the buffer.

The command parser is the system that constantly receives input from the user via the joystick. This system constantly receives the input data and alerts the engine whenever it finds a favorable "pattern", these "patterns" are precisely the revolutions used to define special moves. You can think of this system as a system that understands "joystick-ese" and translates it into "commands".

In MUGEN, the command parser is the section that is located in the .CMD. For example, take a look at this line:

Code:
[Command]
name = "QCF"
command = ~D, DF, F

This piece of code is directly communicating with the command parser in MUGEN. This code says, "Dear command parser, keep a look out for the word '~D, DF, F' from joystick-ese, and translate it into 'QCF'". Afterwards, we can only refer to the command by name, because this parser is actively translating the word.

The command buffer is the system that receives the data from the command parser and performs operations in order to fine-tune the controls and adjust priorities. This piece was introduced mainly because of two things: humans are slow and using joysticks proficiently is difficult.

The command buffer will receive a note from the command parser whenever a new "word" is received. The buffer will then decide how complex this "word" is to give the user enough time to perform other actions with the joystick and complete a message. Without a buffer playing fighting games would be next to impossible, as we would have 1-frame windows to perform any action!

You can think of the command buffer as an editor for the translation given by the parser. The parser only translates words, but the editor makes sure these words flow together nicely.

These two systems are very intimately linked and both are absolutely necessary while designing a fighting game.

In the concrete case, Tiny Buffering is a replacement for MUGEN's buffering system in order to correct its flaws. Not only can we correct its flaws, we also have more precise control over the buffering system to more closely replicate buffering in commercial games.

On the other hand, Tiny Buffering Step 2 provides a replacement for MUGEN's command parser. This is, again, done to correct bugs and problems in the original implementation.

Timers.

Stop time!

Let's get to work now! Now that we have a general idea of how a buffer works, there's one very important concept that pops up, and that is, the concept of windows. By window we of course mean an interval of time which is often defined in "frames", or that is, 60ths of a second.

Thus, we arrive at a core concept for a buffer, and this is the concept of timers, in order to create these windows we require a set of carefully defined timers.

To define a timer in MUGEN we need three things:
  • A variable to store the timer.
  • A controller that initializes the timer.
  • A controller to decrease our timer.
That is, we need two controllers in order to define a timer. The Tiny Buffering template I provided already includes several pre-defined timers as an example, but we shall go over them to understand why they were defined that way.

Let's take a look at one of the timers that is labeled "LP" in my example:

Code:
;----------------------------[BUFFER DECREASE]---------------------------------;
...
;--------------------------------[BUTTONS]-------------------------------------;
...

[State 10371, LP Dec]
type = VarAdd
triggerall = root, HitPauseTime = 0
trigger1 = var(0)
var(0) = -1

...

;--------------------------[BUFFER DEFINITION]---------------------------------;
;----------------------------[BUTTON BUFFER]-----------------------------------;
...

[State 10371, LP Init]
type = VarSet
trigger1 = command = "x"
var(0) = 3 ;This defines how long the buffering should be for this button.

Alright! So as we can see, this is indeed a proper timer with two pieces: the timer definition controller and the controller to decrease the timer. Please note that I specifically wrote a huge label at the top that says "buffer decrease" and another that says "buffer definition", and this has to do with the coding priority I spoke about before. If these two controllers are reversed in order, then the timer will decrease as soon as we define it, making the window ambiguous.

Since this is extremely important, I will now write in big bold letters so you remember.

When you define a timer, the controller that decreases this timer should be ABOVE the controller you use to define the timer.

Tiny Buffering already separates these sections so you never forget or get confused. Respect these sections and you will avoid problems.

I cannot stress this point enough, I know of many veteran coders who do not pay attention to the coding order and priority in controllers and end up making mistakes when defining timers. This is a real problem, please just follow the section labels and you will be fine, they were designed for precisely this purpose.

While it is possible to write timers in a million ways and one so they can be in a different order, we are really just going around the problem. The way we will be using is the simplest way to write them so there is no ambiguity between what the constants represent and what each controller does. In other words, this is code that can easily be read and easily understood for everyone involved.

Moving on! Let's take a look at the pieces here. First, the buffer definition controller:

Code:
[State 10371, LP Init]
type = VarSet
trigger1 = command = "x"
var(0) = 3 ;This defines how long the buffering should be for this button.

This controller creates a timer of "3" frames whenever we press the "x" button (commonly mapped to Light Punch). This number represents (in frames) how long the buffer window should be for this button. In KOF, this number is usually '3' frames for a button, which is what I provided as default.

Similarly, we will use the same syntax when we move to joystick commands.

Let's take a look at the buffer decrease controller:

Code:
[State 10371, LP Dec]
type = VarAdd
triggerall = root, HitPauseTime = 0
trigger1 = var(0)
var(0) = -1

As you can see, this controller decreases the timer by 1 frame. I will now explain the first trigger.

The condition 'root, HitPauseTime = 0' checks if your character is currently in hitpause. If the buffer was to decrease during hitpause, then the commands would not work correctly if the hitpause is longer than the buffer. Fighting games actively perform this operation to preserve the timer when the player is paused in this fashion.

This trigger is necessary because a helper does not inherit the hitpause effect of the root.

The second trigger, "var(0)" is the same as the trigger "var(0) > 0". This checks if our timer is still active before we attempt to decrease it.


Timers for the rest of the buttons are defined and designed in this manner. We will define more complex timers with this same mindset later on.
Last Edit: June 30, 2017, 04:38:34 pm by Vans
Phase 3: Working with Buttons.
#15  June 30, 2017, 04:26:50 pm
  • ****
    • Japan
Let's do some actual work now and define our basic moves.

Adjusting our mindset.

Don't slack off!

Tiny Buffering already provides a foundation, which is buffers for the 7 buttons on the joystick. In order to not get confused it is very useful to think of the arrangement in a graphical way. This foundation has two different set of buttons, one set is for normal button presses and the other is for negative edge detection.  I have prepared two useful graphics so you can always remember what these buffers are!

First, I would like to get everyone on the same page as to what I mean by "LP", "MP" and "HK". This map I refer to works like this:

=>

Most coders will use this type of layout when working with Capcom characters. Of course, this order is inherited from the arcade and the home ports of their games. I believe that this order is natural and easier to understand than using the names "X", "Y", "Z", etc. In case of NeoGeo games, there is ambiguity when using the actual button names, in those cases I will do my best to specify which button I am talking about.

Tiny Buffering provides the following two maps for the two sets of buttons. This graphic makes it more natural to understand the layout.

NORMAL BUTTONS
=>

NEGATIVE EDGE BUTTONS
=>

Now that we have these two button maps in our mind, we can actually start using them now!

Working with buttons

First let's provide buffering for our basic attacks, that is, the attacks that only require a button press to work. I shall provide an example and let you do the rest.

First I find one of my basic attacks...

Code:
;---------------------------------------------------------------------------
;Jumping Light Punch
[State -1, Jumping Light Punch]
type = ChangeState
value = 600
triggerall = RoundState < 3
triggerall = command = "x"
triggerall = Statetype != C
trigger1 = Statetype = A
trigger1 = ctrl

And then adjust the command trigger to check for the appropriate buffered version...

Code:
;---------------------------------------------------------------------------
;Jumping Light Punch
[State -1, Jumping Light Punch]
type = ChangeState
value = 600
triggerall = numhelper(10371) ;Prevent debug flood.
triggerall = RoundState < 3
triggerall = helper(10371), var(0) ; The buffered version of LP
triggerall = Statetype != C
trigger1 = Statetype = A
trigger1 = ctrl

Note that the line 'numhelper(10371)' is required to prevent debug flood errors, so don't forget it!

In case your basic contains any lines such as:
Code:
triggerall = command != "holddown"

Then these should be replaced to lines of the form:
Code:
(helper(10371),command != "holddown")

As you can see, it is the same condition! The difference is that we're asking our buffering helper to check.

This is used to keep everything standard, as we will be asking our buffering for directions further down the road. In case you have any basic commands such as running or hopping, you can leave them as-is for now. We do not need to modify these for Tiny Buffering step 1!

After carefully making the appropriate changes, my .CMD file now looks like this.
Phase 4: Multiple button presses and shortcuts
#16  July 25, 2017, 12:25:20 pm
  • ****
    • Japan
Today we will go over working with double button presses and defining custom shortcuts.

Double button presses.


Now that we know how to work with single button presses, we can start thinking about the multiple button case!

If you recall what we did before, Tiny Buffering assigns a timer to each button we press. This timer, or buffer, represents how long this button shall be active. For multiple button presses, we want to follow the same basic idea.

One of the strong aspects of Tiny Buffering is that the implementation of multiple button presses is completely free! That means that we have complete control over their detection and definition. This control allows us to detect multiple-button negative edge, define custom shortcuts and even restrict or simplify the detection.

The most basic and common definition of a multiple button press is the first one that comes to mind: a multiple button press should be detected if all the buttons involved are active. That is, the active window is the same as the intersection of the active windows of the buttons.

This type of definition is the most simple and the best example to learn how to define them.

In this example, I will be defining a multiple button detection for the LP+LK combination, this will allow me to detect a roll.

Definition.

To define a multiple button press first we scroll down to the [DOUBLE BUTTON BUFFER] section in the main Tiny Buffering file. This section has been specifically made for this purpose.

Our first example is this:

Code:
[State 10371, DOUBLE BUTTON init]
type = null
trigger1 = var(14) := (((var(0) = [1,3]) && (var(3) = [1,3]))) ;var(0) is LP and var(3) is LK.

The trigger in this null controller has been written in variable assignment syntax, you can find more information about it in the CNS documentation, under expressions. This is a method for assigning variables directly using triggers instead of varset controllers.

Remembering our fabulous button map from the previous section, we see that var(0) and var(3) are the LP and LK buffer windows respectively.

Let's then analyze the expression:
Code:
 (((var(0) = [1,3]) && (var(3) = [1,3])))

Recall that if we evaluate an expression within parenthesis in MUGEN, then it will return either 0 or 1. Thus, this expression will return one of those two values.

This means that the expression does the following: the value returned is 1 if the windows for LP and LK are both active at the same time and it shall be 0 otherwise. We are then assigning this value to var(14).

This is a very fast way of programming varsets, and it is extremely useful when attempting to do heavy recursion and reducing controller instances. I encourage everyone to practice this type of variable assignment as an exercise.

Having this controller in place, var(14) is what we need to detect LP+LK!

This is what my controller for performing a roll looks on my CMD now:

Code:
;---------------------------------------------------------------------------
;Roll 1
[State -1, Roll 1]
type = ChangeState
value = 750
triggerall = RoundState < 3
triggerall = numhelper(10371)
triggerall = helper(10371), var(14) ;LP+LK detection
triggerall = command != "holddown"
triggerall = Statetype != A
trigger1 = Statetype = S
trigger1 = ctrl
trigger2 = stateno = 150 && power >= 1000
trigger3 = stateno = 151 && power >= 1000
trigger4 = stateno = 101

Creating shortcuts.


I hope you're getting your hands dirty, or all this reading will be pointless!

I once more will remind everyone of our fabulous map. It is so important that I will now repost it:

NORMAL BUTTONS
=>

NEGATIVE EDGE BUTTONS
=>

At this point in time we have done something extremely important: we are the ones that have full control over how, when and where a button is pressed. We have full control over the behavior of the buttons, to the point where we can think that the original MUGEN ones behave like hardware.

Since I am working with a KOF character, I only need 4 unique buttons for all the required actions of the character. This means that I have two free buttons: HP (Z) and HK (C). For this reason, I will create button shortcuts using the remaining two buttons, just like in the home ports of these games.

As an example, I will code a shortcut for the rolling command (LP+LK). This has always been assigned to the HK (C) button since the KOF series appeared on Playstation.

The idea is simple: whenever I press HK, the game should behave as if I pressed LP and LK at the same time. But wait! Our beautiful map says that, in fact, we want var(0) and var(3) to be set at the same time whenever I press HK! This is extremely easy to do now!

Let's create a new section for button shortcuts!

Before the DOUBLE BUTTON BUFFER section, let's insert a new section for shortcuts (if you don't have it already):

Code:
;-----------------------  [SHORTCUT DEFINITIONS]-------------------------------;
;This section allows you to define the behavior of shortcut buttons.

Your buffering file should look like this:


Recall that the coding order is sensitive! So make sure the order is correct!

Our objective is this: set the windows for var(0) and var(3) at the same time whenever we press HK. We can use variable assignment to do this!

Code:
;-----------------------  [SHORTCUT DEFINITIONS]-------------------------------;
;This section allows you to define the behavior of shortcut buttons.
[State 10371, SHORTCUT INIT]
type = null
trigger1 = command = "c"
trigger1 = (var(0) := 3) && (var(3) := 3)

This automatically defines a real shortcut, pressing the HK (C) button will behave as if you pressed both the LP (X) AND LK (A) buttons at the same time. That means that this button can be used not only to roll, but also to perform other actions such as special moves.

Now, one of the true powers of Tiny Buffering will shine! Recall that variable number identifiers accept expressions as input!.

So for example, you could use an expression of the form "var(root,var(xx)) := 3" to select which button to define. This means that these shortcuts can be fully customizable using a configuration file! This is something that was impossible before!

This concludes the introduction to multiple button presses and defining shortcuts.

My coding files after finishing this section can be found here.

See you next time!
The reversed commands bug.
#17  July 28, 2017, 01:10:06 pm
  • ****
    • Japan
The dreaded reverse commands bug.

Uncover the truth!

The reversed commands bug is one of the most folklore bugs mentioned by many experienced MUGEN coders. This bug became more popular when Elix, a former KOF creator, mentioned that zzzasd was the only KOF creator to attempt a fix for this bug. However, the bug itself has never been fully described or explicitly mentioned. Here it is:

MUGEN bug (reversed commands): Whenever MUGEN calls upon a turn function on an object, all instances of the "F" or "B" token in the command definitions are flipped by the parser. In essence, the default MUGEN joystick detection has no memory upon turning.

Allow me to explain in layman's terms what this means. For the sake of having no confusions here, I will be saying LEFT or RIGHT as a physical direction on the joystick, this means that I don't account for the current state of the game.

Suppose you have a DP motion, such as this:

Code:
[Command]
name = "dp_p"
command = ~F, D, DF, y
time = 20

Let's set an example scenario. Let's say that I want to do a reversal DP on a jumping opponent that is trying to cross me up. Naturally, you would adjust your input towards the direction your opponent is facing.

For the sake of having hard numbers, pretend that I input LEFT and DOWN-LEFT in 3+3 frames and that I will input the final direction in the 7th frame. Let's look step by step what happens:


As soon as the turn occurred, MUGEN read my inputs as "B,D,DF", so when I press a button I will not obtain a DP as I wanted.

By the same token, i can get a DP by accident.

Suppose the same cross up is about to happen and I hold forward. Suppose that I want to input a backwards half circle, "~F,D,B" instead to do a super cool command throw:


Well I'll be damned. There is the DP.

So the issue here is: if I begin to adjust for the turning behavior in my inputs, those inputs I did are not adjusted by MUGEN once I did them. In terms of command labels, this means that I have no way of retrieving the raw inputs that I was doing in the joystick (in LEFT, RIGHT fashion). MUGEN doesn't keep an accurate record of it. Because in essence, I did input a half circle just fine.

What does this mean for potential fixes? Well.

  • If we attempt to fix it by checking for the reversed command during the turning animation, then we will get the proper command only if we buffer it completely before or after the turning animation. A partial attempt will fail like the ones above.
  • If we split the detection into directionals only, then this bug will also affect these directionals. This extra layer would present problems while trying to keep memory of the raw inputs, since we are still depending on MUGEN's parser and the turning flag.

In terms of solving this issue, there is an underlying and fatal mathematical problem. The assignment MUGEN provides from joystick motions to commands is NOT a function. This means that MUGEN is assigning two different names to the same object.

That is, the RIGHT direction on the joystick is assigned to "F" if we are facing right or "B" if we are facing left. Essentially, we cannot detect if what the user did on the joystick was press RIGHT, because all the information we can obtain is either "F" or "B". Effectively, this is Schrödinger's direction. There is no inverse for this mapping, because it is not a function.

Mathematically, this problem cannot be solved unless we have a way of obtaining a real function to read the actual joystick inputs without tampering. So, what if we get rid of turning altogether?!

If we have an object in MUGEN, that is not affected by the turn behavior then the directions we press will never, EVER be affected by this turning bug. This means that every single directional input we read will actually correspond to what the user is physically doing to the joystick!

Eureka! A helper is an object that can satisfy this condition! This is precisely what Tiny Buffering does to fix the reversed commands bug once and for all!
Last Edit: July 28, 2017, 01:15:42 pm by Vans
Re: Tiny Buffering! An easy, simple and compact buffering system!
#18  July 28, 2017, 01:53:53 pm
  • ****
A very cool read, thanks Vans, that's some dedication right here.

ink

Re: Tiny Buffering! An easy, simple and compact buffering system!
#19  July 28, 2017, 06:10:57 pm
  • ****
  • inktrebuchet
    • USA
    • s236.photobucket.com/albums/ff271/inktrebuchet/?
I really appreciate all this info! This makes understanding your buffer system super easy.

I feel kind of silly for not getting it before.
Phase 5: Defining joystick commands.
#20  August 10, 2017, 05:51:30 pm
  • ****
    • Japan
Thanks for sticking around for all this time! Today we can finally go over the most important (and the easiest!) step. We will be defining commands.

Once you complete this section, you will be 100% capable of deploying a character with buffering.

.CMD standardization and synchronization.

A believing heart is everyone's magic!

Remember how I asked you to strip your .CMD of all special commands? Today is the day where we will start inserting them again.

In Tiny Buffering we take care of buttons as separate "entities", this means that in terms of MUGEN's parser we are only worried about checking the actual joystick inputs. Thanks to this, it is only necessary to define special commands as purely directional inputs and nothing more!

While this sounds natural in terms of the system itself, Tiny Buffering was designed precisely so we only had to define commands this way. This is because one extremely important goal can be effortlessly achieved and that is .CMD standardization.

What does this mean? Well, in MUGEN each .CMD file is limited to 128 unique command labels. For example, if we wanted to program a character with two different types of "shoryuken" depending on whether we use a punch or a kick... then we would have at least two different labels for what is the exact same motion of the joystick. Depending on the complexity of the character, many different labels might need to be used for the exact same joystick motion because the buttons are different.

Tiny Buffering eliminates this distinction, we only need one* label per joystick motion!

If we are programming a full game, for example, we can easily utilize one single .CMD file with these labels defined for every single character. We save space, resources and achieve what I call .CMD "synchronization".

.CMD synchronization.

In MUGEN we have one interesting trigger called "command", but what does it actually mean? When we program a character we know we have command labels, and when we use this trigger we ask MUGEN to check if the command used corresponds to the label. But this is not what MUGEN does!

When MUGEN loads our character it compiles a "list" formed by ALL our command labels, using the order in this list it identifies which commands we are asking for. This means, for example, that if "QCF_p" appears as the third command in this list, then what MUGEN does is the following:

  • MUGEN reads: command = "QCF_p"
  • MUGEN interprets this as checking if the third item on the list activates.
  • MUGEN checks if the third item on the list is activated.
  • MUGEN returns the trigger as true if the joystick command was performed.

The key is the second item, MUGEN is not actually looking for the name of the command, it is looking for its position on the list it compiles at the beginning. Which features open up to use if we have perfect .CMD synchronization? Easy!

Let's say you have a full game comprised of perfectly .CMD synchronized characters. When this happens the following triggers become perfectly valid and accurate in every situation:

  • partner,command
  • enemy,command
  • target,command

Number one is the most important in this list. With perfectly synchronized .CMD files, we can read our partner's commands accurately.

Having perfect .CMD synchronization allows us to program many features, including (but not limited to) real tagging! This type of tagging does not require the use of CTRL+3, remapping the keys of the second player or any external setups for it to work. With careful operation, this type of gameplay can be programmed with good .CMD synchronization!

Defining commands.

This is a piece of cake!

In this example we will program a special move, a super move is done exactly the same way! Let's set up a simple quarter circle motion.

In our CMD file we will need to define the joystick motion, like so:

Code:
[Command]
name = "qcf"
command = ~D, DF, F
time = 21

Observe that I have no buttons defined in it, this is important as we do this separately!

One important parameter here is "time", this is the window of time the player has to complete this motion. In KOF's case (by counting) a quarter circle motion has at maximum 21 frames for it to be completed.

Now, since we have defined a quarter circle forwards motion we will also need its reversed version. Recall that Tiny Buffering is an object that detects joystick motions in an absolute sense, this means that we absolutely need a label for the reversed version so we can detect it!

Just like we did the quarter circle forward, let's define the reversed version:
Code:
[Command]
name = "qcb"
command = ~D, DB, B
time = 21

Just like we did with the buttons, we will be setting a timer for this quarter circle!

Let's head over to the Tiny Buffering file and find the [COMMAND BUFFER] section so we can code it!

The basic timer should look like this:
Code:
;An example of defining buffering for a quarter circle.
[State 10371, QCF Init]
type = VarSet
trigger1 = p2dist X >= 0 && command = "qcf"
trigger2 = p2dist X < 0 && command = "qcb"
var(20) = 13 ;The joystick motion will be active for 13 frames.

As before, we create a timer that will have a window of "13" frames. Observe that we have two triggers, the first one is when the opponent is to the front so we keep track of the joystick motion DOWN,DOWN-RIGHT,RIGHT. In the other case, our opponent is to our back, so we check for the joystick motion DOWN,DOWN-LEFT,LEFT.

Since this helper does not turn, this assures us that we will not be affected by the reversed commands bug as we wanted before!

Now, in my case, KOF does not allow me to perform a standing special move if I hold down or the opposite cardinal direction at the end. To add this limitation, I will adjust my own triggers a little bit:

Code:
;An example of defining buffering for a quarter circle.
[State 10371, QCF Init]
type = VarSet
triggerall = command != "holddown"
trigger1 = p2dist X >= 0 && command = "qcf" && command != "holdback"
trigger2 = p2dist X < 0 && command = "qcb" && command != "holdfwd"
var(20) = 13 ;The joystick motion will be active for 13 frames.

Perfect! Depending on what you are attempting to code, you can add restrictions and refined checks as you wish. Observe that even the detection of reversed commands is completely open to you, the coder!

Let's not forget to log on our checklist that we have added defined this command:


Finally, let's head over to the [BUFFER DECREASE] and then [COMMANDS] subsection so we can decrease this timer. We will define it exactly as the button ones are set up:

Code:
;An example of decreasing the buffer timer for a quarter circle.
[State 10371, QCF Dec]
type = VarAdd
triggerall = root, HitPauseTime = 0
trigger1 = var(20)
var(20) = -1

So this section should look like this:


We are done in the Tiny Buffering file, all that is left is to go back to the .CMD and program the changestate!

In my case I am programming Reppuken, so I will need to check for punches to activate this special move. Exactly like we did the buttons, we make use of var(20) to detect the joystick motion!

This is how the changestate looks in my .CMD:

Code:
;---------------------------------------------------------------------------
;Reppuken
[State -1, Reppuken]
type = ChangeState
value = 1000
triggerall = RoundState < 3
triggerall = numhelper(10371)
triggerall = helper(10371), var(20)
triggerall = (helper(10371), var(0) = [1,3])  || (helper(10371), var(1) = [1,3])
triggerall = statetype != A
triggerall = !NumHelper(1005)
trigger1 = ctrl || stateno = 101
;Basic moves cancelable into command moves
trigger2 = stateno = 205 && animelemtime(2) > 0 && animelemtime(3) < 0
trigger3 = stateno = 215 && animelemtime(4) > 0 && animelemtime(5) < 0
trigger4 = stateno = 305 && animelemtime(2) > 0 && animelemtime(3) < 0
trigger5 = stateno = 315 && animelemtime(3) > 0 && animelemtime(4) < 0
trigger6 = stateno = 400 && animelemtime(3) > 0 && animelemtime(4) < 0
trigger7 = stateno = 405 && animelemtime(2) > 0 && animelemtime(3) < 0
trigger8 = stateno = 500 && animelemtime(2) > 0 && animelemtime(3) < 0
trigger9 = stateno = 505 && animelemtime(4) > 0 && animelemtime(5) < 0
;Mawashi Geri kara-cancel
trigger10 = stateno = 800 && animelemtime(9) < 0

Observe that the triggers that use the buffering are these:
Code:
triggerall = numhelper(10371)
triggerall = helper(10371), var(20)
triggerall = (helper(10371), var(0) = [1,3])  || (helper(10371), var(1) = [1,3])

var(20) is to detect the joystick motion and var(0) and var(1) detect punches, just like in our map!

We can now test and see how the buffering makes our commands easier to do, we can also check ourselves that the reversed commands bug is nowhere to be seen!

With this the very basics of how to use Tiny Buffering are complete! We can now rejoice in our new found buffering life.

Hurray!
Last Edit: August 10, 2017, 05:58:33 pm by Vans