anyone actually using C# source generators in Unity yet? tried it for boilerplate and it's… interesting

237 views 0 replies

Been experimenting with Roslyn source generators in a Unity 6 project. Got tired of writing the same component accessor pattern everywhere. GetComponent<Health>() was scattered across 30 scripts with zero guarantees the component is actually attached. The goal: tag a MonoBehaviour with a custom attribute and auto-generate cached, null-checked accessors on a partial class.

The generator itself isn't too bad once you're past setup:

[Generator]
public class ComponentAccessorGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var decorated = context.SyntaxProvider
            .ForAttributeWithMetadataName(
                "GenerateAccessorsAttribute",
                predicate: static (node, _) => node is ClassDeclarationSyntax,
                transform: static (ctx, _) => (INamedTypeSymbol)ctx.TargetSymbol)
            .Collect();

        context.RegisterSourceOutput(decorated, Generate);
    }

    static void Generate(SourceProductionContext ctx, ImmutableArray<INamedTypeSymbol> types)
    {
        foreach (var type in types)
        {
            var deps = type.GetAttributes()
                .Where(a => a.AttributeClass?.Name == "RequireComponentAttribute")
                .Select(a => (INamedTypeSymbol)a.ConstructorArguments[0].Value!)
                .ToList();

            var sb = new StringBuilder();
            sb.AppendLine($"partial class {type.Name} {{");
            foreach (var comp in deps)
            {
                var field = $"_{char.ToLower(comp.Name[0])}{comp.Name[1..]}";
                sb.AppendLine($"    private {comp.Name}? {field};");
                sb.AppendLine($"    protected {comp.Name} {comp.Name} => {field} ??= GetComponent<{comp.Name}>();");
            }
            sb.AppendLine("}");
            ctx.AddSource($"{type.Name}.generated.cs", sb.ToString());
        }
    }
}

The painful part was Unity integration. The generator has to live in a separate .csproj Unity doesn't own, wired in via Directory.Build.props. Unity 6 handles this reasonably well. Rider picks up the generated output fine and hot reload doesn't choke on it. 2022 LTS was more fragile, incremental generators sort of worked but the API felt half-baked in spots. The main annoyance across both is that generated files are invisible in the Unity editor itself, which makes debugging the generator output kind of awkward the first few times.

What I'm genuinely unsure about is the onboarding cost. Anyone joining the project has to understand a build step that's completely invisible in the editor. For a solo project or a small team that's fine. I'd hesitate before dropping this into a larger codebase without solid documentation, because when it breaks it breaks silently and confusingly.

Curious whether anyone else has used source generators in Unity for anything beyond boilerplate reduction, like serialization, event wiring, or inspector generation. Also whether there's a cleaner way to structure the .csproj side that I'm obviously missing.

Moonjump
Forum Search Shader Sandbox
Sign In Register