We have duplicated the FloatAbs.cs, made a new file FloatCeilingExample.cs, have fixed the class name, and added a new float variable, renamed it. We cleared out the other actions in PlayMaker and added FloatCeilingExample. We've first cover a lot of ground to make sense of the code.
Some Conventions to KnowBefore we go further. Let's see what also happened. Open up the action we've made (FloatCeilingExample.cs) from within Unity. Visual Studio should pop up or still be open.
Right under the class, there is always a list of variables that are identical to the ones you see in the action in PlayMaker. Compare. The variable name corresponds to the name next to the variable field, but interestingly
floatVariable in code is displayed as
Float Variable in the action. And storeResult becomes
Store Result.
The programming language is called c# (pronounced ”c sharp”), and the convention for variables commonly used is called camelCase. There are other conventions e.g. snake_case, Hungarian notation etc (see Wikipedia). We should stick to that camelCase convention, at least in PlayMaker. Important is also that <space> separates instructions, so you cannot use <space>
within a variable name. As you saw, the space is automatically displayed before each uppercase letter in the action.
Another important wrinkle is that variable names are always case-sensitive. foo, Foo, fOo, foO, FoO and FOO are different variables as far as the code is concerned. If you want to rename a variable you'll use in the script, hold CTRL and tap R twice, you can then rename all instances of the variable. Needless to say, you should be careful with this, but you cannot break much when you work in your own file, and saved recently.
Line breaks (i.e. new line) are ignored by the code and are treated as <space>. So you could theoretically put the whole code into a single line. Thus, line breaks are used to make it look nice.
There's is an exception and that are comments. Everything in the
same line after // is seen as a comment ("outcommenting") and ignored by the code. You'll often see this, and it's a good habit to comment non-obvious things.
DO NOT SAVE THE FILE FROM NOW ON.
We're playing around a bit.
The BracketsI often get confused by the many brackets. So let's help ourselves with it. Go to the very bottom of the script to the last } and write
// end namespace after it. The bracket right above it marks the end of the class. So you can write
// end class in a similar fashion behind it, then you'll know that these should always be closing the script.
Visual Studio also helps you identify bracket-pairs with the dotted lines. To align things nicely, use TAB or SHIFT+TAB. You can of course indent entire blocks in either direction. Finally, you can also see which pair belongs together by going with the cursor next to it, or clicking on it. It should mark the pair.
As you've noticed already, there are quite a lot of brackets, which provide the structure of everything. So the class is "inside" the namespace. Then there is stuff "inside" the class. The meaning of the brackets is roughly like this:
{} is like a container for functions/methods.
() typically follows a function name and is a container for variables, so-called arguments that are passed into it.
[] are typically for properties in the editor or inspector elements, for requirements, tooltips, to add sections etc. And then there's also...
"" which is for strings (i.e. words that aren't interpreted as code).[/li]
So first, we are inside the class, which is inside the namespace. Hence, there are the first and last two curly brackets. Then you'll see the list with the variables and below that, finally, where the magic happens. The so-called functions or methods.
Functions?This action has four on a first glance: Let's count them: 1. Reset(), 2. OnEnter(), 3. OnUpdate(), and 4. DoFloatAbs().
Let's try to understand what's going on there. First off, you'll see that they start uppercase and followed by this (). This means that they do not take in arguments, but instead will simply use the variables that are listed above, or made up variables that are only used inside them. Writing functions with arguments is not too difficult, but you don't really encounter this with PlayMaker actions. Basically, they are empty hence (). When they return no particular value or result, they are called
void, which is a keyword you often see.
Great many functions look like this, and now you can also spot that there are more than these four instances. Let's count them quickly, too.
Within OnEnter(), we see DoFloatAbs() and it's exactly the one that is also written out below. When the code encounters this reference, it looks into the same script and executes the function as specified below. If we were to change this, we needed to change it in all places within the script. Since this exists there, it just runs the code. Let's play around with this.
Copy that line inside the function, with CTRL+C
floatVariable.Value = Mathf.Abs(floatVariable.Value);
then mark the
DoFloatAbs();inside OnEnter (in line 37), and replace it. This should show no errors. Basically instead of saying "Go find DoFloatAbs and then run it, which means run "floatVariable.Value = Mathf..." we directly say run this line of code. We skip a step.
But you'll see that
DoFloatAbs();exists a second time inside OnUpdate, too. If we really wanted to remove
DoFloatAbs(); altogether, we would have to plug the line of code there, too, duplicating the logic and that's a bad idea. If we made changes, we would always have to apply the changes to both locations, and that's not ideal. That's why the logic is usually inside its own function and can then be called from various places whenever needed.
The OnEnter version is executed when the state is entered, and the OnUpdate one is called when everyFrame is ticked. Both of them are predefined by PlayMaker or Unity. All you need to remember is that there are different "places" when a function could be executed, which also reveals how often it is executed. Beside these two, there are several more. Of those, you might run into FixedUpdate (typically used for physics) and LateUpdate (afaik for rendering or position updates). There’s also OnExit which executes its stuff when leaving the state. You can thus easily make custom actions for leaving the state (send event when leaving is pretty useful). In regular scripts, OnStart and OnAwake are common.
The IF StatementSpeaking of which:
if (!everyFrame) also looks like a function, and it has an argument it uses. Remember, that's the round bracket. For now we chalk it up as a built-in function, which comes as part of c#. If and else often come up, and they work with boolean logic. The ! before a variable means "is false". Without the exclamation mark means "everyFrame is true". These are shorthands. Let's change that quickly to test it.
Edit: complementary video
Replace the part inside the bracket to everyFrame = false, and some green underlining should appear. Something is not going to work, but the error is not screeching the script to a halt. To express a check that something is exactly equal, we need to fix it by writing everyFrame == false. It should also tell you when you hover over it. Consider when you want to express something is equal or bigger, you'll write >= for instance. Likewise variableA = variableB means "set variableA to the value inside VariableB". That's why it complains. We want to compare.
But now you already know the basics of if statements. To ask
this AND that, use && and for
this OR that, use ||. For example, to check if it's a good day for swimming: if (sun && !clouds) sun is true AND clouds are false. Another example: good day to hike? if (temperature <= 15° celsius || !rain), means it's relatively warm OR not raining. A common check beside this operators is a null check. You can ask if a variable has been set at all by asking if (myGameObject == !null) meaning: there is something stored as myGameObject (it is not null).
Finish() is another function in there. It’s also PlayMaker specific. When this is executed, the action tells the state that it did the thing, and if all actions in the state say this, the state says it’s finished. This is how the Finish event works, which is then triggered. That’s also the secret behind everyFrame in OnUpdate. You see, when everyFrame is false, Finish gets triggered, and thus it doesn’t execute it after this.
The MathF FunctionAnd finally, there's yet another function in use: You'll see
Mathf.Abs(floatVariable.Value). There's apparently a function somewhere which takes an argument, inside the () and it looks for a floatVariable from above.
Let's try something. Go above and find the line
using UnityEngine; and delete this. Now scroll down to the Mathf.Abs part again, and see it's now with red errors. This means, now that the using... statement is gone, the script can no longer find the function that is apparently part of the UnityEngine kit.
To be continued below...