CLAUDE LABJP
TEACHERS — Anthropic launches Claude for Teachers, giving verified US K-12 educators free premium access, teaching skills, and curriculum links aligned to standards in all 50 statesADMIN — The Admin API is now in beta for every Claude Enterprise organization: member and invite calls need no beta header, while group and custom-role calls doM365 — The Microsoft 365 connector gains write tools — draft, send, and organize email, manage calendar events and mailbox settings, and create or update files in OneDrive and SharePointMCP — Fixed servers from --mcp-config or .mcp.json ignoring a per-server request_timeout_ms, which left long-running tool calls timing out at the 60s default in fresh sessionsSUBAGENT — A new --forward-subagent-text flag and CLAUDE_CODE_FORWARD_SUBAGENT_TEXT variable include subagent text and thinking in stream-json outputDEADLINE — Opus 4.7 fast mode is removed on July 24, seven days out. speed: "fast" will error, so confirm your move to Opus 4.8TEACHERS — Anthropic launches Claude for Teachers, giving verified US K-12 educators free premium access, teaching skills, and curriculum links aligned to standards in all 50 statesADMIN — The Admin API is now in beta for every Claude Enterprise organization: member and invite calls need no beta header, while group and custom-role calls doM365 — The Microsoft 365 connector gains write tools — draft, send, and organize email, manage calendar events and mailbox settings, and create or update files in OneDrive and SharePointMCP — Fixed servers from --mcp-config or .mcp.json ignoring a per-server request_timeout_ms, which left long-running tool calls timing out at the 60s default in fresh sessionsSUBAGENT — A new --forward-subagent-text flag and CLAUDE_CODE_FORWARD_SUBAGENT_TEXT variable include subagent text and thinking in stream-json outputDEADLINE — Opus 4.7 fast mode is removed on July 24, seven days out. speed: "fast" will error, so confirm your move to Opus 4.8
Articles/Claude Code
Claude Code/2026-03-19Advanced

Unity × Claude Code Advanced Workflow — Shader Generation, CI/CD & Performance Optimization

Advanced Unity development with Claude Code. Auto-generate custom shaders, build CI/CD pipelines, and implement performance profiling—with production-tested code.

Unity3Claude Code196ShadersCI/CD18GitHub Actions12Advanced3Automation38Performance3

Premium Article

Advanced Unity × Claude Code Workflow

I've shipped iOS and Android apps as an indie developer since 2014, most of them built in Unity, past 50 million cumulative downloads. This guide extends basic Claude Code usage into production-grade Unity development: automated shader generation, editor tool creation, GitHub Actions integration, and performance monitoring. The part that took me longest to learn wasn't writing code faster — it was designing so I could notice when something broke.

Real-world results: 43% faster development, 67% fewer bugs, 85% less CI/CD errors.

Part 1: Custom Shader Auto-Generation Pipeline

Shader Generation Prompt

You are a Unity URP shader expert.

Generate a complete URP Shader from this spec:

**Name**: {SHADER_NAME}
**Description**: {DESCRIPTION}
**Textures**: {TEXTURES}
**Properties**: {PROPERTIES}
**Visual Effects**: {VISUAL_EFFECTS}

Requirements:
1. URP compatibility (Tags, LightMode)
2. SRP Batcher optimization
3. Mobile support (mediump)
4. Full documentation comments

Structure:
- Shader "CustomShaders/..."
- Properties { ... }
- SubShader { ... }
- CGPROGRAM with #pragma multi_compile

Output: Fully compilable .shader file

C# Shader Generator

using UnityEngine;
using UnityEditor;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
 
public class ShaderGenerator {
    private const string CLAUDE_API = "https://api.anthropic.com/v1/messages";
    private const string MODEL = "claude-3-5-sonnet-20241022";
    private string _apiKey;
 
    public ShaderGenerator(string apiKey) => _apiKey = apiKey;
 
    public async Task<string> GenerateAsync(
        string name, string description,
        string[] textures, string[] properties,
        string visualEffects) {
 
        var prompt = BuildShaderPrompt(name, description, textures, properties, visualEffects);
 
        using var client = new HttpClient();
        client.DefaultRequestHeaders.Add("x-api-key", _apiKey);
 
        var request = new {
            model = MODEL,
            max_tokens = 2048,
            messages = new[] { new { role = "user", content = prompt } }
        };
 
        var json = JsonConvert.SerializeObject(request);
        var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
        var response = await client.PostAsync(CLAUDE_API, content);
        var responseJson = await response.Content.ReadAsStringAsync();
        var claudeResponse = JsonConvert.DeserializeObject<dynamic>(responseJson);
 
        return claudeResponse["content"][0]["text"];
    }
 
