Tutorial:Moving_from_GML_to_EDL - hpgDesigns/hpgdesigns-dev.io GitHub Wiki
EDL (ENIGMA's development language) is practically completely compatible with GML. This means you can use all your existing GML code in ENIGMA and EDL will handle it exactly the same. However there are extra features to the EDL language, mainly inherited from C++, which allow you to write your code more efficiently and can provide extra functionality not available in GML. There are also options you can set in the #ENIGMA Settings Panel in LateralGM, which, among other things, will make certain syntax features behave more like C++ instead of GML.
A lot of C++ functionality has been added to EDL. By using ENIGMA it will give you an ideal platform for learning C++ by simply expanding upon your existing gml knowledge. You can start to incorporate these new techniques at your own pace and eventually have a much fuller grasp of the C++ language.
Variable type declarations is one of the main things added to EDL which will allow you to massively improve the efficiency of your code. In GML only two different types can be used: string variants and real variants. For example:
my_text = "word"; //Assigning a text value declares a variable as a string variant type.
my_value = 10; //Assigning a number value declares a variable as a real variant type.
my_color = $FFAA00; //Assigning a hexadecimal value also declares a variable as a real variant type.
However in EDL you can assign variables using all c++ data types. For example, the above could be rewritten as follows:
string my_text = "word"; //Implicitly declared as a string type.
short my_value = 10; //Implicitly declared as a short type.
int my_color = $FFAA00; //Implicitly declared as an integer type.
This is much more efficient (both memory and speed), but typed variables
can't change types (you can say myvar = "a"; myvar = 2;
but you can't
say string myvar = "a"; myver = 2;
). See the data
types article for more information.
The scope of the variable can also still be set like so:
int my_integer = 10; //would declare in the scope of the current code (like var does it gml).
local int my_integer = 10; //would declare the variable local to the current object (like gml naturally does with plain assignments).
global int my_integer = 10; //would declare the variable global (like global. does in gml).
...
There are also some features which have been added to EDL which do not exist in GML or C++.
This panel is located either by double clicking the ENIGMA Settings tree node, or by going through the menus, ENIGMA > Settings. In here, you can change a number of ways that the parser treats code and other things. Note that these settings are dynamically loaded in from an eYAML file, and not hard-coded, meaning that they are subject to quick change. Unfortunately, it also means that they cannot be translated at this time, aside from changing the strings yourself.
Note that many of these settings will break GML compatability if used in-code. For example, if you use \n for newlines in ENIGMA, don't be surprised when you try to port your game back to Game Maker and see literal backslashes followed by n's appearing in your drawn text in place of newlines.
Most of these settings are self-explanatory, but for a refresher, here's what they do:
- Inherit strings from GML ('A'="A") or from C++ ('A'=65)
In C++, individual characters ('A') are a native type that resolves directly to a number (their ordinal value), whereas strings ("A") conclude with an invisible null terminator (\0). In this way, they are treated differently. The benefit of this is that, if you actually want the ordinal value of of character, you may simply name the character, and don't need to pass it through the ord() function. On the downside, 'A' != "A", because char and string don't compare right.
- Inherit escape sequences from GML (#) or from C++ (\n)
This is primarily a visual effect when drawing text to screen. For more information, see Drawing text. In GML, to create a newline, you simply insert a number sign (#) into your string. To draw a literal number sign, double it (##), thus # acts as a kind of escape character. In most C-derived languages (C++, Php, Java), we're more accustomed to the backslash (\) being the escape character, where a newline is \n and a literal backslash being doubled (\\). Note that the C++ option will give you more escape sequences (e.g. \t for a tab and \0 for a null character - useful things for file/stream writing), but also remember that the option is entirely not available in Game Maker, meaning that using escape sequences like such will make your game incompatible with Game Maker.
- Inherit ++/-- from GML (+) or from C++ (+=1/-=1)
In GML, stringing operators is not overloaded, meaning that they act on each other, rather than the parser. For instance, take the following piece of code:
a = 10;
b = --a;
Here, we have a variable a
storing the value 10. Then, we apply a
prefix -- to it. In GML, this gets interpreted as
<whatever is before the -- (0)> <value of a
(10)>. The result is that a = 10; b = 10;
. Recall that a double
negative becomes a positive. In C++, double operators like -- and ++ are
overloaded, meaning that they are treated specially, and actually
increment (++) or decrement (--) the value of the variable that they are
applied to. Also, prefix (--a) vs postfix (a--) determines if we
consider the resulting value or the previous value, respectively, for
the rest of the equation/line of code. In the above code, we have prefix
--, which means we decrement a
by 1, and then apply the result to
the b =
. The results are a = 9; b = 9;
. Note that if we
had done postfix instead, the results would have been a = 9; b = 10;
.
- Inherit a=b=c from GML (a=b==c) or from C++ (b=c,a=c)
This setting deals with the way the equals sign (=) operator is treated. GML does a fancy interpretation based on the lexical context. In an expression (such as if it were appear on a line of code by itself), it sets the left operand to be equal to the right operand, the same as C-derived languages (thus, it is an assignment operator). In a conditional or evaluation, (like in an if () statement or as part of an operand as in the example a=b=c), it is treated as a boolean comparison (returns whether both operands are equivalent, without modifying them), similar to the C-derived double equals (==) operator. In most other languages, we have separate symbols to indicate whether we wish to perform an assignment (= in C, := in Pascal) or a comparison (== in C, = in Pascal), meaning that, in C, if you say if (a = b), you actually mean "set a equal to b, and test if the resulting value evaluates to true (non-zero)", which can easily confuse a newbie. For example, take the following piece of code:
a = false;
b = true;
if (a = b) { /* do something */ }
In GML, a comparison will be made and will resolve to false (false != true), and the 'do something' branch will not be executed. In C, the user might be quite surprised to find that after the conditional, two things happen: a is now suddenly true, and the 'do something' branch executes. As such, it's usually encouraged for newer users who don't know their = from their == to use the GML setting, while more experienced users who know or have use for the difference (e.g. reading input from a file, if (a = fread()) - which stops executing the branch when the fread returns null, indicating that the stream is empty) would use the C++ setting.
Note that this says nothing of the special Pascal := and C-derived == operators, which are exclusively for setting and comparing, respectively.
- Treat literals as EDL (variant) or C++ (scalar)
This is one of the finer points of C++ and an understanding of pointers
and strings. EDL alleviates much of the stress of dealing with strings
by abstracting away the pointers and the C artifacts that C++ exhibits.
This especially stands out when we consider two cases: "wo" + "man";
and "woman" + 2;
. In the first case, normally we'd expect a result of
"woman", but in the C++ world, it's adding two pointers together, which
doesn't make sense, and thus errors. In the second case, we might expect
"woman2", but in the C++ world, we'd expect the pointer to move over 2
characters, resulting in "man".
In brief, this determines whether a literal, such as "string", and 0.5,
are stored in a variant, or in their C++ native
scalar types. Another effect of this is that we can use variants in
conditionals, so things like if (0.5)
will work.
There are still a few small inconsistencies between GML and EDL, these are listed on the discarded behaviors page. Since ENIGMA is still in development there is also obviously still a lot of functionality that needs to be completed for full compatibility, you can find these things listed on the ENIGMA:Todo page.