DOWNLOADS MANUAL TUTORIALS SIGN IN

Breakout

Image Loading...

Introduction


In this tutorial we will build a Breakout clone using the GOTO Logic framework.

The following mindset is crucial:

  • Coder writes C# to create reusable building blocks: Actions and Comparers
  • Designer uses the building blocks to visually create Action Sequences and Logic Graphs

A minimal set of building blocks are built-in, but the rest is up to you. As a professional developer, you will know what is best for your game and your team.

Let's get going!

Startup


You need to have GOTO Studio installed and then download the startup package that has the tutorial assets and a scene.

Open the included /Examples/Breakout/Scenes/breakout.scene. When playing the scene, you will notice that level is generated. A ball, paddle and score label is also setup.

Simple Action


Let's start by making the ball move.

  • Select the Breakout/Ball game object from the hierarchy
  • Add new Logic Player component for it
  • Press Create New button and select Sequence from the dropdown
  • Save the new asset as Examples/Breakout/Logic/ball.asset (if folder doesn't exists, create it)
  • To the Actions list, add Value -> Set action by clicking the + button
  • Drag'n'drop Ball game object from hierarchy to Variable field
  • Select Rigidbody2D/velocity from the dropdown
  • Set value to (-10, -10)

If you press play, you'll notice that the ball is moving.

(Don't worry about the paddle floating left. It's just physics :))

Image Loading...

Simple Reaction


Ball is colliding with our player paddle below. We want to play a sound when the ball hits the paddle.

  • Comparer: Did we collide?
  • Action: Play sound

Using these two elements, we can make this happen.

Custom Comparer


The question is: Did we collide? Let's create comparer to answer that question.

  • Create new folder /Examples/Breakout/Comparers
  • Open the folder
  • Right click empty project view for context menu
  • Choose Create -> GOTO -> Logic Comparer (template)
  • Choose the name CollisionComparer.cs and press save

Copy-paste the code from the right into this new script file.

using System;
using UnityEngine;

namespace GOTO.Logic.Comparers
{
    [Serializable]
    public class CollisionComparer : Comparer
    {
        private bool collided;

        protected override void OnExit()
        {
            collided = false; // Reset cache
        }

        public override bool Compare()
        {
            return collided; // Return the cached value
        }

        // This is just like MonoBehaviour.OnCollisionEnter2D
        protected override void OnCollisionEnter2D(Collision2D collision)
        {
            collided = true; // Cache
        }

        // Let's support triggers too
        protected override void OnTriggerEnter2D(Collider2D collider)
        {
            collided = true; // Cache
        }
    }
}

We now have our comparer. Let's put it in place.

  • Select the Breakout/Player game object from the hierarchy
  • Add new Logic Player component for it
  • Press Create New button and select Sequence from the dropdown
  • Save the new asset as Examples/Breakout/Logic/playercollide.asset
  • Set Play Mode to Once And Reset
  • In the Condition list, add Comparer -> Collision comparer clicking the + button

We have our condition setup. Now we add the action. Fortunately Play Audio action is built-in.

  • To the Actions list, add Audio -> Play Sound clicking the + button
  • Set Audio Clip to /Examples/Breakout/Audio/paddlecollide.wav

Now if you press play, you should hear a "blip" sound when the ball hits the paddle.

Image Loading...

Custom Action


Because Unity physics system sometimes misbehaves, we want to clamp the ball velocity to a max value. We shall create a custom action to accomplish this. Let's be generic and clamp any vector (not just velocity) for extra reusability!

  • Create new folder /Examples/Breakout/Actions
  • Open the folder
  • Right click empty project view for context menu
  • Choose Create -> GOTO -> Logic Action (template)
  • Choose the name ClampVectorAction.cs and press save

Copy-paste the code from the right into this new script file.

using System;
using UnityEngine;

