This controller will handle:
- Moving left and right
- Playing the right animations
This controller will handle:
Before this part of the tutorial, read the following article of the manual. It takes about 5 minutes and it will give you the very basic notions you need to know about graphs, machines and macros.
For the player controller, we will use a flow machine.
Select the Player object in the hierarchy, and click Add Component > Bolt > Flow Machine:
On your machine component, you first need to create a new macro for our player logic.
Save the macro under Macros / PlayerController:
The machine component should then look like this:
And the graph window should now display an default graph with Start and Update events:
Now that our machine is created, apply the changes to the player prefab:
We will repeat this process often in the tutorial for the different graphs we create. From now on, if you see "create a new macro for your graph", use the same process that we described here.
We want the character to move left and right, depending on the horizontal input axis. The horizontal input axis is a pre-configured Unity input shortcut that represents, for example, A and D on a keyboard, or the left joystick on a controller. When you go left, it returns -1, and when you go right, it returns +1. How fast to move will be controlled by a speed variable that we can tweak to adjust the gameplay.
If you're not familiar with Unity input, remember you can see and edit the available axes and buttons from Edit > Project Settings > Input:
Let's get started.
Because this is the first time we create a variable and add units to our graph, we'll show you every step along with gifs. But to speed things up for the rest of the tutorial, after this section we'll only give you the steps and the final screenshots.
The first thing we need to do is create the speed variable on our player game object:
For more information about variables, see the manual.
Then, we need to get the horizontal input axis. We can do that with the Get Axis unit.
You can also search for get axis and let the fuzzy finder to the work for you:
You will notice the new unit shows up as dimmed out. This is because we're not using its value anywhere yet, so Bolt warns us that is is currently useless by fading it out. If you want, you can disable this by toggling off Dim in the graph toolbar.
Next, we need to multiply this value with our speed object variable. We can use Bolt's contextual menu to make that easier.
To complete our movement calculation:
Note: Variable units have been improved in v.1.2 after this tutorial was written. They should look slightly different (with dropdowns), but they are functionally the same.
Let's recap what's happening here:
Our player isn't moving yet, but we know by how much it shouldmove.
Note two important things about the new movement variable:
Whew. This was pretty boring, but now you know all the basics: creating graphs, variables and units. Now, we'll pick up some speed and actually make something happen.
The player prefab in the project already has a Rigidbody 2D component attached. All we have to do to make the player move is tell it at which velocity (speed) to go... and we just calculated that!
Velocity on 2D rigidbodies is a 2D vector: it has a X component for the horizontal speed, and a Y component for the vertical speed. Because we only want to affect the horizontal speed, we'll have to keep the vertical speed intact.
Here is the graph we need:
If you have Dim enabled, you'll notice all of this graph is faded out. That's because we never specified when to set the velocity (the first arrow port on the Set Velocity node). Quite simply, we want to do this after we calculate the movement, so we can connect it with our previous set of units:
Now that our graph is getting a bit more complex, it would be a good time to start organizing bits of logics in group. The left part is used to calculate the movement, and the right part is used to set the velocity. By holding Ctrl and dragging, you can create groups than can be labeled:
We're ready to test! If you enter play mode now, you should be able to move the player with the keyboard or a controller:
If you keep the player selected while in play mode, you'll see our graph now animates its active nodes and connections:
Next we'll add a new part to our graph to flip the player in the direction of the movement.
To do so, we only have to change the scale of the player game object on the X axis.
When going right (movement > 0), the scale should be +1, because the sprite is already facing right.
When going left (movement < 0), the scale should be -1, so the sprite gets flipped to face left.
When not moving (movement = 0), the scale should not change, so the player stays in the direction of the last movement.
The Y and Z axes of the scale should remain at 1.
Here is the graph we need:
Then, connect the control input of the Branch node to the control output of our previous Set Velocity node on the left, and add a Flip group to keep things tidy:
If you play now, you should see the player flip:
The last part of our movement graph will be to play the run animation.
The player prefab is already set up with a proper Unity animator controller. This controller has a Speed parameter that is used to transition between Idle and Walk states:
All we need to do on Bolt's end is to pass our movement speed to the animator. Because we're passing a speed and not a direction, we'll need to make our Movement variable absolute before passing it. This way, if we're going left at -5 movement, we'll tell the animator we're going at 5 speed.
To connect this part of the graph with our movement code, we'll need to add two connections:
This way, whether or not we flip the player, we'll still update the animation. Bolt supports connecting multiple control outputs to a single control input for these kind of scenarios.
With an Animator group around or last part, your graph should now look a bit like this when zoomed out:
If you test now, the animations will play properly. Movement code: check. Hurray!
Because our player is a physics rigidbody, implementing jump is as easy as adding an upwards vertical force.
First, create a new float object variable called Jump and give it a value of 12:
Then, below our movement graph, add a new group with the following units:
Just like the Horizontal axis we used before, Jump is a default Unity input for new projects, mapped to Space on the keyboard. If you test your game now, you'll be able to jump!
There are two problems with our jump code:
To fix this, we'll need to create a ground check. This part of the graph will use a raycast. This means we will throw a ray from the player's belly towards the ground, and check if we hit a platform within a small distance. If we did, it means the player is grounded; otherwise, it's in the air.
To make our raycast more reliable, we'll actually use what is called a circle cast. It's basically the same as a raycast, except you can give a thicker width (radius) to your ray. The node we need is called Circle Cast, under Codebase > Unity Engine > Physics 2D:
There are many options for it, depending on what parameters you need. In our case, we need the one with:
Once properly setup, our circle cast should look like this:
To get the layer mask dropdown, search for Layer Mask and choose Layer Mask Literal.
This graph can be read as: "Throw a ray downwards from my position with a 0.3 thickness, and check if it hits an object on the Platforms layer within 1.1 units".
Next, we need to analyze the result. Drag the little target output port to get the contextual menu open, and choose Raycast Hit 2D > Expose Raycast Hit 2D:
This will expose all the items in the raycast hit result. To check whether we actually hit a platform, we only need to check if the Collider is equal to Null (none). If it is, we didn't hit a platform and the player is in the air. If it isn't, we found a platform below our feet and the player is therefore grounded.
You can then use the result of our ground check to limit the jump to when the player is grounded:
Now, the player can't double jump anymore!
We also need change the sprite when the player is in the air. The animator controller is already configured to take a Groundedbool parameter. The only thing we need to do from Bolt is assign it. To do that, we'll copy-paste our ground check code at the end of our previous animator section, then use the Set Bool unit.
Now, if you test your game, the jump sprite should show up when in the air:
In scripting, there is a very important principle called DRY: Don't Repeat Yourself.
What we just did to change the jump sprite broke this principle: we copied and pasted the same part of our graph twice.
Now, if we wanted to make adjustments to our ground check code, we'd have to change two places every time. This may not seem so bad now, but what about when we'll have enemies doing multiple ground checks too? We'd have to update our graphs in three or four or even more places every time we make a change. Yikes.
Fortunately, Bolt provides a way to reuse the same graph in different places called Super Units. We will use super units to turn our ground check graph into a single unit that we will use for both the double-jump prevention and the jump sprite.
First, create a new macro with Assets > Create > Bolt > Flow Macro, and call it GroundCheck:
Then, copy the ground check units from the player graph, and paste them in the ground check graph:
Go back in the player graph, and delete your ground checks. Instead, drag & drop the new GroundCheck macro into your graph. It should appear as a single node, for example like this for the jump:
We have a problem here... Where is the result of the ground check? How can we connect it to the Branch node?
The reason it's not visible is because we haven't created an Output unit in our ground check macro. Let's do that now. From the player graph, just double click the Ground Check super unit to open its full graph. Notice the breadcrumbs in the toolbar let you navigate in nested graphs easily:
Next, add an Output unit, located under Nesting, and select it to display its graph inspector:
We will add a Value Output for the result of the ground check. Set its key to groundedand its type to Boolean. If you want, you can give it a label and summary to add documentation to the graph inspector. Lastly, connect the new grounded port with the result of the ground check.
Navigate back to the parent player graph using the breadcrumbs in the toolbar. You'll see the super unit now has a grounded output port. Use that to connect it for both the double-jump prevention and the jump sprite animation:
If you play your game now, nothing should have changed. But your graph is now a lot cleaner, DRYer, and easier to maintain for the future.
Finally, apply the changes to your prefab so that the object variables like Speed and Jump we created are automatically added on other player prefab instances in the other scenes.
This concludes the character controller. In this part, you learned how to:
Whew! The ice is broken, and you now know most of the basic concepts you need to use Bolt. Take a moment to review what you've learned, make sure you understand, and stretch your legs. Next up, we'll be adding a death mechanic to our games.
Let's start by getting our project ready.
In this part, we will add a death mechanic when the player touches the spikes.