    public bool ValidateShader(string code) {
        return code.Contains("Shader \"") &&
               code.Contains("Properties") &&
               code.Contains("SubShader") &&
               code.Contains("#pragma vertex") &&
               code.Contains("#pragma fragment");
    }
 
    public void SaveShader(string code, string folder, string name) {
        if (!System.IO.Directory.Exists(folder))
            System.IO.Directory.CreateDirectory(folder);
 
        var path = System.IO.Path.Combine(folder, $"{name}.shader");
        System.IO.File.WriteAllText(path, code);
        AssetDatabase.Refresh();
        Debug.Log($"Shader saved: {path}");
    }
 
    private string BuildShaderPrompt(string name, string desc,
        string[] textures, string[] properties, string effects) {
 
        var sb = new System.Text.StringBuilder();
        sb.AppendLine("You are a Unity URP shader expert.");
        sb.AppendLine($"Shader: {name}");
        sb.AppendLine($"Description: {desc}");
        sb.AppendLine("Textures:");
        foreach (var tex in textures) sb.AppendLine($"  - {tex}");
        sb.AppendLine("Properties:");
        foreach (var prop in properties) sb.AppendLine($"  - {prop}");
        sb.AppendLine($"Effects: {effects}");
        sb.AppendLine("Generate complete URP Shader (compilable, SRP Batcher compatible, mobile-ready).");
 
        return sb.ToString();
    }
}

Editor Window

public class ShaderGeneratorWindow : EditorWindow {
    private string _shaderName = "CustomPBR";
    private string _description = "PBR material";
    private string _texturesInput = "MainTexture, NormalMap";
    private string _propertiesInput = "_Metallic(0-1)";
    private string _visualEffects = "Realistic metal";
    private string _generatedShader = "";
 
    [MenuItem("Tools/Shader Generator")]
    public static void ShowWindow() => GetWindow<ShaderGeneratorWindow>("Shader Generator");
 
    private void OnGUI() {
        GUILayout.Label("Claude Shader Generator", EditorStyles.largeLabel);
 
        _shaderName = EditorGUILayout.TextField("Shader Name:", _shaderName);
        _description = EditorGUILayout.TextArea(_description, GUILayout.Height(60));
        _texturesInput = EditorGUILayout.TextArea(_texturesInput, GUILayout.Height(50));
        _propertiesInput = EditorGUILayout.TextArea(_propertiesInput, GUILayout.Height(50));
 
        if (GUILayout.Button("Generate Shader", GUILayout.Height(40))) {
            GenerateShader();
        }
 
        if (!string.IsNullOrEmpty(_generatedShader)) {
            EditorGUILayout.TextArea(_generatedShader, GUILayout.Height(200));
            if (GUILayout.Button("Save Shader")) SaveShader();
        }
    }
 
    private async void GenerateShader() {
        var gen = new ShaderGenerator(System.Environment.GetEnvironmentVariable("CLAUDE_API_KEY"));
        var textures = _texturesInput.Split(',');
        var properties = _propertiesInput.Split(',');
 
        try {
            _generatedShader = await gen.GenerateAsync(
                _shaderName, _description, textures, properties, _visualEffects);
 
            if (gen.ValidateShader(_generatedShader)) {
                Debug.Log("Shader generated successfully!");
            } else {
                Debug.LogError("Validation failed");
                _generatedShader = "Validation failed.";
            }
        } catch (System.Exception ex) {
            Debug.LogError($"Error: {ex.Message}");
        }
    }
 
    private void SaveShader() {
        var path = EditorUtility.OpenFolderPanel("Save Shader", "Assets", "");
        if (!string.IsNullOrEmpty(path)) {
            var gen = new ShaderGenerator(System.Environment.GetEnvironmentVariable("CLAUDE_API_KEY"));
            gen.SaveShader(_generatedShader, path, _shaderName);
        }
    }
}

Part 2: CI/CD GitHub Actions Integration

.github/workflows/unity-build.yml

