Blame
|
1 | <!-- Generated from applied-computing-au vic/unit3-4/sat/C06-2026/C06-Reference-Godot by port-reference-godot-to-wiki.py — do not hand-edit; re-run the port. --> |
||||||
|
2 | # Data Sources — GDScript |
||||||
| 3 | ||||||||
| 4 | *VCE scope only. The study design names exactly three data sources: **plain text (TXT), delimited (CSV) and XML files**. JSON, databases (SQLite), REST APIs and Godot's own `.tres`/`.res` resources are **out of scope** — they do not earn source codes, however natural they feel in Godot.* |
|||||||
| 5 | ||||||||
| 6 | **Skill codes on this page** |
|||||||
| 7 | ||||||||
| 8 | | Code | Level | Skill | |
|||||||
| 9 | |---|---|---| |
|||||||
| 10 | | C644 | 7–8 | uses appropriate data types, data structures and data sources — *"including plain text, delimited and XML files"* | |
|||||||
| 11 | | C645 | 7–8 | describes, in the internal documentation, why the selected data sources were used | |
|||||||
| 12 | | C657 | 9–10 | uses a range of data types, data structures and data sources | |
|||||||
| 13 | | C658 | 9–10 | explains, in the internal documentation, why the selected data types, data structures and data sources were used | |
|||||||
| 14 | ||||||||
| 15 | **Before you build:** a data layer built on JSON, SQLite or `.tres` earns **none** of C644–C658, however natural those feel in Godot — and graders (human or AI) apply that rule mechanically. The sources you demonstrate must be TXT, CSV and/or XML. |
|||||||
| 16 | ||||||||
| 17 | ## The three sources |
|||||||
| 18 | ||||||||
| 19 | **Definitions.** |
|||||||
| 20 | ||||||||
| 21 | - *Plain text (TXT) file:* a structured file that contains characters of readable data. |
|||||||
| 22 | ||||||||
| 23 | - *CSV:* a comma-separated value file — a delimited file separated by commas. The *delimiter* is the character separating the values. A CSV is a flat table: rows, each row the same fields. |
|||||||
| 24 | ||||||||
| 25 | - *XML:* Extensible Markup Language — data in **elements** marked by opening and closing **tags**. Elements can hold other elements, so the data is **nested** rather than flat: a *tree* with a *root element* at the top, and a *prolog* (version, encoding) before the content. |
|||||||
| 26 | ||||||||
| 27 | **Know the difference:** `res://` is your project's read-only data; `user://` is the writable save location. Naming that in validation is easy marks. club-ladder runs on exactly this split — the bundled season ships in `res://seasons/`, the player's copy lives at `user://ladder.xml`, and `load_season()` picks between them with one `if FileAccess.file_exists(save_path)`. |
|||||||
| 28 | ||||||||
| 29 | ## TXT in Godot |
|||||||
| 30 | ||||||||
| 31 | ```gdscript |
|||||||
| 32 | # C644 — TXT source: write |
|||||||
| 33 | var file = FileAccess.open("user://balance.txt", FileAccess.WRITE) |
|||||||
| 34 | file.store_line(str(balance)) |
|||||||
| 35 | ``` |
|||||||
| 36 | ||||||||
| 37 | ```gdscript |
|||||||
| 38 | # C644 — TXT source: read |
|||||||
| 39 | var file = FileAccess.open("user://balance.txt", FileAccess.READ) |
|||||||
| 40 | balance = float(file.get_line()) |
|||||||
| 41 | ``` |
|||||||
| 42 | ||||||||
| 43 | ## CSV in Godot |
|||||||
| 44 | ||||||||
| 45 | `FileAccess` speaks CSV natively — `get_csv_line()` and `store_csv_line()` handle the delimiter for you. |
|||||||
| 46 | ||||||||
| 47 | ```gdscript |
|||||||
| 48 | # C644 — CSV source: read journeys.csv, one journey per row (zone,fare) |
|||||||
| 49 | var journeys: Array[Dictionary] = [] |
|||||||
| 50 | var file = FileAccess.open("user://journeys.csv", FileAccess.READ) |
|||||||
| 51 | file.get_csv_line() # skip the header row |
|||||||
| 52 | while not file.eof_reached(): |
|||||||
| 53 | var row: PackedStringArray = file.get_csv_line() |
|||||||
| 54 | if row.size() >= 2: |
|||||||
| 55 | journeys.append({"zone": int(row[0]), "fare": float(row[1])}) |
|||||||
| 56 | ``` |
|||||||
| 57 | ||||||||
| 58 | ```gdscript |
|||||||
| 59 | # C644 — CSV source: write journeys.csv |
|||||||
| 60 | var file = FileAccess.open("user://journeys.csv", FileAccess.WRITE) |
|||||||
| 61 | file.store_csv_line(PackedStringArray(["zone", "fare"])) # header |
|||||||
| 62 | for journey in journeys: |
|||||||
| 63 | file.store_csv_line(PackedStringArray([str(journey["zone"]), str(journey["fare"])])) |
|||||||
| 64 | ``` |
|||||||
| 65 | ||||||||
| 66 | **In tile-matching-game** (`deck.gd`) — the whole game is driven by one CSV read; each row becomes a record in an array: |
|||||||
| 67 | ||||||||
| 68 | ```gdscript |
|||||||
| 69 | # tile-matching-game — deck.gd |
|||||||
| 70 | var file := FileAccess.open(path, FileAccess.READ) # C644 — CSV source |
|||||||
| 71 | var header := file.get_csv_line() # skip the pair,text,image header |
|||||||
| 72 | while not file.eof_reached(): |
|||||||
| 73 | var row := file.get_csv_line() |
|||||||
| 74 | if row.size() < 2: |
|||||||
| 75 | continue |
|||||||
| 76 | cards.append({"pair": row[0].strip_edges(), "text": row[1].strip_edges()}) |
|||||||
| 77 | ``` |
|||||||
| 78 | ||||||||
| 79 | The real file (`decks/spanish.csv`): |
|||||||
| 80 | ||||||||
| 81 | ```csv |
|||||||
| 82 | pair,text,image |
|||||||
| 83 | dog,perro, |
|||||||
| 84 | dog,dog, |
|||||||
| 85 | cat,gato, |
|||||||
| 86 | cat,cat, |
|||||||
| 87 | ``` |
|||||||
| 88 | ||||||||
| 89 | Defensive touches worth copying: check `FileAccess.file_exists(path)` first, skip short rows, `strip_edges()` every field. And a Godot trap: **`.csv` imports as a translation file by default** — select the file, Import tab → *Keep File (exported as is)* → Reimport, or deck loading breaks in exported builds. |
|||||||
| 90 | ||||||||
| 91 | ## XML in Godot |
|||||||
| 92 | ||||||||
| 93 | Read XML with the built-in `XMLParser`. There is no built-in XML *writer* — either write your outputs as TXT/CSV, or build the XML text yourself as club-ladder does below. |
|||||||
| 94 | ||||||||
| 95 | ```xml |
|||||||
| 96 | <?xml version="1.0" encoding="UTF-8"?> |
|||||||
| 97 | <fares> |
|||||||
| 98 | <fare zone="1" cost="5.30"/> |
|||||||
| 99 | <fare zone="2" cost="8.00"/> |
|||||||
| 100 | </fares> |
|||||||
| 101 | ``` |
|||||||
| 102 | ||||||||
| 103 | ```gdscript |
|||||||
| 104 | # C644 — XML source: read fares.xml into a Dictionary (zone → cost) |
|||||||
| 105 | var fares: Dictionary = {} |
|||||||
| 106 | var parser = XMLParser.new() |
|||||||
| 107 | parser.open("res://data/fares.xml") |
|||||||
| 108 | while parser.read() == OK: |
|||||||
| 109 | if parser.get_node_type() == XMLParser.NODE_ELEMENT \ |
|||||||
| 110 | and parser.get_node_name() == "fare": |
|||||||
| 111 | var zone: int = int(parser.get_named_attribute_value("zone")) |
|||||||
| 112 | fares[zone] = float(parser.get_named_attribute_value("cost")) |
|||||||
| 113 | ``` |
|||||||
| 114 | ||||||||
| 115 | Here `<fares>` is the root element and each `<fare>` is a child element carrying its data in attributes. |
|||||||
| 116 | ||||||||
| 117 | **In club-ladder** (`season.gd`) — XML both ways, in a studied project. Reading, with a `_attribute()` helper that falls back when a value is missing ("real files are often missing things"): |
|||||||
| 118 | ||||||||
| 119 | ```gdscript |
|||||||
| 120 | # club-ladder — season.gd |
|||||||
| 121 | while parser.read() == OK: # C644 — XML source |
|||||||
| 122 | if parser.get_node_type() != XMLParser.NODE_ELEMENT: |
|||||||
| 123 | continue |
|||||||
| 124 | match parser.get_node_name(): |
|||||||
| 125 | "season": |
|||||||
| 126 | season.title = _attribute(parser, "title", "Season") |
|||||||
| 127 | "team": |
|||||||
| 128 | var team_name := _attribute(parser, "name", "") |
|||||||
| 129 | if team_name == "": |
|||||||
| 130 | continue # a team with no name is not worth keeping |
|||||||
| 131 | season.teams.append(Team.new(team_name, |
|||||||
| 132 | int(_attribute(parser, "wins", "0")), |
|||||||
| 133 | int(_attribute(parser, "draws", "0")), |
|||||||
| 134 | int(_attribute(parser, "losses", "0")))) |
|||||||
| 135 | ``` |
|||||||
| 136 | ||||||||
| 137 | And writing — the season builds the text line by line, escaping XML's five special characters first: |
|||||||
| 138 | ||||||||
| 139 | ```gdscript |
|||||||
| 140 | # club-ladder — season.gd |
|||||||
| 141 | lines.append('<season title="%s">' % _escape(title)) # C644 — XML written by hand |
|||||||
| 142 | for team in teams: |
|||||||
| 143 | lines.append('\t<team name="%s" wins="%d" draws="%d" losses="%d"/>' |
|||||||
| 144 | % [_escape(team.team_name), team.wins, team.draws, team.losses]) |
|||||||
| 145 | lines.append("</season>") |
|||||||
| 146 | file.store_string("\n".join(lines) + "\n") |
|||||||
| 147 | ``` |
|||||||
| 148 | ||||||||
| 149 | A team called "Smith & Sons" would corrupt the file without `_escape()` — and the `&` must be replaced first, or it would mangle the entities just added. |
|||||||
| 150 | ||||||||
| 151 | ## C644 — Uses appropriate types, structures and sources |
|||||||
| 152 | ||||||||
| 153 | Level 7–8 is the three layers working together: a file (**source**) parsed into a **structure** of typed **values** — like the CSV reader above (`journeys.csv` → `Array[Dictionary]` → `int` and `float` fields), labelled: |
|||||||
| 154 | ||||||||
| 155 | ```gdscript |
|||||||
| 156 | # C644 — CSV source → Array[Dictionary] structure → typed values |
|||||||
| 157 | ``` |
|||||||
| 158 | ||||||||
| 159 | ## C645 — Why these data sources |
|||||||
| 160 | ||||||||
| 161 | Level 7–8 asks you to **describe** the reason — a sentence or two per source, in internal documentation: |
|||||||
| 162 | ||||||||
| 163 | ```gdscript |
|||||||
| 164 | # C645 — CSV for journeys: one journey per row with the same two fields; |
|||||||
| 165 | # a flat table that also opens in Excel for checking |
|||||||
| 166 | ``` |
|||||||
| 167 | ||||||||
| 168 | **Described in the wild:** tile-matching-game's README describes its source choice at exactly this level — the game "reads its cards from a CSV file, so the same game can be re-skinned for any subject … without changing a line of code." One sentence that says why CSV earns its place. |
|||||||
| 169 | ||||||||
| 170 | ## C657 and C658 — Range, and the full explanation |
|||||||
| 171 | ||||||||
| 172 | **C657** (9–10): a **range** — several types, several structures, several sources, each doing a job the others could not. A Myki-app spread using only VCE sources: XML fare table in `res://` (nested: zones × day types), CSV journey log in `user://` (flat table), TXT save file in `user://` (one balance value). |
|||||||
| 173 | ||||||||
| 174 | **C658** (9–10): **explains** why — reasons that weigh alternatives: |
|||||||
| 175 | ||||||||
| 176 | ```gdscript |
|||||||
| 177 | # C658 — chose CSV for the journey log: flat table, one row per journey; |
|||||||
| 178 | # XML would wrap every value in tags for no gain. Kept the fare table in |
|||||||
| 179 | # XML: fares nest by zone then day type, and nesting is what XML does |
|||||||
| 180 | # that CSV cannot. TXT for the save file: one value needs no structure. |
|||||||
| 181 | ``` |
|||||||
| 182 | ||||||||
| 183 | **Explained in the wild:** `club-ladder/season.gd`'s header comment explains its XML design *by weighing the alternative* — every value is an attribute because "one call gets each one", whereas nested `<name>` tags would mean "tracking which tag you are inside while text arrives separately". That is C658-grade reasoning, written where the marker will find it. |
|||||||
| 184 | ||||||||
| 185 | The verb ladder (outlines → identifies → describes → explains) is in the [C06 home page](/sd/C06/C06-home). |
|||||||
| 186 | ||||||||
| 187 | ## Check Your Understanding |
|||||||
| 188 | ||||||||
| 189 | 1. Which sources earn C644–C658? |
|||||||
| 190 | ||||||||
| 191 | >| ### Answer |
|||||||
| 192 | >| Only TXT, CSV and XML. JSON, SQLite and `.tres` earn nothing here. |
|||||||
| 193 | ||||||||
| 194 | 2. What reads XML in Godot, and what is missing? |
|||||||
| 195 | ||||||||
| 196 | >| ### Answer |
|||||||
| 197 | >| `XMLParser` reads it; there is no built-in writer — build the text yourself, escaping `&` first. |
|||||||
| 198 | ||||||||
| 199 | 3. `res://` vs `user://`? |
|||||||
| 200 | ||||||||
| 201 | >| ### Answer |
|||||||
| 202 | >| `res://` is the project's read-only data; `user://` is the writable save location. |
|||||||
| 203 | ||||||||
| 204 | 4. You add `fares.csv` to your project and loading breaks in the exported build. First thing to check? |
|||||||
| 205 | ||||||||
| 206 | >| ### Answer |
|||||||
| 207 | >| The Import setting — Godot treats `.csv` as a translation file by default. Set **Import As: Keep File (exported as is)** and reimport. |
|||||||