namespace GOTO.Logic.Actions
{
    [Serializable]
    public class ClampVectorAction : Action
    {
        // Vector we are clamping
        public Value vector = new Value(typeof(Vector2));
        // Max magnitude for the vector
        public Value maxMagnitude = new Value(typeof(float));

        protected override void OnStart()
        {
            // Read values from variable system
            var vec = vector.GetValue<Vector2>(this); 
            var max = maxMagnitude.GetValue<float>(this);

            // Clamp the vector
            var clamped = Vector2.ClampMagnitude(vec, max);

            // Set the new value back to variable system
            vector.SetValue(clamped, this);

            // Inform sequence player that the action is done
            SetFinished();
        }
    }
}

We now have our custom action. Let's put it in place.

  • Select the Breakout/Ball game object from the hierarchy
  • Add new Logic Player component into it (there is already one, but let's add another)
  • Press Create New button and select Sequence from the dropdown
  • Save the new asset as Examples/Breakout/Logic/ballvelocity.asset
  • Set Play Mode to Loop
  • In the Actions list, add Action -> ClampVector action clicking the + button.
  • Drag'n'drop Ball game object from hierarchy to Vector field
  • Select Rigidbody2D/velocity from the dropdown
  • Set Max Magnitude value to 2

If you play the game now, you will notice that the ball speed is clamped to 2 (slower than before). For the sake of playability, let's set it a bit higher.

  • Set Max Magnitude value to 15

Image Loading...

Keyboard Comparer


Next we will need a way to check against keyboard state. Let's create a new custom comparer.

  • Open the folder /Examples/Breakout/Comparers
  • Right click empty project view for context menu
  • Choose Create -> GOTO -> Logic Comparer (template)
  • Choose the name KeyboardComparer.cs and press save

Copy-paste the code from the right into this new script file.

using System;
using UnityEngine;

namespace GOTO.Logic.Comparers
{
    [Serializable]
    public class KeyboardComparer : Comparer
    {
        public KeyCode key;

        public override bool Compare()
        {
            return Input.GetKey(key);
        }
    }
}

Logic Graph


It is time to make our player move. Let's give birth to our very first Logic Graph!

  • Select Breakout/Player game object from the hierarchy
  • Add new Logic Player component
  • Click the Create New button and select Graph from the dropdown
  • Save the graph as /Examples/Breakout/Logic/PlayerMove.asset
  • Logic Graph editor window should open up automatically

Image Loading...

Building the Graph


First let's delete the Exit node. We want an infinitely executing graph.

  • Left click on the Exit node to select it
  • Right click it to open up context menu
  • Select Delete from the menu

Next we use our freshly created KeyboardComparer and check against keyboard state

  • Rename the node as Check for Input
  • Rename the first output (where it says out) as Left
  • Add new comparer to the list clicking the + button
  • Select Comparer -> Keyboard from the menu
  • Set Key to Left Arrow
  • Add new output by clicking the Add Output button
  • Rename it as Right add new KeyboardComparer with Key set to Right Arrow
  • Add new comparer to the list clicking the + button
  • Set Key to Right Arrow
  • Add a third output, name it as None and leave it empty

Image Loading...

Graph Variables


Before we can affect the world outside our cozy graph, we have to create a variable.

Variables are a way to store something and retrieve it later. They are also Logic Graphs connection to the outside world. When you want to send information into the scene or you need to read something back, you create a variable.

  • Select Variables from the top-right toolbar
  • Add new variable clicking the + button
  • Name the new variable as velocity
  • Set the type as Vector2

Image Loading...

Using Variables


Let's go back and set some values into our new variable.

  • Return back to the Node tab in the inspector
  • And new node by right-clicking empty space in the graph editor
  • Name the new node as Move Left and make sure it is selected
  • Add new action Value -> Set by clicking the + button
  • Set Variable as velocity and value as (-10, 0)
  • Now repeat the same for two new nodes: Move Right and Stop, except set velocities to (10, 0) and (0, 0)

Now all that is left is returning to the original state

  • Select Move Left node
  • Create empty output by clicking Add Output button
  • Connect the new output out into the in socket of the Check for Input node
  • Repeat same for Move Right and None

Keep the logic graph editor open and start the game. While the player isn't moving yet, pressing the left and right arrow keys makes the graph and connections light up.

In addition, our velocity variable should be changing between -10, 0 and 10 in the Variables tab.

Note: Player will not move yet. There is still stuff to do!

Image Loading...

Connect Graph to the Scene


All that is left is connecting our variable to the outside world.

  • Select Breakout/Player game object and find the Logic Player component
  • Unlock the velocity variable by clicking the cyan lock icon
  • Click the green icon next to the lock and change variable type to Property
  • Drag'n'drop the Rigidbody2D component of this game object onto the variable field
  • Select velocity from the dropdown list that pops up

Now the velocity variable of the Logic Graph is connected to the the velocity property of the Rigidbody2D.

Image Loading...

Current Progress


Start the game and confirm that what you have resembles the animated gif above.

Image Loading...

Block hit logic


Let's do something when the ball hits one of the blocks

  • Select prefab Examples/Breakout/Prefabs/Block1.prefab from the project
  • Add a Logic Player component into it
  • Click the Create New button and choose 'Sequence' from the dropdown that appears
  • Save as Examples/Breakout/Logic/BlockHit.asset
  • Add new Comparer -> Collision to output with the + button
  • Add new Audio -> Play Sound action to the list
  • Set Audio Clip to Examples/Breakout/audio/blockcollide.wav
  • Add new Action -> SelfDestruct action

Play the game. When the ball hits a block, it should make a sound and dissappear.

Image Loading...

Animated fadeout


Time for more polish. We will fade out the block with a animation curve.

  • Select prefab Examples/Breakout/Prefabs/Block1.prefab from the project
  • Add new action Value -> Animate and drag it upward to reorder is as second item
  • Drag'n'drop the Transform component into the Variable field and select Transform -> localScale from the dropdown
  • Set Seconds to 0.5, From to (1,1,1) and To to (0,0,0)
  • Define Curve in curve editor going from (0,0) -> (1,1). Add a bit of wobble if you want :)
  • Add new action Action -> Destroy and drag it as first in list
  • Drag'n'drop the BoxCollider2D component into the Thing field. Rename the created variable to Collider if you want.
  • Add new action Action -> Wait and drag it below Variable Animator
  • Make sure Duration is set as Previous Action Finished

