Hello! Welcome back to our course on building a life simulation game.
In our last lesson, we did some excellent work setting up a reusable GameWorld_Template scene in Unity. We established a clean scene hierarchy, configured high-quality lighting and post-processing, and even blocked out a basic room with ProBuilder. We now have a visual stage for our game.
Today, we'll define the data that describes this world. The learning outcome for this lesson is to define a JSON schema for a pre-built world containing lots and character spawn locations. This is a crucial step that bridges your Unity frontend and the Rust backend. While Unity knows where everything is in the scene, our Rust engine needs a structured, language-agnostic data file to understand the world's layout for managing simulation logic.
1. From Visual Scene to Data Contract
In game development, we often need to separate an object's visual representation from its underlying data. Your Unity scene is the visual part. The JSON file we're designing today will be the data part. To ensure this data is always correct and well-structured, we use a JSON Schema.
A JSON Schema is a formal "blueprint" or "contract" that a JSON file must follow. It defines:
- What properties an object must have (e.g., a
worldmust have aname). - The data type of each property (e.g.,
nameis a string,idis a number). - Which properties are required and which are optional.
- The structure of nested objects and arrays.
Using a schema ensures that both our Unity C# code and our Rust engine agree on what a "world" is, preventing bugs and making our architecture robust.

2. A Refresher on JSON and an Introduction to Schemas
Before we design our own schema, let's quickly review the fundamentals of JSON and what a JSON Schema looks like.
First, let's watch a brief overview of JSON syntax. Since you have a computer science background, this should be a quick refresher on key-value pairs, objects, and arrays.
This video from Web Dev Simplified provides a concise and clear explanation of JSON's structure and data types.
Please watch from 01:54 to 06:23. Focus on: The six native data types (string, number, boolean, null, array, object). How objects ({}) use double-quoted keys and values. How arrays ([]) hold lists of values. The concept of nesting objects and arrays to create hierarchical data.
Now that JSON syntax is fresh in your mind, let's see what a JSON Schema is. This next video introduces the core ideas and vocabulary of schema design.
What is a JSON Schema? Generate, Modify, and Understand a JSON Schema: Example
This video from Brandan Jones explains the purpose of a JSON Schema and its main components.
Watch the first two minutes (from 00:00 to 02:07). Pay close attention to keywords like $schema, title, description, type, properties, and definitions.
These two conceptsβJSON for the data and JSON Schema for the rulesβare the foundation for our world definition.
3. Designing the World Schema
We will now create a file named world.schema.json. This file will contain the rules for any valid world file in our game. We'll build it up piece by piece.
Top-Level World Object
Our world will be a single JSON object. At the highest level, it needs a name, a list of lots, and a list of spawn locations for characters.
Let's start our world.schema.json file with this structure:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Life Simulation Game World",
"description": "Defines the structure for a pre-built game world, including its lots and character spawn points.",
"type": "object",
"properties": {
"world_name": {
"description": "The display name of the world.",
"type": "string"
},
"lots": {
"description": "A list of all lots available in the world.",
"type": "array"
},
"spawn_locations": {
"description": "A list of predefined spawn locations for characters.",
"type": "array"
}
},
"required": [
"world_name",
"lots",
"spawn_locations"
]
}
This is a good start. We've defined that a valid world JSON must be an object with three required properties: world_name, lots, and spawn_locations.
Defining Reusable Components with $defs
Right now, the lots and spawn_locations properties are just generic arrays. We need to specify what kind of objects can go inside them. A powerful feature of JSON Schema is the ability to define reusable structures in a section called $defs (or definitions) and then reference them using $ref. This keeps our schema clean and avoids repetition.
This approach is common in game development, as explained in the following article.
How to apply JSON Schema in game development
This article from Medium, 'How to apply JSON Schema in game development', provides an excellent, practical example of defining a schema for game layout objects. Pay attention to how it separates the main schema from the component definitions.
Read the sections 'Defining the schemaToLoad.json' and 'Defining the schemaDefinitionsToLoad.json'. Notice how the main file uses $ref to point to object structures defined in another file's definitions block. We will use the same principle, but keep everything in one file for simplicity by using a top-level $defs section.
Inspired by this, let's add a $defs section to our schema and define structures for a lot and a vector3.
1. The vector3 Definition: A 3D coordinate is used in many places (position, size), so it's a perfect candidate for a reusable definition.
"$defs": {
"vector3": {
"description": "A 3D vector with x, y, and z components.",
"type": "object",
"properties": {
"x": { "type": "number" },
"y": { "type": "number" },
"z": { "type": "number" }
},
"required": ["x", "y", "z"]
}
}
2. The lot Definition: A lot represents a piece of land in our world. It needs an ID, a name, a position, and its dimensions.
"lot": {
"description": "A single plot of land in the world.",
"type": "object",
"properties": {
"id": {
"description": "A unique identifier for the lot.",
"type": "integer"
},
"name": {
"description": "The display name or address of the lot.",
"type": "string"
},
"position": {
"$ref": "#/$defs/vector3"
},
"size": {
"$ref": "#/$defs/vector3"
}
},
"required": ["id", "name", "position", "size"]
}
Notice how we use "$ref": "#/$defs/vector3" to say that the position and size properties must conform to our vector3 definition. The # indicates that we're referencing a path within the current document.
3. The spawn_location Definition: This is simply a point in space where a character can appear.
"spawn_location": {
"description": "A specific point in the world where a character can be spawned.",
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"position": {
"$ref": "#/$defs/vector3"
},
"y_rotation": {
"description": "The initial facing direction of the character (in degrees).",
"type": "number"
}
},
"required": ["id", "position", "y_rotation"]
}
Finally, we update our lots and spawn_locations arrays to use these definitions. We add an items property to specify the schema for each element in the array.
"lots": {
"description": "A list of all lots available in the world.",
"type": "array",
"items": { "$ref": "#/$defs/lot" }
},
"spawn_locations": {
"description": "A list of predefined spawn locations for characters.",
"type": "array",
"items": { "$ref": "#/$defs/spawn_location" }
}
4. The Complete World Schema and an Example
By putting all the pieces together, we have our final world.schema.json. This file is the official blueprint for any world data file our game will load.
Click to view the complete world.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Life Simulation Game World",
"description": "Defines the structure for a pre-built game world, including its lots and character spawn points.",
"type": "object",
"properties": {
"world_name": {
"description": "The display name of the world.",
"type": "string"
},
"lots": {
"description": "A list of all lots available in the world.",
"type": "array",
"items": { "$ref": "#/$defs/lot" }
},
"spawn_locations": {
"description": "A list of predefined spawn locations for characters.",
"type": "array",
"items": { "$ref": "#/$defs/spawn_location" }
}
},
"required": [
"world_name",
"lots",
"spawn_locations"
],
"$defs": {
"vector3": {
"description": "A 3D vector with x, y, and z components.",
"type": "object",
"properties": {
"x": { "type": "number" },
"y": { "type": "number" },
"z": { "type": "number" }
},
"required": ["x", "y", "z"]
},
"lot": {
"description": "A single plot of land in the world.",
"type": "object",
"properties": {
"id": {
"description": "A unique identifier for the lot.",
"type": "integer"
},
"name": {
"description": "The display name or address of the lot.",
"type": "string"
},
"position": {
"$ref": "#/$defs/vector3"
},
"size": {
"$ref": "#/$defs/vector3"
}
},
"required": ["id", "name", "position", "size"]
},
"spawn_location": {
"description": "A specific point in the world where a character can be spawned.",
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"position": {
"$ref": "#/$defs/vector3"
},
"y_rotation": {
"description": "The initial facing direction of the character (in degrees).",
"type": "number"
}
},
"required": ["id", "position", "y_rotation"]
}
}
}
To make this concrete, here is an example of a my_world.json file that is valid according to our schema:
{
"world_name": "Willow Creek",
"lots": [
{
"id": 101,
"name": "Oakenstead",
"position": { "x": 50, "y": 0, "z": 120 },
"size": { "x": 40, "y": 20, "z": 30 }
},
{
"id": 102,
"name": "Crick Cabana",
"position": { "x": -10, "y": 0, "z": 80 },
"size": { "x": 20, "y": 20, "z": 20 }
}
],
"spawn_locations": [
{
"id": 1,
"position": { "x": -12, "y": 0.5, "z": 85 },
"y_rotation": 180.0
},
{
"id": 2,
"position": { "x": 55, "y": 0.5, "z": 122 },
"y_rotation": -90.0
}
]
}
If we were to create a JSON file that violated the schema (e.g., by omitting the id from a lot, or using a string for y_rotation), any tool using the schema would immediately flag it as an error. This is the power of defining a clear data contract.
Conclusion
Excellent work! You have now designed a clean, robust, and extensible data format for our game's worlds. You've created a formal contractβthe JSON Schemaβthat will be the single source of truth for what constitutes a valid world. This is a foundational piece of architecture that will ensure stability as we connect our Unity frontend with our Rust backend.
Key Takeaways:
- JSON Schema is a "blueprint" that defines the structure and rules for a JSON data file.
- It's used in game development to create a strict data contract between different parts of a game, like the engine and the UI, or in our case, the Rust backend and Unity frontend.
- Key schema keywords include
type,properties,required, anditems. - Using
$defsand$refallows you to create reusable components, making your schema cleaner and more maintainable. - We designed a schema for a
worldthat contains aworld_name, an array oflots, and an array ofspawn_locations.
In the next lesson, we will bring this data definition to life. You'll implement a Rust function to load world data from a JSON file, parsing it into Rust structs that mirror the schema we designed today.