skills/taozhuo/game-dev-skills/building-construction

building-construction

SKILL.md

Building & Construction Patterns


MANDATORY: Stud Material Initialization

CRITICAL: This code block MUST be included at the START of every building script. No exceptions. Always load stud materials BEFORE creating any parts.

--------------------------------------------------------------------------------
-- MANDATORY INITIALIZATION - COPY THIS BLOCK FIRST
--------------------------------------------------------------------------------

local Workspace = game:GetService("Workspace")
local AssetService = game:GetService("AssetService")
local MaterialService = game:GetService("MaterialService")

-- Stud material configuration
local STUD_ASSET_ID = 13719188279  -- 2022 Stud MaterialVariant
local studMaterialName = ""
local studBaseMaterial = Enum.Material.Plastic

-- Load stud materials (MUST run before creating parts)
local function loadStudMaterials()
    -- Check if already loaded in MaterialService
    for _, item in ipairs(MaterialService:GetChildren()) do
        if item:IsA("MaterialVariant") then
            studMaterialName = item.Name
            studBaseMaterial = item.BaseMaterial
            print("[Studs] Found existing: " .. studMaterialName)
            return true
        end
    end

    -- Load via AssetService
    local success, err = pcall(function()
        local asset = AssetService:LoadAssetAsync(STUD_ASSET_ID)
        if asset then
            local matVariant = asset:FindFirstChildWhichIsA("MaterialVariant", true)
            if matVariant then
                matVariant.Parent = MaterialService
                studMaterialName = matVariant.Name
                studBaseMaterial = matVariant.BaseMaterial
                print("[Studs] Loaded: " .. studMaterialName .. " (BaseMaterial: " .. tostring(studBaseMaterial) .. ")")
            end
            asset:Destroy()
        end
    end)

    if not success then
        warn("[Studs] FAILED to load: " .. tostring(err))
        return false
    end

    return studMaterialName ~= ""
end

-- EXECUTE IMMEDIATELY
local studsLoaded = loadStudMaterials()
if not studsLoaded then
    warn("[Studs] Running without stud materials - parts will be smooth plastic")
end

--------------------------------------------------------------------------------
-- STANDARD PART CREATION FUNCTION (Uses loaded stud materials)
--------------------------------------------------------------------------------

local function createPart(size, cframe, color, name, parent)
    local part = Instance.new("Part")
    part.Size = size
    part.CFrame = cframe
    part.Anchored = true
    part.BrickColor = color

    -- Apply stud material (if loaded)
    if studMaterialName ~= "" then
        part.Material = studBaseMaterial
        part.MaterialVariant = studMaterialName
    else
        part.Material = Enum.Material.Plastic
    end

    -- NEVER use legacy studs (causes Z-fighting)
    part.TopSurface = Enum.SurfaceType.Smooth
    part.BottomSurface = Enum.SurfaceType.Smooth

    part.Name = name or "Part"
    part.Parent = parent
    return part
end

--------------------------------------------------------------------------------
-- YOUR CODE STARTS HERE (use createPart function above)
--------------------------------------------------------------------------------

Why This Is Mandatory

Issue Legacy SurfaceType.Studs MaterialVariant (This Pattern)
Z-Fighting YES - 3D geometry overlaps NO - flat PBR texture
Performance Worse - extra geometry Better - texture only
Future-proof NO - deprecated 2018 YES - modern standard
Visual Quality Inconsistent High quality PBR

Critical Rules

  1. ALWAYS load materials BEFORE creating parts
  2. ALWAYS use createPart() function, never raw Part creation
  3. NEVER use SurfaceType.Studs - causes Z-fighting
  4. ALWAYS set surfaces to Smooth - MaterialVariant handles appearance
  5. part.Material MUST match studBaseMaterial - or variant won't apply

Quick Reference Links

Official Documentation:

Community Resources:

  • Asset ID 13719188279 - 2022 Stud MaterialVariant (RECOMMENDED)
  • Asset ID 18987102498 - Full 2008-2024 Studs pack
  • Resurface Plugin - For existing builds

Performance Optimization (Community Consensus)

Part Count Guidelines

  • Optimal: Under 5,000 parts in render distance
  • Per Building: 100-600 parts (no interior)
  • Warning: 9,000+ parts = noticeable fps drops

Critical Rules

  • ALWAYS anchor static parts - moving parts = lag
  • Disable CanCollide + CanTouch on decorative parts
  • Avoid Unions - bad topology, breaks instancing
  • Reuse designs - vary with scale/color/rotation instead of unique parts
  • Enable Streaming in Workspace for large maps

