Data Types
M.U.G.E.N only provides two standard data types to users who are coding characters: signed int
and float
.
Supported Bitwise Operations in M.U.G.E.N
Since M.U.G.E.N is written in C, users are given most of the bitwise operators that C has, with the exception of the shift operators (which can be covered). M.U.G.E.N provides the following bitwise operators:
- & - Bitwise AND
- | - Bitwise OR
- ^ - Bitwise XOR
- ~ - Bitwise NOT
Reading the Wikipedia article on C Bitwise operators is recommended. This explains everything one needs to know about their usage.
Common Data Types
Bit
A bit is a unit which represents one of two possible values (1 or 0, on or off, true or false). A value that is either 0 or 1 (false or true) is also called a Boolean
(or bool
). These terms are not interchangeable and in fact, the representation of a Boolean
may be more than one bit, but for the simplicity of this article, a boolean Boolean
value is considered 1-bit.
Byte
The second unit to know in computing is known as a byte
, which is composed of 8 bits. A byte can hold up to 256 unique values ([0,255] for unsigned, or [-128,127] for signed).
Short
The third unit is 16 bits, or two bytes, and is known as a short
. A short
can hold up to 65536 possible unique values. The fourth unit we're concerned about is the simple integer
(or int
) type, which is 32 bits and can hold up to 2147483648 unique values.
Float
There are other integer
types, but of course, there are also float
types we need to use to deal with real numbers and fractions. A float
can represent many unique values and they are also 32-bit.
Problem
Users are only given 60 int
variables and 40 float
variables to work with. This is done so that memory can be managed more efficiently, but at the same time, users are limited in terms of how many variables they can declare.
Solution
One 32-bit variable can be split into several variables when utilizing only the bits necessary.
Example
Felicia by Jesuszilla's Cat Ride makes use of this for the bind position for her custom state. The following code fits an x coordinate and y coordinate into one int
variable.
[State -2, Cat Ride Pos] type = VarSet trigger1 = Anim != 9545 trigger1 = Enemy, Name = "Felicia_LG" || Enemy, Name = "Felicia_Y" trigger1 = StateNo != 132 && StateNo != 155; Air guard trigger1 = StateNo != [5000,5999] ; Gethits sysvar(0) = (14 & 65535) | (-100 * 65536) ; Store (14,-100) into sysvar(0)
Normally, two float
variables are used to store an X and Y position, but since this is dealing with sprite binding, two short
variables work perfectly for this use case. This is because the chances of a sprite being larger than 65536 pixels in width or 65536 pixels in height is slim to none. Most GPU's can't even have textures larger than 2048✕2048px (which is considered a safe value in this era due to the graphics limitations of tablets)[1]. By limiting the data size to only the bits necessary, more efficient use of memory can be made.
The operation can be reversed to retrieve the individual X and Y components like so:
[State 60, VarSet] type = VarSet trigger1 = NumTarget(60) var(39) = Target(60),SysVar(0) [State 60, BindToTarget] type = BindToTarget triggerall = NumTarget(60) && Var(39) trigger1 = target(60), SelfAnimExist(9545) pos = ceil(((var(39)&65535) | ifElse(var(39)&32768,-65536,0)) * target(60),Const(Size.XScale)), ceil((var(39)/65536) * target(60),Const(Size.YScale)), Foot
Although this provides readability issues, there's really no way around it, and this is actually the more readable version that uses Var(39)
to temporarily store the value of the enemy's Cat Ride position.
For the x component, the bitwise AND of the first 15 bits is taken. The 16th bit, which is known as the "sign" bit, is then checked in the ifElse
statement. The sign bit is only used in what's known as "signed" values. A signed value is one that can be either positive or negative. A 1 for the sign bit indicates that the value is negative, whereas a 0 indicates that it is positive. The bitwise OR is then taken with -65536 so that it converts a signed 16-bit short
back into a signed 32-bit int
with the sign intact that M.U.G.E.N can understand.
Retrieving the y component is much simpler. The original value is divided by 65536 in order to mimic a right arithmetic shift by 16 bits. In most languages, the binary shift operator, >>
, would be used. However, M.U.G.E.N has no shift operations, so multiplication by powers of 2 must be utilized (x*(2**y)
) for shifting x
left by y
bits, and division by powers of 2 (x/(2**y)
) for arithmetic right shifting x
by y
bits.
Any data type smaller than 32 bits can be recreated using bitwise operations. This means that for a single integer
, up to 32 Boolean
values can be defined
[State -2, Flags] type = Null trigger1 = e||(var(0) := 1*(2**0)) ; Set 1st bit of var(0) to 1 trigger1 = e||(var(0) := 0*(2**1)) ; Set 2nd bit of var(0) to 0 ... trigger1 = e||(var(0) := 1*(2**30)) ; Set 31st bit of var(0) to 1 trigger1 = e||(var(0) := 1*-2147483648) ; Set 32nd bit of var(0) to 1
The 32nd bit utilizes the value -2147483648 rather than (2**31). This is due to the limitations of the max value for a signed 32-bit int
. 2**31 would return 2147483648, which requires at least 33 bits when signed. Since this cannot fit in 32 bits, the two's complement value must be used directly.