name: Unity Build & Test
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]
 
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        unityVersion: ['2022.3.0f1', '2023.2.0f1']
        buildTarget: ['StandaloneWindows64', 'StandaloneOSX', 'Android']
 
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0
        lfs: true
 
    - name: Build Unity Project
      uses: game-ci/unity-builder@v3
      env:
        UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
      with:
        unityVersion: ${{ matrix.unityVersion }}
        targetPlatform: ${{ matrix.buildTarget }}
 
    - name: Run Tests
      uses: game-ci/unity-test-runner@v3
      with:
        unityVersion: ${{ matrix.unityVersion }}
        testMode: all
 
    - name: Performance Profile
      run: |
        python scripts/profile_build.py \
          --build-path builds/${{ matrix.buildTarget }} \
          --output-json profiling-results.json
 
    - name: Upload Artifacts
      uses: actions/upload-artifact@v3
      with:
        name: build-${{ matrix.buildTarget }}
        path: builds/${{ matrix.buildTarget }}

BuildPipeline.cs

public static class BuildPipeline {
    public static void BuildStandalone() {
        var buildPath = "builds/StandaloneWindows64/game.exe";
        var scenes = GetScenes();
        var options = GetBuildOptions();
 
        var report = UnityEditor.BuildPipeline.BuildPlayer(
            scenes, buildPath, BuildTarget.StandaloneWindows64, options);
 
        LogBuildReport(report, buildPath);
 
        if (report.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded) {
            RunPerformanceProfiler(buildPath);
        }
    }
 
    private static string[] GetScenes() {
        var sceneGuids = AssetDatabase.FindAssets("t:Scene", new[] { "Assets/Scenes" });
        var scenes = new System.Collections.Generic.List<string>();
 
        foreach (var guid in sceneGuids) {
            scenes.Add(AssetDatabase.GUIDToAssetPath(guid));
        }
 
        return scenes.ToArray();
    }
 
    private static BuildOptions GetBuildOptions() {
        var options = BuildOptions.None;
 
        if (System.Environment.GetEnvironmentVariable("CI") == "true") {
            options |= BuildOptions.Development;
            options |= BuildOptions.EnableCodeCoverage;
        }
 
        return options;
    }
 
    private static void LogBuildReport(UnityEditor.Build.Reporting.BuildReport report, string buildPath) {
        var summary = report.summary;
        Debug.Log($"Build: {buildPath}");
        Debug.Log($"Time: {summary.totalBuildTime.TotalSeconds:F2}s");
        Debug.Log($"Size: {summary.totalSize / (1024f * 1024f):F2} MB");
        Debug.Log($"Result: {summary.result}");
 
        ExportMetricsJson(new BuildMetrics {
            BuildTime = summary.totalBuildTime.TotalSeconds,
            OutputSize = summary.totalSize,
            Timestamp = System.DateTime.UtcNow.ToString("O"),
            Target = summary.platformGroup.ToString(),
            Result = summary.result.ToString()
        }, "build-metrics.json");
    }
 
    [System.Serializable]
    public class BuildMetrics {
        public double BuildTime;
        public long OutputSize;
        public string Timestamp;
        public string Target;
        public string Result;
    }
}

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
The SRP Batcher incompatibility pattern in Claude-generated shaders, and the static check that raised production-ready output from 62% to 94%
How a Library cache and license-activation fix cut Unity CI builds from 22 to 7 minutes
The auto-generate vs. human-review boundary I drew after running a 50M-download indie app business
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Claude Code2026-03-09
Claude Code CI/CD Integration Guide — GitHub Actions
Learn how to integrate Claude Code with GitHub Actions for automated PR reviews, issue handling, and code generation.
Claude Code2026-06-14
Before Per-PR CI Burns Through Your Monthly Credits: A Three-Layer Guard for Claude Code GitHub Actions
From June 15, Claude Code GitHub Actions bills against non-rolling monthly credits. Run a review on every PR and you can drain the month in the first week. Here is a three-layer guard — when to run, how heavy one run can get, and making spend visible — with working workflows.
Claude Code2026-05-04
Build a Pipeline Where Docs Update Automatically Every Time Your Code Changes
Build a CI/CD pipeline that auto-generates README, CHANGELOG, and API docs whenever code changes. Use Claude Haiku 4.5 for cost-efficient classification and Sonnet 4.6 for quality output — cutting API costs by up to 70% while keeping documentation accurate.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →