Building Guide
Learn how to construct buildings using S1MAPI's semantic building API.
Overview
The BuildingBuilder class provides a fluent API for creating complete buildings with walls, floors, roofs, windows, doors, and interior elements.
Basic Building
using S1MAPI.Building;
using S1MAPI.Building.Config;
GameObject shop = new BuildingBuilder("MyDispensary")
.WithConfig(BuildingConfig.Medium)
.AddFloor()
.AddCeiling()
.AddWalls(southDoor: true)
.Build();
shop.transform.position = new Vector3(100, 0, 50);
Building Configuration
Preset Configurations
.WithConfig(BuildingConfig.Tiny) // 3m x 2.4m x 3m
.WithConfig(BuildingConfig.Small) // 5m x 3m x 4m
.WithConfig(BuildingConfig.Medium) // 8m x 3m x 6m
.WithConfig(BuildingConfig.Large) // 12m x 4m x 10m
.WithConfig(BuildingConfig.Huge) // 20m x 6m x 15m
.WithConfig(BuildingConfig.Warehouse) // Industrial style
Custom Configuration
var config = new BuildingConfig
{
Width = 10f,
Height = 4f,
Depth = 8f,
WallThickness = 0.2f,
FloorThickness = 0.1f,
CeilingThickness = 0.1f
};
new BuildingBuilder("CustomBuilding")
.WithConfig(config)
.AddFloor()
.AddCeiling()
.AddWalls()
.Build();
Color and Material Palettes
Basic Colors
new BuildingBuilder("Shop")
.WithConfig(BuildingConfig.Medium)
.AddFloor(color: new Color(0.5f, 0.35f, 0.25f)) // Wood color
.AddCeiling()
.AddWalls(color: new Color(0.9f, 0.9f, 0.9f)) // White walls
.Build();
Custom Palette
var palette = new BuildingPalette
{
FloorMaterial = Materials.ConcreteLightGrey,
FloorColor = new Color(0.75f, 0.75f, 0.75f),
WallMaterial = Materials.GraniteDullSalmonLighter,
WallColor = new Color(0.94f, 0.94f, 0.94f),
CeilingMaterial = Materials.ConcreteLightGrey,
CeilingColor = new Color(0.94f, 0.94f, 0.94f),
TrimMaterial = Materials.BrickWallRed,
TrimColor = new Color(0.6f, 0.3f, 0.2f),
PillarMaterial = Materials.BrickWallRed,
PillarColor = new Color(0.6f, 0.3f, 0.2f),
AccentColor = new Color(0.29f, 0.48f, 0.29f),
AccentMaterial = null,
LightColor = new Color(1f, 0.98f, 0.95f),
LightIntensity = 1.2f
};
new BuildingBuilder("Shop")
.WithConfig(BuildingConfig.Large)
.WithPalette(palette)
.AddFloor()
.AddCeiling()
.AddWalls()
.Build();
Walls and Openings
Simple Openings
.AddWalls(
northDoor: true, // Door on north wall
southDoor: true, // Door on south wall
eastWindow: true, // Window on east wall
westWindow: true // Window on west wall
)
Custom Openings & Offsets
You can combine doors and windows on the same wall, and offset them horizontally:
using S1MAPI.Building.Structural;
.AddWalls(
north: WallOpening.DoorWithWindows(
doorWidth: 1.5f, doorHeight: 2.5f,
leftWindow: WallOpening.Window(width: 1f),
rightWindow: WallOpening.Window(width: 1f)
),
south: WallOpening.Window(width: 2f, height: 1.5f)
)
Interior Walls
Segment your building by defining interior walls. Note that InteriorWallAxis.X walls run along the east-to-west axis, and InteriorWallAxis.Z walls run north-to-south.
.AddInteriorWall(
axis: InteriorWallAxis.X,
position: 4f,
opening: WallOpening.Door()
)
Decorative Elements
.AddRoofTrim(height: 0.3f) // Roofline trim
.AddSecondaryRoofTrim(height: 0.15f) // Secondary trim
.AddCornerPillars(width: 0.5f) // Corner columns
.AddBaseMolding(height: 0.3f) // Baseboard
.AddFoundation(height: 2.0f, expandX: 0.3f, expandZ: 0.3f) // Foundation
.AddStairs(WallSide.South, maxStepHeight: 0.2f, style: StairStyle.ClosedRiser) // Entry stairs
Roofs
Top off the building with generated roofs:
.AddParapetRoof(ParapetPreset.Shallow) // Flat roof with parapet walls
.AddHipRoof(ridgeHeight: 2.0f, overhang: 0.3f) // Sloped hip roof
Environment Preparation
Always clear nature from the construction site to prevent foliage and rustle sounds from interfering with your building:
// 1. Clear trees, foliage, and ambient rustle triggers
TerrainClearer.ClearAroundBuilding(buildingObject, new Vector3(10f, 4f, 10f), new ClearingOptions { Padding = 2f });
// 2. Flatten the terrain directly beneath the foundation
buildingBuilder.FlattenTerrain();
NPC Pathfinding (A* Navigation)
S1MAPI replaces standard NavMesh repairers with a robust custom A* grid system (InteriorPathGrid). This solves edge cases and allows NPCs to navigate cleanly inside tight building footprints:
NavigationBuilder navBuilder = buildingBuilder.CreateNavigationBuilder();
navBuilder.Build(); // Always call Build() after physical walls are placed
Lighting
.AddLights() // Default ceiling lights
.AddLights(intensity: 1.5f) // Custom intensity
.AddLights(color: Color.white) // Custom color
.AddLights(intensity: 1.2f, color: new Color(1f, 0.98f, 0.95f)) // Both
.AddAmbientLighting(intensity: 0.3f) // Ambient fill light
Furniture
Semantic Positioning
.AddFurniture(FurnitureType.Desk, "north") // North wall
.AddFurniture(FurnitureType.Counter, "south") // South wall
.AddFurniture(FurnitureType.Chair, "center") // Center of room
.AddFurniture(FurnitureType.Table, "east") // East side
Exact Positioning
.AddFurniture(
FurnitureType.Counter,
new Vector3(5, 0, 9),
Quaternion.Euler(0, 180, 0),
color: Color.gray
)
Available Furniture Types:
Desk,Counter,Table,ChairShelf,DisplayCabinet,CoffeeTableRug,Box,Sphere,Cylinder(primitive shapes)
Note: For S1 game furniture meshes, use InteriorBuilder instead.
Building Part Registry
When you call builder.Build(), a BuildingPartRegistry component is attached to the root GameObject. This registry catalogs all generated meshes automatically:
BuildingPartRegistry registry = builder.Registry;
// Example: Change the material of all exterior walls at runtime
registry.SetMaterial(BuildingPart.ExteriorWalls, newMaterial);
Prefabs & Multiplayer Sync
Place Schedule 1 game prefabs inside your building using BuildingBuilder or the PrefabPlacer component:
// Fluent API
.AddPrefab(Prefabs.ATM, position, rotation)
Alternatively, you can use PrefabPlacer directly. This is crucial for networked interactables (like Doors and Light Switches) as it utilizes NetworkedPrefabLinker under the hood to perfectly synchronize the object's parent hierarchy to joining clients over FishNet:
PrefabPlacer placer = new PrefabPlacer(building.transform);
placer.Place(Prefabs.MetalGlassDoor, position, rotation, networked: true);
Note: BuildingBuilder.AddPrefab() only accepts PrefabRef and always spawns networked. For non-networked S1 furniture meshes, use InteriorBuilder methods instead.
⚠️ Critical: Networked Parameter
Always set networked: true for prefabs that have a NetworkObject component!
Using networked: false (or omitting it) on networked prefabs like ATM, doors, or storage containers will crash FishNet and break multiplayer. This cannot be recovered without restarting the game.
Networked prefabs include:
- ATM, ModularSwitch
- Doors (any interactive doors)
- Storage containers
- Any prefab with multiplayer synchronization
Non-networked objects:
- Use
InteriorBuildermethods likeAddDesk(),AddChair(),AddPlant()for S1 furniture meshes - These are static decorations without network synchronization
Common Prefabs:
- Networked (require
networked: true):ATM,ModularSwitch,SlidingDoors - Non-networked (decorative): Use
InteriorBuildermethods instead (e.g.,AddDesk(),AddChair())
Doors
// Sliding double doors at door opening
.AddSlidingDoors(
new Vector3(6, -0.058f, 0.0655f), // Position at door opening
Quaternion.Euler(0, 180, 0), // Rotation
"6AM-6PM" // Opening hours sign text
)
Interior Decoration
For complex interiors, use InteriorBuilder:
var interior = new InteriorBuilder(building.transform);
// Add S1 furniture meshes (non-networked decorative objects)
interior.AddDesk(new Vector3(6f, 0f, 9.2f), Quaternion.Euler(0f, 180f, 0f));
interior.AddChair(new Vector3(6f, 0f, 7f), Quaternion.Euler(0f, 0f, 0f));
interior.AddCabinet(new Vector3(0.2f, 0f, 3f), Quaternion.Euler(0f, 90f, 0f));
// Add decorations
interior.AddPlant(new Vector3(1f, 0f, 1f));
interior.AddClock(new Vector3(4f, 2.5f, 9.5f), Quaternion.Euler(0f, 180f, 0f));
// Place networked prefabs (MUST use networked: true for NetworkObject prefabs!)
interior.AddPrefab(
Prefabs.ATM,
new Vector3(3f, 0f, 5f),
Quaternion.identity,
networked: true // CRITICAL: ATM has NetworkObject component
);
// Build organizes all objects under an Interior folder
interior.Build();
Available Furniture Methods:
AddDesk(),AddChair(),AddArmchair(),AddBench()AddTable(),AddOfficeTable(),AddBed()AddCabinet(),AddDrawer(),AddLocker(),AddSafe()AddBox(),AddBarrel(),AddBin(),AddFridge()AddPlant(),AddPlanter(),AddVase(),AddPainting()AddClock(),AddWallClock(),AddToilet()AddComputer(),AddScreen()
Note: The networked parameter is critical for AddPrefab(). See the warning in the Prefabs section above.
Complete Example
using S1MAPI.Building;
using S1MAPI.Building.Config;
using UnityEngine;
public static class BuildingExamples
{
public static GameObject CreateSmallShop(Vector3 position)
{
var palette = new BuildingPalette
{
FloorColor = new Color(0.5f, 0.35f, 0.25f),
WallColor = new Color(0.9f, 0.9f, 0.85f),
CeilingColor = new Color(0.95f, 0.95f, 0.95f),
TrimColor = new Color(0.3f, 0.25f, 0.2f),
LightColor = new Color(1f, 0.95f, 0.9f)
};
return new BuildingBuilder("SmallShop")
.WithConfig(BuildingConfig.Small)
.WithPalette(palette)
.AddFloor()
.AddCeiling()
.AddWalls(southDoor: true, eastWindow: true)
.AddRoofTrim()
.AddBaseMolding()
.AddLights(intensity: 1.0f)
.AddFurniture(FurnitureType.Counter, "south")
.AddFurniture(FurnitureType.Chair, "center")
.Build();
}
}
Complete Example: Full Dispensary
See the MAPITesting repository for a complete working example of a dispensary building with:
- Custom palette with green accents
- Double sliding doors
- Display cabinets for products
- Wall shelves
- Neon GLTF sign
- Integration with S1API for game entities
Next Steps
- GLTF Loading - Add imported 3D models to buildings
- API Reference - Full API docs
- Examples - More code examples