Milestone 1 · Software Technology

Requirements and design: use cases, class and database diagrams

Milestone 1, without code: turn the specification into use cases and user stories with acceptance criteria, and design a model–view class diagram and a database diagram on the GitLab wiki.

14 min readUse cases and use-case diagrams«include» vs «extend»User stories and acceptance criteriaModel–view class diagramDatabase diagramRendering big maps

The first milestone, in week 4, has no code in it at all. You read the specification, decide what the game does and who does it, and draw how the program will be built: use cases, a use-case diagram, a class diagram and a database diagram, written up on the GitLab wiki.

It can feel like paperwork when everyone wants to start programming. It isn't: three people can only work in parallel if they share one picture of the game, and the use cases you write now are the yardstick of the next two milestones – "30% of the use cases work" means nothing until the use cases exist.

What milestone 1 asks for

Everything in M1 grows out of the use cases (Fig. 1). The deliverable is the wiki of your GitLab project – not a Word file, not a PDF.

Specificationthe game you getUse caseswho does whatUse-case diagramthe overviewUser stories+ acceptance criteriaClass + DB diagramsmodel, view, storageGitLab wikithe M1 deliverableThe stories become the first issues of M2; the class diagram becomes the package structure.
Fig. 1 · One chain: specification → use cases → diagrams and stories → wiki. A gap early in the chain shows up in every later milestone.
ArtifactWhat it answers
Use cases (text)What can each actor do with the game, step by step, including what goes wrong?
Use-case diagramWhich actors and use cases exist, and how do they relate – on one page?
User stories + acceptance criteriaWhat exactly does "done" mean for each feature?
Class diagramWhich classes exist, in which layer, and who knows about whom?
Database diagramWhat does the game store – saves, high scores, settings – and in what structure?

Use cases

A use case (használati eset) is a goal an actor reaches through the system: "start a new game", "save the game", "build a road". An actor is a role outside the system – a player, a second player on the same keyboard, sometimes a timer or another system. Find them by reading the specification and underlining every "the player can …".

A use case written out has a fixed shape – the main flow, plus what happens when things go differently:

A use case description

Use case:        Load game
Actor:           Player
Precondition:    the main menu is open; at least one save file exists
Main flow:       1. The player chooses "Load game".
                 2. The game lists the save files, newest first.
                 3. The player picks one.
                 4. The game validates the file and restores the board, the players and the score.
                 5. The game continues from the saved state.
Alternative:     4a. The file is corrupt → the game shows a message and stays in the menu.
Postcondition:   the running game is exactly the saved one

The use-case diagram is the one-page overview: actors outside the system boundary, use cases inside it, lines for "this actor does it" (Fig. 2).

PlayerGame (system boundary)Start new gameMove characterPause gameSave gameLoad gameChoose mapPick up itemValidate save file«include»«include»«extend»«include»always runs as partof the base use case«extend»optional, only whena condition holdsLinesactor ↔ use case:"this actor does it"
Fig. 2 · Use cases sit inside the system boundary. «include» arrows point to the part that always runs; «extend» arrows point from the optional behaviour to the use case it extends.
«include»
The base use case always runs the included one. Load game always validates the save file. Use it to share a step between use cases.
«extend»
The extension runs only under a condition. Moving onto an item extends Move character with Pick up item.
System boundary
The box that separates the game from its actors. Everything inside is what you build.

User stories and acceptance criteria

A user story is the same goal in one sentence – who, what, why – plus acceptance criteria: the checks that decide whether it is done. The criteria are the most useful part. They tell the developer when to stop, the reviewer what to try, and the tester what to automate (Fig. 3).

A story with its criteria

Title:   Implement save/load
Story:   As a player, I want to save the game, so that I can continue later.

Acceptance criteria
- [ ] Save creates a file in the user's save folder
- [ ] Load restores the exact game state, including moving units
- [ ] Error case: loading a corrupt file shows a message and keeps the current game
Issue: Implement save/loadAs a player, I want to save the game,so that I can continue later.Acceptance criteria✓ save creates a file in the save foldersaveCreatesFile()✓ load restores the exact game stateloadRestoresExactState()✓ error: corrupt file → message, game keptcorruptFileKeepsCurrentGame()unit tests in the model (from M3)One criterion, one check: the reviewer tries it by hand, the model gets a unit test.
Fig. 3 · Each acceptance criterion becomes a check. The error case is the one most often missing – and the one most often broken in the final demo.

The class diagram: model and view

The course requires at least a two-layer architecture: a model that holds the rules and the state of the game, and a view that draws it and turns input into calls on the model. MVC – with a separate controller – is fine too. The rule that makes it work is one-way: the view knows the model, the model never knows the view (Fig. 4).

view – windows, drawing, inputGameWindow+ showMenu()+ showGame(game)BoardPanel- game: Game+ paint(g)+ onTileClicked(x, y)MenuPanel+ onNewGame()+ onLoadGame()model – rules and state, no UI imports«interface»GameListener+ boardChanged()+ gameOver(result)Game- board: Board- players: List<Player>+ start(map)+ step(direction)+ addListener(l)Board- tiles: Tile[][]+ tileAt(x, y)+ isFree(x, y)SaveService+ save(game, file)+ load(file)usesimplementsnotifiesreads / writesThe model knows the view onlythrough this interface.
Fig. 4 · Every arrow between the layers points down, into the model. The model reaches the view only through an interface it defines itself – GameListener – which the view implements.

The interface trick is the observer pattern: the model publishes "the board changed", and whoever listens repaints. The model can then be created, played and tested without a window – which is exactly what the unit tests of M3 need.

Game.java – the model notifies, it doesn't draw

// model package – no javax.swing / java.awt import anywhere in it
public interface GameListener {
    void boardChanged();
    void gameOver(GameResult result);
}

