Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 11 — Scoreboards: Counting and Comparing

What You’ll Build

This chapter starts a new part of the book: storing and tracking data. Up to now your mypack functions have done things (said messages, summoned mobs, given effects), but they’ve had no memory. The moment a function finishes, the game forgets everything it just did. This part fixes that, and it does so with three different tools. The scoreboard you’ll learn here is the first, and it’s the right tool for one specific job: keeping a whole number attached to a player or an entity, changing it over time, showing it on screen, and comparing it. By the end of this chapter you’ll have added a live kill counter to mypack, a number on the right side of your screen that ticks up every time you defeat a mob, and you’ll finally be able to use execute if score, the comparison you were promised back in Chapter 4.

This chapter extends the pack you started in Chapter 9 (mypack). It assumes you’re comfortable calling functions (Chapter 9), with target selectors like @s and @a (Chapter 3), and with the /execute command — its as/at/run pieces and the idea of forking over many entities (Chapter 4). That’s also where you first saw if score named; here you learn it for real.

Modern Minecraft Older data pack tutorials lean on scoreboards as “Minecraft’s variable system” and try to store everything in them. That’s no longer how experienced creators work. In current Java Edition a scoreboard is one of three storage tools, and it’s best at exactly one kind of data: a whole number you might want to see or compare. Complex data (lists, text, nested settings) belongs in command storage (Chapter 12), and simple on/off flags belong in entity tags (Chapter 13). Chapter 13 ends with a decision guide for picking between them. Keep that in the back of your mind as you read: scoreboards are useful, but they are not the answer to everything.

What a scoreboard is

A scoreboard is, in the game’s own words, a gameplay mechanic used through commands to track, set, and list the scores of entities in many different ways. Strip away the formality and it’s simpler than it sounds: a scoreboard is a big table of numbers. Each row pairs a who (a player or entity) with a what (a named counter), and the cell holds a number.

That named counter is called an objective. An objective tracks a score for entities that meet a single criterion, and every score is a 32-bit integer, a whole number that can range from roughly negative two billion to positive two billion (the exact limits are -2,147,483,648 and 2,147,483,647). Two things matter about that: scores are always whole numbers (never 2.5), and they can be negative.

The who in the table is called a score holder. A score holder’s name can either be the player’s username or the entity’s UUID, a UUID being the long unique ID every entity carries (you met UUIDs glancingly before; just think “the entity’s permanent serial number”). So one objective can hold a different number for every player and every mob at once. The objective is the column; each holder is a row.

An objective has two main properties of its own: a name, used internally when you reference it in commands, and a criterion, which decides what the objective tracks. In Java Edition the name must be a single, case-sensitive string of alphanumeric characters (A–Z and 0–9), hyphen -, plus +, dot . and underscore _. In plain terms: pick a short, lowercase, no-spaces name, the same kind of name you’ve been using for functions.

Creating an objective

Everything about scoreboards happens through one command, /scoreboard, which manages and displays scores for your scoreboard objectives. It has two big families of subcommands: scoreboard objectives ... (which create and configure the counters themselves) and scoreboard players ... (which read and change the numbers in them). We’ll start with creating an objective.

The syntax is:

scoreboard objectives add <objective> <criteria> [<displayName>]

<objective> is the internal name you’ll use everywhere else, <criteria> is what it tracks (next section), and the optional <displayName> is a prettier label shown on screen. Two companions you’ll reach for:

scoreboard objectives list
scoreboard objectives remove <objective>

list lists all existing objectives, and remove deletes the named objective from the scoreboard system, wiping its data from every holder and removing it from any display. If you try to add a name that already exists, the command fails; if you remove one that doesn’t, it fails too. Both are harmless mistakes that just print a red message.

Criteria: what an objective tracks

A criterion determines an objective’s behavior and tracks statistical game elements. When a criterion’s source value changes, the change is automatically reflected in the objective’s score. This is the most important idea in the chapter, so read it twice. The criterion is the difference between a number you control and a number the game controls.

The simplest criterion is dummy. A dummy objective tracks nothing on its own: the game never touches it. Its score only changes when a command changes it. That makes dummy the right choice whenever you want to be in charge of the number: a timer, a points total, a counter you increment yourself. (One note: in Bedrock Edition dummy is the only criterion that exists; the rich automatic ones below are Java-only, another reason this book is a Java book.)

