Add a scoring system

Tutorial

·

Beginner

·

+10XP

·

30 mins

·

(226)

Unity Technologies

Add a scoring system

In many games, scoring helps provide feedback and motivation to players. In this tutorial, you'll add a basic scoring system to your game that tracks how long the player survives and displays their score on the screen using Unity's UI Toolkit system.

1. Overview

In many games, scoring helps provide feedback and motivation to players. In this tutorial, you'll add a basic scoring system to your game that tracks how long the player survives and displays their score on the screen using Unity's UI Toolkit system.

2. Track elapsed time

To begin calculating a score, you first need to measure how long the player has been alive. Unity provides a helpful built-in value called Time.deltaTime, which returns the time in seconds since the last frame. By adding this value to a running total each frame, we can track how long the player has survived — regardless of frame rate. In this step, you’ll implement this timer inside your PlayerController script.

1. Declare a variable to store elapsed time:

  • Open your PlayerController script.
  • Near the top of the class (after your other variable declarations), add the following line of code:
private float elapsedTime = 0f;

Note: Writing private is not required — Unity automatically treats variables as private if you don’t specify them as private or public. Still, it’s a good habit to include private explicitly. This makes your code easier to understand by clearly showing which variables are private and which are public.

2. Increment elapsed time using Time.deltaTime:

  • Inside the Update() method, add the following line of code at the top:
elapsedTime += Time.deltaTime;

Explanation:

  • += is a shorthand for "add to this variable."
    • elapsedTime += Time.deltaTime; is the same as writing: elapsedTime = elapsedTime + Time.deltaTime;
  • Time.deltaTime is the time (in seconds) since the last frame.
  • If you add that small number every frame, it adds up over time. The result is a running total of how much time has passed since the game started — this works no matter what the frame rate is.

3. Log the result in the Console window:

  • Below the line you just added, add the following line of code:
Debug.Log("Elapsed time: " + elapsedTime);

4. Test and observe the Console window:

  • Save the script, return to Unity, and enter Play mode to test.
  • Watch the Console window to see the timer increase over time.
  • Let the Player GameObject collide with an obstacle and notice that the log stops. This script is attached to the Player GameObject, so when it gets destroyed — the timer stops because the script is no longer in the scene.

Your game now tracks how long the player survives using Time.deltaTime.

3. Add a score multiplier

Now that you’re tracking how long the player has survived, it’s time to turn that into a score. It wouldn’t be very interesting if the score just went up by 1.0 every second.

In this step, you’ll create two new variables: one to hold the final score and one to define a score multiplier. Then, you’ll display that score using Debug.Log.

1. Declare the score and multiplier variables:

  • In the PlayerController script, add the following lines of code near your other variables:
private float score = 0f;
public float scoreMultiplier = 10f;

Note: You might want the score multiplier to be public if you want to adjust it in the Inspector window later.

2. Calculate the score in Update():

  • Just below where you update elapsedTime, add the following line of code:
score = elapsedTime * scoreMultiplier;

3. Log the score to the Console window:

  • Replace the existing Debug.Log line with the following line of code:
Debug.Log("Score: " + score);

Explanation:

  • This Debug.Log line will display the score instead of the elapsed time.

4. Test and observe the console:

  • Save the script, return to Unity, and enter Play mode to test.
  • Watch the Console window to see the score increase over time.

Because both values are floats, your result will include decimal points (for example, Score: 17.32647). Don’t worry — you’ll round that value to an integer in the next step.

4. Round the score to an int

Right now, the score displays as a long decimal number, which can be hard to read and doesn’t feel very game-like. Most games use whole numbers for score displays. In this step, you’ll use Mathf.FloorToInt(), a Unity function that rounds a decimal number down to the nearest whole number. You’ll also learn what an int is and why you’d use it instead of a float when displaying clean numbers.

1. Round the score using Mathf.FloorToInt():

  • Replace the line where you calculate the score with the following line of code:
score = Mathf.FloorToInt(elapsedTime * scoreMultiplier);

Explanation:

  • An int is a whole number (like 0, 1, 25). It doesn’t store decimal places.
  • Mathf.FloorToInt() takes a decimal (float) and returns the largest whole number less than or equal to that value.
  • For example: Mathf.FloorToInt(3.7f) gives 3.
  • This ensures the score only increases after full seconds have passed — which is useful if, for example, you want the player to earn 1 point per second of survival.
  • Without flooring, a score like 3.7 would round up to 4, giving credit before the full second has actually passed.

2. Test and observe the Console window:

  • Save the script, return to Unity, and enter Play mode to test.
  • Watch the Console window to see the score increase over time.

The Console window should display clean whole numbers like Score: 47, instead of Score: 47.39214.

5. Set up a UI document

Now that the score is being calculated correctly, it’s time to display it on the screen. In Unity, you can create user interfaces using the UI Toolkit, which uses assets called UI Documents written in UXML (a markup language similar to HTML). This step walks you through creating a UI Document and connecting it to your scene so you can begin building a heads-up display (HUD) for your score.

1. Create a UI Document GameObject in the scene:

  • Right-click in the Hierarchy window and select UI Toolkit > UI Document.
  • Rename the new GameObject “GameUI”.

2. Create a new UI Document asset:

  • In the Project window, right-click the Assets folder, select Create > Folder, and name the new folder “UI”.
  • Right-click the UI folder and select Create > UI Toolkit > UI Document.
  • Name this new asset “UILayout”.

3. Link the asset to the scene's UI Document:

  • Select the GameUI GameObject in the Hierarchy window.
  • Select the Source Asset picker () and select the UILayout asset.

Explanation:

  • A UI Document GameObject in the scene is what actually displays the interface during gameplay.
  • A UXML asset (like the UILayout asset you just created) defines the structure and layout of the UI.

You won’t actually see any UI elements on the screen yet; you’ve just created the UI container that will hold your score display.

6. Add the score label

With the UI container set up in your scene, the next step is to design the actual interface that’ll display your score. You’ll do this using the UI Builder window, a drag-and-drop editor for creating UI layouts visually. In this step, you’ll add a Label, give it a clear name, and set some placeholder text.

1. Open the UILayout asset in UI Builder:

  • In the Project window, double-click the UILayout asset you created earlier.
  • This opens the UI Builder window, which contains a live Viewport window (similar to the Scene view), a Library window (similar to the Project window), a Hierarchy window, and an Inspector window, just like the Unity Editor.

2. Add a Label to the UI:

  • From the Library window (usually on the left), drag a Label element into the Hierarchy window.
  • You’ll now see a text label appear in the Viewport window that says Label by default.

3. Rename the Label to ScoreLabel:

  • In the Hierarchy window of the UI Builder window, select the new Label element.
  • In the Inspector window (on the right), rename the Label element by setting its name at the very top of the Inspector panel to “ScoreLabel”.
    • Spelling is important here — you’ll use this name in your script to reference the label later.
  • In the Attributes section, in the Text box, change the placeholder text to “Score: 0”.

4. Save and preview your changes:

  • Press Ctrl+S (macOS: Cmd+S) to save the UILayout asset.
  • You can now close or minimize the UI Builder window.

Back in the Unity Editor, you will see the “Score: 0” text appear in the upper left corner of the screen, though it may be hard to see against a dark background — you’ll fix that next.

7. Style the label

By default, UI elements added in UI Builder are small, plain, and usually tucked in the upper-left corner. In this step, you’ll center the label horizontally and give it some visual breathing room using inlined styles — direct style settings applied to individual UI elements.

1. Reopen the UILayout in UI Builder (if closed):

  • In the Project window, double-click the UILayout asset to reopen it in the UI Builder window.

Tip: If the Viewport window looks zoomed or misaligned, select Fit Viewport at the top of the window to recenter it.