MeshPart Settings (If Using Meshes)

RenderFidelity Use For
Performance Decorative, distant objects
Automatic Interactive items
Precise Major landmarks only
CollisionFidelity Use For
Box Walkthrough objects, tiny items
Hull Trees, varied shapes
Default Terrain, complex structures

Layer System (Avoid Z-Fighting)

IMPORTANT: Never place parts with surfaces at exactly the same Y position. Use a layer system.

-- Define layer heights (build from bottom up)
local LAYERS = {
    GROUND = 0,           -- Base ground level
    PLATFORM = 2,         -- Platforms ON TOP of ground (not overlapping)
    SPAWN = 4,            -- Spawn platforms above main platforms
    DECORATION = 5,       -- Decorations on platforms
}

-- Ground: 2 thick centered at Y=0, top surface at Y=1
local GROUND_TOP = LAYERS.GROUND + 1

-- Platforms sit ON TOP of ground (bottom at Y=1, not Y=0)
createPart(Vector3.new(80, 3, 80), CFrame.new(0, GROUND_TOP + 1.5, 0), color, "Platform", parent)

Natural Map Borders (Community Best Practice)

DO NOT use plain walls. Use mountains + forest + invisible collision.

--------------------------------------------------------------------------------
-- NATURAL BORDER PATTERN
--------------------------------------------------------------------------------

local borderFolder = Instance.new("Folder")
borderFolder.Name = "MapBorder"
borderFolder.Parent = workspace

-- Low-poly mountain
local function createMountain(position, baseSize, height, color)
    local mountain = Instance.new("Model")
    mountain.Name = "Mountain"

    -- Base (wide)
    createPart(Vector3.new(baseSize, height * 0.4, baseSize),
        CFrame.new(position + Vector3.new(0, height * 0.2, 0)),
        color, "Base", mountain)

    -- Middle (narrower)
    createPart(Vector3.new(baseSize * 0.7, height * 0.35, baseSize * 0.7),
        CFrame.new(position + Vector3.new(0, height * 0.55, 0)),
        color, "Mid", mountain)

    -- Peak with snow cap
    createPart(Vector3.new(baseSize * 0.35, height * 0.3, baseSize * 0.35),
        CFrame.new(position + Vector3.new(0, height * 0.85, 0)),
        BrickColor.new("Institutional white"), "Peak", mountain)

    mountain.Parent = borderFolder
    return mountain
end

-- Dense forest cluster
local function createForestCluster(centerX, centerZ, groundY, radius, density)
    for i = 1, density do
        local angle = math.random() * math.pi * 2
        local dist = math.random() * radius
        local x = centerX + math.cos(angle) * dist
        local z = centerZ + math.sin(angle) * dist
        local height = math.random(6, 14)
        createTree(Vector3.new(x, groundY, z), height, borderFolder)
    end
end

-- Invisible collision wall
local function createInvisibleWall(size, cframe)
    local wall = Instance.new("Part")
    wall.Size = size
    wall.CFrame = cframe
    wall.Anchored = true
    wall.Transparency = 1
    wall.CanCollide = true
    wall.Name = "InvisibleBarrier"
    wall.Parent = borderFolder
    return wall
end

-- Add atmosphere fog
local atmosphere = Instance.new("Atmosphere")
atmosphere.Density = 0.3
atmosphere.Haze = 1
atmosphere.Parent = game:GetService("Lighting")

Border Placement Pattern

  1. Mountains - Far outside map edge (50+ studs beyond)
  2. Forest - Dense trees inside map edge
  3. Invisible Wall - At exact map boundary
  4. Atmosphere - Fog to obscure distant edges

Building Patterns

Simple Tree

local function createTree(position, height, parent)
    local tree = Instance.new("Model")
    tree.Name = "Tree"

    -- Trunk
    createPart(Vector3.new(2, height, 2),
        CFrame.new(position + Vector3.new(0, height / 2, 0)),
        BrickColor.new("Reddish brown"), "Trunk", tree)

    -- Leaves (stacked, tapering)
    local leafSizes = {8, 6, 4}
    for i, size in ipairs(leafSizes) do
        createPart(Vector3.new(size, 3, size),
            CFrame.new(position + Vector3.new(0, height + i * 3 - 1, 0)),
            BrickColor.new("Bright green"), "Leaves" .. i, tree)
    end

    tree.Parent = parent
    return tree
end

Castle Components

-- Foundation (widest, ground level)
createPart(Vector3.new(40, 2, 40), CFrame.new(0, 1, 0), BrickColor.new("Dark stone grey"), "Foundation", castle)

