UGTK cat mascotUGTKengine within an engine Request access

UGTK / Toolkit / Gameplay Systems / Tournament System Completed

Gameplay Systems — UGTK

Tournament System

Summary

The Tournament System generates and manages a single-elimination bracket UI from a list of players. It builds rounds and match slots with configurable orientation, seeding, spacing and connector graphics, supports multiple layout strategies (Standard, Butterfly, Midpoint), lets you declare winners and advance the bracket, and can save/load the whole state via a ScriptableObject snapshot. Optional netcode components let remote players join and report results.


Content


Modules Dependencies

Carried inside the prefabs. These modules are not referenced by the assembly definition - the components are simply already attached to the prefabs this module ships. Remove one of them from the project and the prefab keeps its layout and loses that behaviour, with nothing in the console to say so.

Module Component, and the prefab carrying it
UIModule/TextUpdaterSystem Mn_TextUpdater (in TournamentPlayer), Mn_TextUpdater (in TournamentUser)

Only in the demo scene. The Debug scene of this module also uses UIModule/GradientSystem, UIModule/LayoutAutoResize, UtilitySystems/KeyInputActionSystem, UtilitySystems/PoolingSystem, UtilitySystems/TimingSystem, UtilitySystems/WebParamSystem, UtilitySystems/WindowsDeviceManagement. Nothing in the runtime code needs them: import this module on its own and it works, but the demo scene opens with missing scripts where those components were.


Setup

The bracket is data first and pixels second: SO_TournamentData holds it, and the scene draws it.

  1. Create a SO_TournamentData asset and add the entrants.
  2. Add Mn_TournamentManager to the scene, give it that asset, and give it a SO_TournamentUILayoutDatabase - the profiles inside decide how the bracket is drawn.
  3. Pick the shape with E_TournamentLayoutOrientation. TournamentLayoutFactory builds the matching strategy: LayoutBracketStandard for the usual ladder, LayoutBracketButterfly for two halves meeting in the middle, and the Layout_Midpoint family for the four directions.
  4. TournamentPlayer is the entrant, TournamentUser the participant row, and Mn_TournamentConnectorsGraphic draws the lines between matches. Mn_CanvasZoomer exists because a full bracket does not fit on a screen.

For the networked version, use TournamentHostScene and TournamentClientScene together: Mn_TournamentHostReceiver owns the truth, Mn_TournamentClientSender only asks. The state travels as an S_TournamentSnapshot - the whole bracket, not a diff - so a phone that reconnects halfway through is correct immediately instead of being patched back into place.

A bracket only exists for a power of two. Six entrants become eight with two byes, and where those byes go is the difference between a fair draw and one where the same seed advances free every time. TournamentMathUtils places them; check the result with an entrant count that is not a power of two before you trust a real tournament to it.


How To Use

  1. Add Mn_TournamentManager to a RectTransform under a Canvas, assign the participantSlotPrefab and (optionally) a roundsRoot.
  2. Set the players with SetPlayers(names, rebuildNow) / AddPlayer / RemovePlayerAt, then configure orientation, seedingMode (Ordered/Random) + shuffleSeed, slot size, spacing and connector options.
  3. Call BuildTournament() to generate the bracket (rounds, match slots and connector graphics). Use DestroyGeneratedUI() to clear it.
  4. Advance the bracket with DeclareWinner(round, match, winnerIsA), ForceWinPlayer(name) or ClearMatchResult(...); query state with IsMatchReady / IsMatchResolved / GetAllReadyMatches.
  5. Save and restore the bracket with SaveMomentIntoScriptableObject() and LoadByScriptableObject() (fires onSnapshotSaved / onSnapshotLoaded).
  6. For a higher-level flow use Mn_TournamentController (player count/nickname setup, orientation cycling, validation and onTournamentGenerated / onTournamentGenerationFailed events); for online play add the host/client netcode components.

Testing Scene

Open TournamentScene in the module's Debug/ folder.

What to do, in order:

  1. Build a bracket with eight entrants and walk it to a winner.
  2. Build one with a count that is not a power of two, say six.
  3. Try it with entrant names left blank or padded with spaces.
  4. Switch the layout orientation, including the butterfly.
  5. Use TournamentHostScene and TournamentClientScene together for the networked flow.

Step 2 is the structural case: a bracket only works on a power of two, so six entrants have to be padded with byes, and how those byes are placed decides whether the bracket is fair. Step 3 is the input case — the names come from players, so blanks and stray spaces are normal, not exceptional.

The recording is the setup panel doing its job: a maximum of eight, eight players added with the New default player button, and Generate drawing the bracket - Player 1 against Player 8, Player 5 against Player 3, and so on up to the final. The seeding is random, so pressing Generate again gives a different first round from the same roster, which is the quickest way to see whether your layout survives every pairing.


Technical Info


© 2026 Marcello De Bonis. All rights reserved

Real dependenciesOpen in the map →

Measured from the repository: code references (asmdef) plus prefab and asset GUIDs. Importing this module alone brings in 25 modules in total.