4. Level Change

In this part, we'll add level switching when we reach the red flag at the end of a level.


1. Create the flow machine

  1. Select the Objective object in the scene view. 
  2. Add a flow machine component
  3. Create a new macro for it named Objective
  4. Apply the changes to the prefab
  5. Delete the default Start and Update units

2. Detect player collision

The first thing we need to do is detect when the player reaches the red flag. All that's needed for this is a collision check, like we just did for spikes:


But hey, that's copy-pasting! We'll try to avoid that to keep our graphs DRY. We'll create a new super unit that handles checking for collision with an object of a given tag, and telling us whether we hit or target or another object.

Cut and paste these units in a new macro called OnCollisionWith, and add Input and Output units, both found under Nesting.


On the input node, add a string value input for the tag to check:


Make sure you check the Has Default Value option, allowing us to use inline values in the super unit. 

On the output node, add three outputs:

  • A control output in case we hit our target
  • A control output in case we hit another object
  • A game object value output for the object that we hit


Then, connect the ports on the input and output units to their matching ports in our collision check graph, like so:


Going back to our Objective graph, we can now simply drag & drop our new macro to create a super unit, then fill in the Tag field with Player:


If you want, you can also replace our previous collision detection on the Spikes graph with the new super unit. Look how simple it becomes then!

Keeping your graphs DRY might seem like overkill at first, especially for simple logic like this. But it's important to remember that as your game scales, you'll need to do this to keep everything easily maintainable. 

We're currently working on making this a one-click operation: select the units you want, then click a button to turn them into a super unit and save it as a macro. Stay tuned for updates!

2. Load the next level

Now that we have a solid, reusable collision detection, we just need to load the next scene. But because we'll be using that same Objective prefab across all our levels, we need a variable to configure which scene is the next. Add a new string object variable called Scene, and give it a value of Level2:


Then, apply the changes to the objective prefab so that they all have a Scene variable.

Later, in other levels, you'll need to change that variable to the name of the next scene. For example, in Level2, you'll select the Objective object, and change its Scene variable to Level3.

Finally, to load the level, simply connect the collision super unit with a Load Scene unit:


3. Unlock the next level for later

Later in this tutorial, when we create the main menu, we'll want only the levels that the player has entered at least once to be available for replay, so that they can't skip to the last level before having beat the previous ones.

To do that, we'll be using saved variables. This is a powerful feature of Bolt that acts as a built-in save system: any content you place in a saved variable will be persistent across playthroughs, even if you exit the game.

The convention we will be using is to have one saved variable per unlocked level. It will be a boolean (true or false) indicating whether the level is unlocked, and its name will be LevelX_Unlocked, where X changes for every level.

When the player reaches this objective, the next level should get unlocked. In order to do that, add the following nodes to your Objective graph:


Concat is located under Codebase > System > String. It takes the arguments you pass to it and joins them together. For example, here, the output of the concat unit will be Level2_Unlocked, which we use as the name of the saved variable.

Finally, tie up everything together in the graph with groups. 


If you test your game now, you should switch to level 2 once the player reaches the red flag:


You should also see a new Level2_Unlocked variable appear in the Saved tabs of the variable window:

This article was helpful for 30 people. Is this article helpful for you?

+1

minor glitch for me

I had to change the New string object variable ‘Name’ to ‘Scenes’ Also the two 

‘Object Get Variable’ to ‘Scenes’ to get this to work  

This may save someone else time if they have the same problem

Hi, I don't know how it is for others, but is it normal that I will stand on the Flag? I know because of the collider. Wouldn't it be better to turn the collider into a trigger?


In my opinion making the flag a trigger won't be much of a difference since what the code does is, the frame you touch it, it loads the next scene.

Yeah, you are right. Was before I build in the functionality ;)

Saved variable is what? XML data, Scriptable Data or PlayerPrefs?

JSON serialized into PlayerPrefs.

Switching from level 1 to level 2 does not work well.

I went from Level 1 to Level 1, which I did in the test.

I wonder what made a mistake ...

Self-solved by registering in the build setting

All scenes should now be included in the build settings by default with the new project zip.