Let's play the game again. Instead of disappearing instantly, the blocks will wobble out.

Note: We destroy the collider right away, because wobbling collider could ruin the game mechanics.

Image Loading...

Global Score Variable


It is time to create our first global variable. Global variables are something that are global to entire scene. Game score is a perfect example.

  • Create new folder Examples/Breakout/Variables
  • Right-click and choose Create -> GOTO -> Global Variable List from the menu
  • Name the asset as BreakoutVariables.asset
  • Set the Name field in the top of the inspector to Breakout
  • Select the asset from the project view
  • In the inspector, add a new variable called score with the type of int

Now let's make our block logic to add some points into the score on collision

  • Select prefab Examples/Breakout/Prefabs/Block1.prefab from the project
  • Create new variable called points with the type int in the inspector
  • Add new Math -> Add action with the + button
  • Drag the action to top of the list
  • Set Variable to Global -> Breakout -> score
  • Press the green icon for Value field and set it as Variable
  • Select points from the dropdown

Make sure your inspector matches the picture on the right

Image Loading...

Now we are all setup. Finally let's select our global variable list so we can see if it is working.

  • Select Examples/Breakout/Prefabs/BreakoutVariables.asset
  • Start the game and look at the inspector to see scores accumulate

Image Loading...

Show Score


We now have global score variable that accumulates nicely, but we still need to show it to the players.