2. Select the ScoreLabel in the UI Hierarchy panel:

  • In the Hierarchy window, select the ScoreLabel element you created earlier.

3. Apply basic layout styling:

  • In the Inspector window on the right side, expand the Inlined Styles section.
  • Under Position, set the Top Offset to around 20px (pixels). This will move the score down a bit so it’s not overlapping the top border wall.
  • Under Align > Align Self, select Center to center the label horizontally.

4. Adjust the font and text color:

  • In the Text section of the Inlined Styles section, try the following changes:
    • Set Font Size to something like 20.
    • Select a Color that contrasts well with your background.
    • Optionally, add Font Style like Bold if you want the score to stand out.
  • Press Ctrl+S (macOS: Cmd+S) to save the UILayout asset and return to the Unity Editor to preview your score UI formatting.

After this step, the ScoreLabel should be clearly visible, centered near the top of the screen, and easy to read.

8. Get a reference to the UI document

Before your script can update the score label, it needs to access the UI that’s visible in the scene. To do that, you’ll get a reference to the UI Document component attached to your GameUI GameObject.

1. Add a variable for the UI Document:

  • Open your PlayerController script.
  • Near your other variable declarations, add the following line of code:
public UIDocument uiDocument;

Note: Declaring this as public makes the variable appear in the Unity Inspector window, where you can assign it manually without writing extra code. This is a common and beginner-friendly way to connect scene references.

2. Fix the missing reference error:

  • If UIDocument is underlined or causes an error, add the following line of code to the top of your script:
using UnityEngine.UIElements;

Note: This line gives your script access to Unity’s UI Toolkit classes. If you're using autocomplete in your IDE, it may have already added this line for you automatically.

3. Assign the UI Document in the Inspector window:

  • Save your script and return to the Unity Editor.
  • With the Player GameObject selected, in the Inspector window, locate the UI Document property that now appears in the PlayerController component.
  • Drag the GameUI GameObject from the Hierarchy window into the UI Document box.

Note: You must assign the GameObject from the scene, not the UILayout UXML asset from the Project window.

After this step, your script has access to the UI and is ready to find and modify specific elements inside it.

9. Set the score text with true score

Now that your script can access the UI document in the scene, the next step is to find the score label inside it and update its text.

1. Add a variable to reference the label:

  • In your PlayerController script, add the following line of code with your other variables:
private Label scoreText;

Note: A Label is the specific UI Toolkit class used for displaying text. This line prepares a variable to hold the reference to the ScoreLabel UI element.

2. Initialize the reference in Start():

  • Inside your Start() method, add the following line of code to initialize the variable:
scoreText = uiDocument.rootVisualElement.Q<Label>("ScoreLabel");

Explanation:

  • .rootVisualElement gives you access to the top-level container of the UI layout.
  • .Q<Label>("ScoreLabel") uses Unity’s query system to find the first element of type Label with the name ScoreLabel.
  • Make sure the name exactly matches what you set in UI Builder — spelling, capitalization, and all.

3. Update the label text in Update():

  • After you calculate the score (the score = ... line), add the following line of code:
scoreText.text = "Score: " + score;

Notice that you’re now setting the text with the same message that you had previously logged to the Console window: "Score: " + score.

Tip: If the label doesn’t update during Play mode, double-check the following possible causes:

  • That the UI Document was assigned to the Player GameObject in the Inspector window.
  • That the ScoreLabel name in the UI Builder window exactly matches what you entered in the script.

After this step, your score will be displayed live on screen and will update smoothly during gameplay.

10. Scale with screen size

The score label looks fine on your current screen, but if you resize the Game view or change aspect ratios, it might shift or appear too small. With these settings in place, if someone plays your game on a huge high resolution monitor or on a small low resolution phone, the UI could change in unexpected ways. To address this, you’ll adjust the Panel Settings so your UI scales automatically with the screen. This is an easy way to ensure your UI remains readable and correctly positioned on any resolution or device.