May be another bug. When I get to next level and jump I get InvalidOperationException: Variable not found: 'Jump'. Bolt.VariableDeclaration.Get(System.String variable) (at c:/User/Lazlo/Projects/Ludig/.... and so on VariableDeclaration.cs:60)

I checked my Input Manager for Level2 and Jump is setup. On Version 1.2.1 due to the other bug I found in my early post.

Thank you

That sounds like a complaint that it can't find the variable "Jump" on your game object, not the input manager.  You created a Jump variable on your object in section 2.7.  Does it still look the way it should?

hello


can you guys help me =(


its says : Graph input cannot be used outside a super unit

this is the error i got and it never switch to the next level =(

A image of how you coded it will be useful.



here ... i hope its clear image

I'm having the same problem mentioned above. "Graph input cannot be used outside a super unit."

Here's a screenshot of my Objective graph. As I understand, it should be loading the next level, but it doesn't. 
Also, it doesn't seem to save the Level2_Unlocked variable. Can anyone help? I'm happy to provide more info.

+1

Shoutout to Satsukeshi on the Bolt Discord #help channel for the solution.


Just have to make sure that the correct macro is set in the Flow Graph component. 

That gets rid of the error and loads the next level. 

+2

Ready to have your MIND BLOWN!    My post here is all about "Levels".

While I wanted to follow the tutorial down to the fine-details, I also wanted to make sure I could understand how Bolt worked so I went off on my own tangent while still performing the tutorial steps (kind of making it a little bit or mine, a little bit of theirs - so I could learn better).

If anyone has a better way of doing anything I'm posting below, by all means reply back to me :)  I think we're all here to learn Bolt and to start making games, and I don't want to derail anyone else - So I really hope this helps.

So I got ridiculously out of control and made what you see below for handling all of my Level needs, even though it's sort of in the tutorial but my updates are overkill compared to the tutorial requirements.

Here's what you're seeing:

1. My Macros Folder.

2. A folder I created for my Level Macros.

3. A list of the Macros I created for handling anything/everything I want for levels.

4. Is it overkill ? Probably. But I'm thinking as a programmer and re-using Super Units (Macros) in an extremely DRY way (I guess that's how it's put in the tutorial, DRYer).

save image

Here's what a few of these Macros look like:

Notice how I am Grouping even when I'm using just 1 Macro. This isn't necessary, but it provides a great way to add a description to what I am seeing without looking at the tiny text in the top left corner (the file name). The first thing I do when I make a new Super Unit (Macro) is I make a new Group.

"Level Exists"   This checks if a Level Exists, by passing the Level # in through Input, into a Comparison, and also pulling in an Application Variable I set with the Max # of Levels. How this is handled for your game may be different, but for now I'm in Tutorial Mode.

My anticipation is that I will receive Output if it exists, and no output if it does not -- As a Boolean (which should always be True if I get something out of it).

save image


And here are a few more screenshots:

This will take the Level #, turn it into a string (yeh unfortunately, Numbers Cannot Be Strings in programming, so you must convert them). It's then Concatenated to tell the Scene Manager to load a Scene called "Level2"  (if I passed in 2 as my Level).

save image

This one probably needs review by the creators of Bolt - Because I'm probably not using this correctly.... Just look below at the "SELECT" box (to the right) how I connected a FALSE boolean into it. I did this because I have nothing that can give it a false as there's no comparison. I could have built a Not Exists into my "Level Exists" Super Unit, but I feel like that's a lot of extra work just to determine the result is Not what I am expecting. This will take some practice to learn how to handle correctly.

save image

This is a basic Super Unit for reloading the current scene (used for the Death -> Spikes Super Unit Macro). Nothing special here, but who's to say Death is the only way to restart a Level ?

save image

This may be a bit farther ahead in the Tutorial, but you can see that I stopped here to plan all of my Level Macros in good conscience knowing I would need them even if the Tutorial hasn't gotten to them yet.

I am basically checking if a Level is "Unlocked", and if it is, to Load the Level.  Again - God knows if I'm doing any of this the right way - The game seems to work though.

save image

There's a few more, such as the "Level Unlock", etc. But you get the idea. I think this is the behavior Ludiq wants from their users is to take the tutorial and have their minds blown when they finally learn how it works to the developer's benefit.

Hope this helps inspire people :)

+1

Also I wanted to add that using "SNAP TO GRID" for your Graph settings makes things 100x easier to organize, especially if you have OCD? I'm pretty sure OCD is a byproduct of being a developer or designer, so you should look for Snap To Grid.. 

Here's how you Snap To Grid:

Edit -> Preferences -> Luqid

save image

@Ludiq.io -- Typo in my last post, I put "Luqid" instead of "Ludiq" - Can you guys edit that and delete this reply? Sorry :)