public void step(Direction direction) {           // in Game
    if (state != State.RUNNING || !board.isFree(player.next(direction))) return;
    player.move(direction);
    listeners.forEach(GameListener::boardChanged);  // the view repaints itself
}

Unity and other engines

The same separation is required in Unity. A MonoBehaviour is view and controller: it reads input and moves sprites. The rules – whose turn, what is free, what the score is – live in plain C# classes that never import UnityEngine. Those are the classes you can unit test in EditMode, quickly.

BoardView.cs – the engine calls the model

// Unity: the MonoBehaviour is the view and controller; the rules live in plain C# classes
public class BoardView : MonoBehaviour
{
    private Game game;                      // model class – no "using UnityEngine" in it

    void Start()  { game = new Game(MapLoader.Load(levelFile.text)); game.BoardChanged += Redraw; }
    void Update() { if (Input.GetKeyDown(KeyCode.W)) game.Step(Direction.Up); }
    void Redraw() { /* move the sprites to the positions the model reports */ }
}

On the wiki, the class diagram can be text that GitLab renders, so it changes in the same review as the code:

Class diagram page on the wiki (PlantUML)

```plantuml
@startuml
package model {
  interface GameListener {
    +boardChanged()
    +gameOver(result)
  }
  class Game {
    -board: Board
    +step(direction)
    +addListener(l: GameListener)
  }
  Game "1" *-- "1" Board
  Game --> GameListener : notifies
}
package view {
  class BoardPanel
}
BoardPanel ..|> GameListener
BoardPanel --> Game
@enduml
```

The database diagram

The database diagram shows what the game stores and how the pieces relate: saved games, high scores, players, settings. Most teams store a save as one serialized blob (JSON) next to a few searchable columns, which is a perfectly good design (Fig. 5).

saved_gamesid PKplayer_id FKmap_namestate (JSON)saved_atplayersid PKnamecreated_athigh_scoresid PKplayer_id FKscoreachieved_at*1*1PK = primary key, FK = foreign key. One player – many saved games, many high scores.
Fig. 5 · Three tables and two one-to-many relations. Draw keys explicitly – the arrows are only as good as the foreign keys behind them.

One table of the diagram, as SQL

CREATE TABLE saved_games (
    id         INTEGER   PRIMARY KEY,
    player_id  INTEGER   NOT NULL REFERENCES players(id),
    map_name   TEXT      NOT NULL,
    state      TEXT      NOT NULL,      -- the serialized game, e.g. JSON
    saved_at   TIMESTAMP NOT NULL
);

Designing for big maps

Some designs don't scale, and the usual victim is rendering. A grid of buttons or a table with one cell per tile is quick to build and works for a 10 × 10 board. At 100 × 100 it means ten thousand components that the UI toolkit lays out and repaints, and the game stutters (Fig. 6).

One widget per tilea button, label or table cell for every tile100 × 100 map → 10 000 widgets to lay outOne canvas, visible tiles onlyvisible areadrawn every framethe rest of the map lives only in the modelcost grows with the screen, not the map
Fig. 6 · A widget per tile scales with the map. A single canvas that draws only what is on screen scales with the window.

The scalable version is one drawing surface – a JPanel with paintComponent, a canvas, a tilemap in an engine – that asks the model for the tiles inside the visible area and draws only those:

BoardPanel.java – draw only the visible tiles

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    int firstCol = Math.max(0, camera.x / TILE);
    int lastCol  = Math.min(board.width() - 1, (camera.x + getWidth()) / TILE);
    int firstRow = Math.max(0, camera.y / TILE);
    int lastRow  = Math.min(board.height() - 1, (camera.y + getHeight()) / TILE);
    for (int row = firstRow; row <= lastRow; row++)
        for (int col = firstCol; col <= lastCol; col++)
            drawTile(g, board.tileAt(col, row), col * TILE - camera.x, row * TILE - camera.y);
}

The wiki

The milestone's text goes on the project wiki, edited in the browser or cloned as a git repository. Keep the structure flat and predictable, so the instructor – and you, in week 12 – can find things:

A wiki structure that works

Home                      ← one paragraph about the game + links to every page below
├── Specification         ← the task in your own words; what is core, which sub-tasks you chose
├── Use cases             ← the diagram + one description per use case
├── User stories          ← story + acceptance criteria; these become the M2 issues
├── Class diagram         ← model and view layers, with a few sentences on the key decisions
├── Database diagram      ← tables or the save-file structure
├── UI sketches           ← menu, game screen, dialogs (photos of paper sketches are fine)
└── Team                  ← who owns which area, how you split the work

GitLab renders PlantUML blocks on the wiki, so diagrams can be text: no exported images going stale, and a change to the design is a readable diff.

Use-case diagram on the wiki (PlantUML)

```plantuml
@startuml
left to right direction
actor Player
rectangle Game {
  usecase "Start new game" as Start
  usecase "Choose map" as Map
  usecase "Load game" as Load
  usecase "Validate save file" as Validate
}
Player --> Start
Player --> Load
Start .> Map : <<include>>
Load .> Validate : <<include>>
@enduml
```
The design is not frozen after week 4. When the code teaches you something, update the diagram – the team's Definition of Done includes "wiki updated if the design changed".

Next: milestone 2 turns the stories into issues and the issues into branches.

Summary

  • M1 is design only: use cases, use-case diagram, class and database diagrams – on the GitLab wiki.
  • A use case is a goal an actor names; write the alternative flows; keep them countable and of similar size.
  • User stories carry acceptance criteria, including an error case; they become the M2 issues.
  • At least two layers: the view knows the model, the model never knows the view – also in Unity.
  • Plan rendering for the biggest map: draw only the visible tiles from the model.