Unfortunately you can't directly link int and string variables & properties, so we need to create custom action to do that for us. Let's make it a fancy one with animated numbers and custom string formats.

  • Open the /Examples/Breakout/Actions folder
  • Right click empty project view for context menu
  • Choose Create -> GOTO -> Logic Action (template)
  • Name the new action as SetUINumberAction.cs

Copy-paste the code from the right into this new script file.

using System;
using UnityEngine;
using UnityEngine.UI;

namespace GOTO.Logic.Actions
{
    [Serializable]
    [ActionCategory("Action")]
    public class SetUINumberAction : Action
    {
        public Value number = new Value(typeof(int));
        public Value target = new Value(typeof(GameObject));
        public string format = "000000";
        public float speed = 5.0f;
        private float result;

        protected override void OnStart()
        {
            var go = target.GetValue<GameObject>(this);
            if(go != null)
            {
                var text = go.GetComponentInChildren<Text>();
                if(text != null)
                {
                    var targetNumber = number.GetValue<int>(this);
                    var amount = Time.deltaTime * speed;

                    if(speed > 0)
                        result = Mathf.Lerp(result, targetNumber, amount);
                    else
                        result = targetNumber;

                    text.text = Mathf.RoundToInt(result).ToString(format);
                }
            }
            SetFinished();
        }
    }
}

Animated Score


  • Select ScoreUI/Score game object from the hierarchy window
  • Add new Logic Player component into it
  • Press Create New button and select Sequence from the dropdown
  • Save the new asset as Examples/Breakout/Logic/scorelabel.asset
  • Set Play Mode as Loop
  • Add new action Action -> SetUINumber by clicking the + button
  • Press the green icon for Number and set it as Variable
  • Set Number to Global/Breakout/score
  • Drag in the Score game object itself as Target. New variable is generated.
  • Rename New Variable as Target

You should now see how the score label in top-left of screen is animating nicely.

Image Loading...

Image Loading...

Block Variations


Instead of one block to rule them all, let's make three variations with different colors and points scored.

  • Open the Examples/Breakout/Prefabs folder in the project view
  • Drag Block1 to replace Block2
  • Unity will ask if you are sure, click Replace anyway
  • Do the same for Block3 so all three prefabs are copies of Block1
  • Set SpriteRenderer colors to Block2 -> 31622EFF and Block3 -> 10380DFF in the inspector
  • Set Block2 points -> 30 and Block3 points -> 100

Image Loading...

We also need to tell our level building script that there are different prefabs available.

  • Select Breakout/Level game object from the hierarchy
  • Add Breakout/Examples/Prefabs/Block2.prefab and Breakout/Examples/Prefabs/Block3.prefab to the Blocks list

Image Loading...

Current Progress


If you play the game, you should now see some variation in block colors and scores scored.

Image Loading...

Gameover


Final thing to do is to end the game when the ball hits the floor and have a nice state machine handling the start -> game -> gameover -> restart states.

First let's make global variable for game over state.

  • Open Examples/Breakout/Variables/BreakoutVariables.asset
  • Add new gameover variable with bool type

Image Loading...

Next we will set gameover = true, when ball hits the floor.

  • Select Examples/Breakout/Prefabs/Floor.prefab from the project
  • Add new Logic Player component for it
  • Press Create New button and select Sequence from the dropdown
  • Save the new asset as Examples/Breakout/Logic/gameover.asset
  • Set Play Mode to Once and Reset
  • Add new Comparer -> Collision comparer in the conditions list
  • Add new Value -> Set action in the actions list
  • Set Variable as Global -> Breakout -> Gameover from the dropdown list
  • Set Value as true

Image Loading...

If you play the game with global variables open, you should see gameover variable change to true when ball hits the floor.

Image Loading...

Restart Level Action