-- Walls
createPart(Vector3.new(wallLength, wallHeight, wallThickness), wallCFrame, BrickColor.new("Medium stone grey"), "Wall", castle)

-- Crenellations (on top of walls)
for i = -n, n do
    createPart(Vector3.new(1.5, 2, 1.5), CFrame.new(i * spacing, wallTop + 1, wallZ), BrickColor.new("Medium stone grey"), "Crenel", castle)
end

-- Corner towers (taller than walls)
createPart(Vector3.new(towerSize, towerHeight, towerSize), cornerCFrame, BrickColor.new("Medium stone grey"), "Tower", castle)

-- Tower roofs (stacked, tapering)
for i, size in ipairs({4, 3, 2}) do
    createPart(Vector3.new(size, 1, size), CFrame.new(roofX, roofY + i - 1, roofZ), BrickColor.new("Bright red"), "Roof" .. i, castle)
end

Dragon/Creature (Static Decoration)

-- For STATIC decorations: keep all parts Anchored
-- DO NOT use WeldConstraint for static models

local function createDragon(position, color, scale, parent)
    local dragon = Instance.new("Model")
    dragon.Name = "Dragon"
    local s = scale or 1

    -- Body (main mass)
    createPart(Vector3.new(6*s, 4*s, 10*s), CFrame.new(position), color, "Body", dragon)

    -- Head
    createPart(Vector3.new(3*s, 3*s, 4*s), CFrame.new(position + Vector3.new(0, 1*s, -6*s)), color, "Head", dragon)

    -- Snout
    createPart(Vector3.new(2*s, 1.5*s, 2*s), CFrame.new(position + Vector3.new(0, 0.5*s, -8.5*s)), color, "Snout", dragon)

    -- Neck
    createPart(Vector3.new(2*s, 3*s, 2*s), CFrame.new(position + Vector3.new(0, 2*s, -4*s)) * CFrame.Angles(math.rad(-30), 0, 0), color, "Neck", dragon)

    -- Tail segments
    local tailPos = position + Vector3.new(0, 0, 5*s)
    for i = 1, 4 do
        local tailSize = (5 - i) * s
        createPart(Vector3.new(tailSize, tailSize, 3*s), CFrame.new(tailPos + Vector3.new(0, 0, i * 3*s)), color, "Tail" .. i, dragon)
    end

    -- Wings
    for side = -1, 1, 2 do
        createPart(Vector3.new(8*s, 0.5*s, 6*s),
            CFrame.new(position + Vector3.new(side * 6*s, 2*s, 0)) * CFrame.Angles(0, 0, math.rad(side * 20)),
            color, (side == 1 and "RWing" or "LWing"), dragon)
    end

    -- Legs
    for _, offset in ipairs({{-2, -3}, {2, -3}, {-2, 3}, {2, 3}}) do
        createPart(Vector3.new(1.5*s, 3*s, 1.5*s),
            CFrame.new(position + Vector3.new(offset[1]*s, -2*s, offset[2]*s)),
            color, "Leg", dragon)
    end

    dragon.Parent = parent
    return dragon
end

Model Organization

Workspace/
  MapName/
    Terrain/          -- Ground, borders, natural features
      MainGround
      MapBorder/
        Mountains/
        Trees/
        InvisibleWalls/
    HubArea/          -- Spawn, shops, etc
    ObbyCourse/       -- Stages
    WinnerArea/       -- Victory zone

Rules

  • Use Folders for organization (not Models)
  • Use Models only for geometric objects that move together
  • Set PrimaryPart only for physics-joined models
  • Anchor all static parts

Color Palette (Classic Style)

Color BrickColor Name Use For
Grey "Medium stone grey" Neutral, backgrounds, stone
Dark Grey "Dark stone grey" Shadows, metal, dark accents
Brown "Reddish brown" Wood, doors, trunks
Green "Bright green" Grass, leaves, nature
Red "Bright red" Lava, danger, accents
Blue "Bright blue" Water, ice, sky elements
Yellow "Bright yellow" Gold, highlights, rewards
White "Institutional white" Snow, clean surfaces

Checklist Before Building

  • Stud materials loaded at script start?
  • Using createPart() function (not raw Instance.new)?
  • All surfaces set to Smooth?
  • Layer system defined to avoid Z-fighting?
  • All static parts anchored?
  • Map border uses mountains + forest (not plain walls)?
  • Part count reasonable (<5000 in view)?
Weekly Installs
2
First Seen
Jan 25, 2026
Installed on
windsurf1
opencode1
cursor1
codex1
claude-code1
antigravity1