The other criteria are automatic: the game updates them for you whenever the matching thing happens in the world. A good worked example is the deathCount criterion: make an objective with it, and a player’s score increments whenever they die. You never write a command to bump it; the game does, every time that player dies. There are many such criteria for health, hunger, experience, statistics, and more; some are compound criteria with dotted names, like minecraft.killed_by:minecraft.zombie, under which a player’s score increments whenever they are killed by a zombie.

Under the Hood (skippable) Why the two kinds? An automatic criterion is wired to a number the game already keeps (your death total, your play time, a statistic) and the objective just mirrors it. A dummy objective is a blank counter with no wiring, waiting for your commands. A useful rule of thumb: if Minecraft already counts the thing, there’s probably an automatic criterion for it; if it’s your idea (a quest stage, a minigame score), use dummy and drive it yourself.

Finding more criteria Java Edition has a long list of automatic criteria (for health, hunger, experience, triggers, statistics, and more) and the full set runs to dozens of alphabetical names. This chapter sticks to the handful you’ll actually reach for: dummy, deathCount, and the compound minecraft.killed_by:… form. When you want to browse the complete alphabetical list, the in-game command auto-complete (type scoreboard objectives add name and press Tab) and the wiki’s Scoreboard page are the best places to see every criterion at once. The kill counter below is built on dummy plus a function that increments it, so it needs no special criterion name at all.

Changing a score: set, add, remove

To read and change the numbers you use the scoreboard players subcommands. The four you’ll use constantly are:

scoreboard players set <targets> <objective> <score>
scoreboard players add <targets> <objective> <score>
scoreboard players remove <targets> <objective> <score>
scoreboard players get <target> <objective>
  • set: sets the targets’ scores in the given objective, overwriting any previous score. Use it to force a number to an exact value.
  • add: increments the targets’ scores in that objective by the given amount. This is how a counter goes up.
  • remove: decrements the targets’ scores in that objective by the given amount. This is how it goes down.
  • get: returns the scoreboard value. Handy for checking a number, and it can feed execute store result … run scoreboard players get … later.

There’s also reset: scoreboard players reset <targets> [<objective>]. Be careful here: reset does not merely set the scores to 0, it removes the targets from the scoreboard system. So reset is forget this holder entirely, while set … 0 is keep them, value zero. They look the same on screen but mean different things; for a counter you almost always want set … 0, not reset.

Here’s a tiny demo function so the four verbs feel concrete. Create:

mypack/data/mypack/function/kills_demo.mcfunction

# kills_demo — show set / add / remove on the "kills" objective
scoreboard players set @s kills 0
scoreboard players add @s kills 3
scoreboard players remove @s kills 1
scoreboard players get @s kills

After /reload and /function mypack:kills_demo, your kills score is 0, then 3, then 2, and the final get prints 2 back to you. (We’ll create the kills objective itself in the walkthrough.)

Try It! There’s a whole set of arithmetic operations for combining two scores (assignment =, addition +=, subtraction -=, multiplication *=, floor-division /=, modulus %=, swap ><, and min/max < / >) run with scoreboard players operation <targets> <objective> <operation> <source> <objective>. You don’t need them for a simple counter, and we’ll use them in the minigame project (Chapter 33). For now, just know the word operation means “do math between two holders’ scores.”

Displaying a score

A number you can’t see isn’t much fun. Scores can be shown in display slots: these can appear in the player list, on the sidebar at the right side of the screen, or below a player’s name tag. Each slot shows one objective at a time. You set a slot with:

scoreboard objectives setdisplay <slot> [<objective>]

Java Edition names three display slots exactly: sidebar (the panel on the right edge of the screen), list (the tab player list), and below_name (under players’ name tags in the world). For example, scoreboard objectives setdisplay sidebar kills puts the kills objective on the sidebar. Leaving the objective off (scoreboard objectives setdisplay sidebar) clears that slot back to empty.

One detail worth knowing: only the sidebar can show non-player entities’ scores; list and below_name are player-only.

No action-bar slot Those three (sidebar, list, and below_name) are the only slots setdisplay accepts. There’s no action-bar display slot. If you want a score in the action bar, that’s a different tool: use the /title … actionbar command from Chapter 5 with a text component, which can embed a score via the score text component. That’s a text feature, not a setdisplay slot, so reach back to Chapter 5 for it rather than looking for a fourth slot here.

Fake players: numbers under made-up names

Here’s a trick that surprises everyone the first time. A score holder’s name can be any arbitrary username you choose, belonging to no real player at all. A made-up name used this way is called a fake player.