Before we can make our game state handling graph, we need one more new action for restarting the entire level.

  • Open the /Examples/Breakout/Actions folder
  • Right click empty project view for context menu
  • Choose Create -> GOTO -> Logic Action (template)
  • Choose the name RestartLevelAction.cs and press save

Copy-paste the code from the right into this new script file.

using System;
using UnityEngine;

namespace GOTO.Logic.Actions
{
    [Serializable]
    [ActionCategory("Action")]
    public class RestartLevelAction : Action
    {
        protected override void OnStart()
        {
            GameObject go = GameObject.Find("Level");
            BreakoutLevel level = go.GetComponent<BreakoutLevel>();
            level.ReloadLevel();
            SetFinished();
        }
    }
}

Game State Logic


Time to make our final logic graph which handles the entire game state cycle.

It is going to be start -> game -> gameover -> restart.

  • Select Breakout game object from the hierarchy
  • Add new Logic Player component into it
  • Click the Create New and select Graph from the dropdown
  • Save the graph as Examples/Breakout/Logic/BreakoutGame.asset
  • Logic graph editor window should popup (unless already open)
  • Remove Exit node
  • Create four nodes Start, Game, Gameover and Restart and connect them according to the screenshot

Image Loading...

Variables


  • Switch to the Variables tab from the toolbar
  • Create startlabel variable with type of GameObject
  • Create gameoverlabel variable with type of GameObject
  • Create ballvelocity variable with type of Vector2

Image Loading...

Start Node


  • Switch to the Node tab from the toolbar
  • Select Start node from the graph view
  • Add new Action -> Toggle action to the action list
  • Change field type to Variable from the green icon dropdown
  • Change the mode to Enable and target variable as startlabel
  • Add new Comparer -> Keyboard to the list and Key to Space

Image Loading...

Game Node


  • Select Game node from the graph view
  • Add new Action -> Toggle action to the action list
  • Change field type to Variable from the green icon dropdown
  • Change the mode to Disable and target variable as startlabel
  • Add new Value -> Set action
  • Set Variable to ballvelocity and Value to (-10, -10)
  • Add new Comparer -> Value to the list
  • Set left side to Global -> Breakout -> gameover and right side to bool & true

Image Loading...

Game Over Node


  • Select Game Over node from the graph view
  • Add new Action -> Toggle action to the action list
  • Change field type to Variable from the green icon dropdown
  • Change the mode to Enable and target variable as gameoverlabel
  • Add new Audio -> Play Sound action to the action list
  • Set AudioProvider as Examples/Breakout/Audio/gameover.wav
  • Add new Value -> Set action
  • Set Variable to ballvelocity and Value as '(0,0)'
  • Add new Comparer -> Keyboard to the list and Key to Space

Image Loading...

Restart Level Node


  • Select Restart Level node from the graph view
  • Add new Value -> Set action
  • Set Variable to Global -> Breakout -> gameover and Value to 'false'
  • Add new Value -> Set action
  • Set Variable to Global -> Breakout -> score and Value to '0'
  • Add new Action -> Toggle action to the action list
  • Change field type to Variable from the green icon dropdown
  • Change the mode to Disable and target variable as gameoverlabel
  • Add new Action -> RestartLevel

Image Loading...

Connect Graph to Scene


Now we still need to connect the graph to the outside world as usual

  • Select Breakout game object from the hierarchy
  • Unlock all the variables in the Logic Player inspector by clicking the cyan lock icon
  • Drag in Canvas/PressSpace game object into startlabel field
  • Drag in Canvas/GameOver game object into gameoverlabel field
  • Drag in Ball from the scene into ballvelocity and select Rigidbody2D/velocity from the dropdown

Image Loading...

Final Touch


Last thing to do is remove the very first action we put into the game.

  • Select Ball game object from the scene
  • Remove the Logic Player that gives the ball it's initial velocity

We remove this, because the initial velocity is now handled by our game state logic graph.

Final Product


Image Loading...

The game is done! Ship it!