Hi ! I have a problem here. When i hit the flag, my game set pause and appear the next message :

Scene 'Level2' couldn't be loaded because it has not been added to the build settings or the AssetBundle has not been loaded.

I checked if it was on my build settings, it's good, and he also is in my project. Why it's happening ? Thanks for your answer !

+2

Tris:  I was able to fix this by moving all of my scenes into the main list (where Level 1 is) and then clicking "Unload Scene" on everything but the one I am working on.

Another way I was able to fix this was going to Build Settings and clicking "Build".

I hope someone has a better way to fix this, it's really weird.

+1

"Another way I was able to fix this was going to Build Settings and clicking "Build"."

This indeed helped me to fix the issue. Thank you. 


+1

Just wanted to chime in and say that this worked for me.

Also, I didn't understand what you meant when I read it the first time and had to do some more tinkering before I figured it out. Here's a more detailed description:

You have to drag the "Level2" Scene object from the Scenes folder onto the Hierarchy pane. Then everything in Level2  shows up in the Scene and Game panes, right on top of the Level1 stuff. To make the Level2 stuff disappear from the Scene and Game panes until you load it during play, click the little hamburger dropdown menu icon to the right of its name in the Hierarchy pane.

Here's a screenshot that should make it quite clear.


+1

I built it and it's now working thank you ! I agree with you it's not very logic to build the entire game... Does it affect computer capacity in case of it's a big game ? In all cases, thanks a lot, you just made my day ! Sorry for my english, Vive la France, thanks again, and i go to practice again ! :D

After loading Level2 scene a have such exception. A did all steps by instruction. Whats about debugging such errors? Sory for my english.

ArgumentException: An element with the same key already exists in the dictionary.
System.Collections.Generic.Dictionary`2[UnityEngine.SceneManagement.Scene,Bolt.SceneVariables].Add (Scene key, Bolt.SceneVariables value) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:404)
Ludiq.SceneSingleton`1[Bolt.SceneVariables].Instantiate (Scene scene) (at C:/Users/lazlo/Projects/Ludiq/Ludiq.Core/Runtime/Unity/SceneSingleton.cs:120)
Ludiq.SceneSingleton`1[Bolt.SceneVariables].InstanceIn (Scene scene) (at C:/Users/lazlo/Projects/Ludiq/Ludiq.Core/Runtime/Unity/SceneSingleton.cs:74)
Bolt.SceneVariables.Instance (Scene scene)
Bolt.VariablesWidget+Tab.Scene () (at C:/Users/lazlo/Projects/Ludiq/Bolt.Flow/Editor/Variables/VariablesWidget.cs:339)
Bolt.VariablesWidget.Update () (at C:/Users/lazlo/Projects/Ludiq/Bolt.Flow/Editor/Variables/VariablesWidget.cs:37)
Bolt.VariablesWindow.UpdateWidget () (at C:/Users/lazlo/Projects/Ludiq/Bolt.Flow/Editor/Variables/VariablesWindow.cs:86)
Ludiq.GraphContext.set_current (Ludiq.GraphContext value)
Ludiq.GraphContext.EnsureIntegrity (Boolean retraverse)
Ludiq.GraphInspectorWindow.OnGUI ()
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222)
Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232)
System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115)
UnityEditor.HostView.Invoke (System.String methodName, System.Object obj) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:295)
UnityEditor.HostView.Invoke (System.String methodName) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:288)
UnityEditor.HostView.InvokeOnGUI (Rect onGUIPosition) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:255)

Hi Kishir,

Are you on Bolt v.1.3? This is an issue that is supposed to be fixed in the latest release.

Kishir:

I posted a link in Discord to see if someone could come help you.

I've poured over the tutorial, but at no point can I see the section where we change 'Level2' to the variable that holds all the levels.  Can you help please?