Why bother? Because a fake player gives you a place to store a global number, one that belongs to the whole pack rather than to any particular player. Want a single shared “total mobs spawned” count, or a “current game phase,” or a constant like 100 you compare against? Store it on a fake player:

scoreboard players set #total kills 0
scoreboard players add #total kills 1

#total isn’t a real player, so nobody owns it: it’s just a labeled box for a number. The leading # is a deliberate convention: fake players with names starting with a # character never show up in the sidebar. So a # name is a hidden global that stays out of the on-screen list. Creators use the # prefix for behind-the-scenes values they want to keep hidden from players.

Under the Hood (skippable) Player names can’t contain spaces, so there’s a trick worth knowing: a “figure space” (an invisible-looking character, U+2007) can stand in for a space in a fake player’s display. You won’t need that for normal work (short #names are clearer), but if you ever see a fake player whose name looks like it has gaps, that’s what’s happening.

Comparing scores: if score (the Chapter 4 promise, paid off)

Back in Chapter 4 you met execute if block and execute if entity, and I told you a third condition, if score, was coming once you had scoreboards. Now you do. (if|unless) score checks whether a score has a specific relation to another score, or whether it is in a given range. There are two forms.

Comparing two scores. The syntax is:

(if|unless) score <target> <targetObjective> (<|<=|=|>=|>) <source> <sourceObjective> -> [execute]

The middle piece is one of five comparison operators (<, <=, =, >=, >), read exactly like in math. Here’s an example that checks whether two scores are equal:

execute if score @s A = @s B

That reads: “if my score in objective A equals my score in objective B.” You can compare across holders, too: if score @s kills > #total kills asks whether my kills beat the stored total.

Comparing a score to a range. The second form tests one score against a range of numbers:

(if|unless) score <target> <targetObjective> matches <range> -> [execute]

A <range> is written the same way you wrote distance= ranges back in Chapter 3: 10 means exactly ten, 10.. means ten or more, ..10 means ten or less, and 5..10 means anywhere from five to ten. So if score @s kills matches 10.. means “if my kills are ten or more.” This range form is the one you’ll use most for milestones and thresholds.

Just like if entity and if block, the trailing -> [execute] means another subcommand is optional: if score can sit at the end of a chain (just testing), or be followed by run to do something when the test passes. And unless score is simply if score flipped. This example, execute as @a unless score @s test = @s test run say "Score is reset", fires only when a player has no score set (a value compared to itself fails when it doesn’t exist).

Try It! Selectors can filter by score directly, too. There’s a scores selector argument: @e[scores={<name>=<min>..<max>}]. For example, @a[scores={kills=10..}] targets every player whose kills is ten or more. It’s the same range idea as if score … matches, just packed into a selector. Try swapping one for the other once your counter works.

Walkthrough: a kill counter on the sidebar

Now the real project. We want a number on the sidebar that goes up by one every time you kill a mob. We’ll build it from pieces you now know: a dummy objective named kills, a sidebar display, and a tick-driven check that watches for kills and increments the score. We use dummy plus our own increment so that we stay in full control of when the number moves.

Step 1 — create the objective and show it

Make a setup function that creates the objective and puts it on the sidebar. This should run once, when the pack loads. Create:

mypack/data/mypack/function/score_setup.mcfunction

# score_setup — create the kill counter and show it on the sidebar
scoreboard objectives add kills dummy "Mob Kills"
scoreboard objectives setdisplay sidebar kills

The first line makes a dummy objective named kills with the on-screen label "Mob Kills". The second line shows that objective on the sidebar. Now wire this into the load tag so it runs automatically. You already created this file in Chapter 9 for mypack:load. Add the new function to its list (don’t replace what’s there):

mypack/data/minecraft/tags/function/load.json

{
  "values": [
    "mypack:load",
    "mypack:score_setup"
  ]
}

What Went Wrong? If you run score_setup twice (say, after a second /reload), the second scoreboard objectives add kills dummy … line fails with a red message saying the objective already exists, and that’s harmless. The objective from the first run is still fine. A load function trying to re-create an existing objective is a normal, ignorable warning, not a bug in your pack.

Step 2 — count the kills

A dummy objective won’t move on its own. That’s the whole point of dummy: you drive it. So we make a small function that adds one to a player’s kills, and call it whenever a kill should count. Create a function you call to register a kill:

mypack/data/mypack/function/add_kill.mcfunction

# add_kill — register one mob kill for the player who runs it
scoreboard players add @s kills 1

Run /function mypack:add_kill and your sidebar kills number climbs by one each time. To make it feel automatic in a test, pair it with the /execute skills from Chapter 4. For instance, a tick function that adds a kill for every player standing on a gold block reuses the exact as @a at @s if block pattern you already wrote:

mypack/data/mypack/function/kill_on_gold.mcfunction

# kill_on_gold — demo: stand on a gold block to rack up the counter
execute as @a at @s if block ~ ~-1 ~ minecraft:gold_block run scoreboard players add @s kills 1

This is just a stand-in so you can watch the sidebar move without fighting mobs. To make it run every tick, list it in the tick function tag, the every-tick sibling of the load tag you met in Chapter 9. This is the first function you’ve wanted on every tick, so create the file:

mypack/data/minecraft/tags/function/tick.json

{
  "values": [
    "mypack:kill_on_gold"
  ]
}

/reload, stand on a gold block, and watch “Mob Kills” climb on the sidebar.

Figure (to be captured). the sidebar on the right of the screen titled “Mob Kills” showing a rising number while the player stands on a gold block

Step 3 — react at a milestone

Finally, do something when the counter crosses a threshold. This is where if score earns its keep. Create a function that congratulates the player once they hit ten kills:

mypack/data/mypack/function/kill_milestone.mcfunction

# kill_milestone — celebrate at 10 kills
execute as @a if score @s kills matches 10.. run say Ten kills! Nice work.

as @a forks over every player; if score @s kills matches 10.. passes only for players whose kills is ten or more; and run say … fires for each one who qualifies. Call it from chat with /function mypack:kill_milestone, or add it to a tick function if you want it watched constantly (though a constant version would repeat every tick, so a real pack would set a “done” flag, which is exactly the kind of on/off state you’ll learn to store in Chapters 12 and 13).

When scoreboards are the right tool

You’ve now seen what a scoreboard does well, so here’s the honest summary the rest of this part builds on. Reach for a scoreboard when you need:

  • a whole number: scores are integers, nothing else (no text, no lists, no decimals);
  • that you might display: the sidebar, list, and below-name slots are built for exactly this;
  • or compare: if score and the scores= selector argument make numeric thresholds easy;
  • or have the game track for you: automatic criteria like deathCount update themselves.

That’s a real, common set of needs: kill counts, timers, points, lives, levels. But notice what’s not on the list: anything beyond a single whole number. A player’s chosen difficulty, a list of completed quests, a block of configuration: those are the wrong shape for a scoreboard, and trying to cram them in is the classic “scoreboards as variables” mistake from the Modern Minecraft note at the top. The next chapter introduces command storage, which holds exactly that richer data, and Chapter 13 caps the part with a decision guide so you’ll never have to guess which of the three tools to grab.

What Can Go Wrong

Forgetting to create the objective first. Every scoreboard players … command needs an objective that already exists. If you add @s kills 1 before any objectives add kills … has run, the command fails because the objective doesn’t exist. This is why the walkthrough creates kills in a load function: it’s guaranteed to exist before any other function tries to use it.

Confusing reset with set … 0. Remember the warning from earlier: reset removes the holder from the objective entirely, while set … 0 keeps them with a value of zero. If your sidebar suddenly stops listing a player after you “zeroed” them, you probably used reset when you meant set … 0.

Wrong display-slot name. setdisplay takes exactly sidebar, list, or below_name. A typo like side_bar or belowname won’t match, and the score simply won’t appear. If your number isn’t showing up, check the slot name first, and confirm you actually ran the setdisplay line at all (a freshly added objective is invisible until you display it).

What You Know Now

You can use the first of the book’s three storage tools. You know a scoreboard is a table pairing score holders with objectives, that every score is a whole 32-bit integer, and that an objective’s criterion decides whether you drive the number (dummy) or the game does (automatic criteria like deathCount). You can create objectives with scoreboard objectives add, change scores with set / add / remove (and the careful difference between reset and set 0), show a score with scoreboard objectives setdisplay in the sidebar, list, or below_name slot, and stash global numbers on fake players (with a # prefix to hide them). And you’ve finally delivered on the Chapter 4 promise: execute if score, both the two-score comparison (@s A = @s B) and the matches <range> form, to branch a command on a number. Your mypack pack now shows a live “Mob Kills” counter on the sidebar. Next chapter: command storage, for all the data that isn’t a single number.