1. Experiment with different Game view resolutions:

  • In the Game view, use the aspect ratio dropdown to select a few different screen resolutions.
  • Notice that the score text gets smaller and changes position at higher resolutions.

2. Select the Panel Settings asset:

  • In the Project window, locate the Panel Settings asset that was automatically created when you added the UI Document to the scene. It’s usually inside a folder called UI Toolkit or next to your UILayout asset.

Tip: You can search the entire Assets folder using the search bar.

3. Set it to scale with the screen size:

  • With the Panel Settings asset selected, in the Inspector window, set the Scale Mode property to Scale With Screen Size.

4. Adjust the reference resolution if needed:

  • If the UI elements became smaller or larger than you’d like, under the Scale Mode Parameters section, increase or decrease the Reference Resolution Width and Height properties.

After changing this setting, test different Game view aspect ratios from the dropdown in the Game view or by dragging the top edge of the Game view up and down. The score label should now stay anchored and readable regardless of window size.

11. Final script sample

If you have any errors in your code or you just want to make sure your script matches exactly what has been taught up to this point, feel free to reference or copy the final code sample below.

PlayerController.cs

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UIElements;

public class PlayerController : MonoBehaviour
{
    private float elapsedTime = 0f;
    private float score = 0f;
    
    public float scoreMultiplier = 10f;
    public float thrustForce = 1f;
    
    Rigidbody2D rb;
    
    public UIDocument uiDocument;
    private Label scoreText;
        
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        scoreText = uiDocument.rootVisualElement.Q<Label>("ScoreLabel");
    }

    void Update()
    {
        elapsedTime += Time.deltaTime;
        score = Mathf.FloorToInt(elapsedTime * scoreMultiplier);
        scoreText.text = "Score: " + score;
        
        if (Mouse.current.leftButton.isPressed) {
            
            // Calculate mouse direction
            Vector3 mousePos = Camera.main.ScreenToWorldPoint(Mouse.current.position.value);
            Vector2 direction = (mousePos - transform.position).normalized;
            
            // Move player in direction of mouse
            transform.up = direction;
            rb.AddForce(direction * thrustForce);
        }
        
    }

    void OnCollisionEnter2D(Collision2D collision)
    {
        Destroy(gameObject);
    }

}
Optional Step

12. Optional: More things to try

Try these optional activities to challenge yourself, build your skills, and improve your project.

Each challenge is tagged as Easy, Medium, or Expert difficulty so that you know what to expect. You can complete one, all, or none of them — it’s totally up to you. If you’re not interested in doing these challenges, mark this step as complete and proceed to the next tutorial.

Easy: Clean up the Update method

Make your PlayerController script easier to read by splitting Update() into two smaller methods.

  • Create two empty methods below Update():

void UpdateScore() {}

void MovePlayer() {}

  • Copy and paste the score-related code into UpdateScore().
  • Copy and paste the if-statement that moves the player into MovePlayer().
  • Then, call both methods from inside Update():

void Update()
{
UpdateScore();
MovePlayer();
}

Breaking up your code into smaller, named methods makes it easier to understand and maintain.

Medium: Customize the score text box

UI Toolkit allows you many more options for styling UI elements. With these tools, you can make your score stand out even more.

  • Experiment with the Border property: you can add an outline and round out the corners.
  • Experiment with the Background property: you can set the Color to something semitransparent so you can still see the obstacles behind it.

Note: Reference the UI Toolkit Manual for more guidance.

Medium: Import new fonts for your labels

Custom fonts can give your game a unique look. Try importing a new font for use in your score label.

  • Browse free fonts at Google Fonts or other open-source font sites.
  • Download any .TTF font files and import them into a new Fonts folder in the Assets folder.
  • Imported fonts will automatically become available for use in the UI Builder window.

13. Next steps

In this tutorial, you created a scoring system that tracks how long the player survives and displays the score on screen using UI Toolkit. In the next tutorial, you’ll build a game over effect with a particle explosion and a Restart button to reset the game when the player is destroyed.

Complete this tutorial