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
dummyobjective 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), usedummyand 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 compoundminecraft.killed_by:…form. When you want to browse the complete alphabetical list, the in-game command auto-complete (typescoreboard objectives add nameand press Tab) and the wiki’s Scoreboard page are the best places to see every criterion at once. The kill counter below is built ondummyplus 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 feedexecute 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 withscoreboard 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, andbelow_name) are the only slotssetdisplayaccepts. There’s no action-bar display slot. If you want a score in the action bar, that’s a different tool: use the/title … actionbarcommand from Chapter 5 with a text component, which can embed a score via thescoretext component. That’s a text feature, not asetdisplayslot, 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
#namesare 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
scoresselector argument:@e[scores={<name>=<min>..<max>}]. For example,@a[scores={kills=10..}]targets every player whosekillsis ten or more. It’s the same range idea asif 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_setuptwice (say, after a second/reload), the secondscoreboard 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 scoreand thescores=selector argument make numeric thresholds easy; - or have the game track for you: automatic criteria like
deathCountupdate 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.