As an indie developer who also runs the Dolice Labs sites, I've been rebuilding a small Unity game in my spare time alongside the apps I ship solo. The first thing I noticed after bringing Antigravity into that workflow was that it neither "does everything" nor "is useless" — its strengths and weaknesses split cleanly by task.
Rather than abstractly claiming "AI is handy in Unity," I'll show the exact prompts I sent, the code that came back, and the parts I fixed by hand. Where the AI is worth handing work to, and where it's faster to write it yourself, is the whole point of this article.
A player controller comes back "80% done"
I started with the classic: player movement. My prompt was, "A CharacterController script: move with WASD, jump with Space, variable jump height based on how long the button is held, plus ground detection." Here's what came back.
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
public class PlayerController : MonoBehaviour
{
[SerializeField] float moveSpeed = 6f;
[SerializeField] float jumpForce = 7f;
[SerializeField] float gravity = -20f;
[SerializeField] float jumpCutMultiplier = 0.5f; // damp rise on early release
CharacterController cc;
Vector3 velocity;
void Awake() => cc = GetComponent<CharacterController>();
void Update()
{
bool grounded = cc.isGrounded;
if (grounded && velocity.y < 0) velocity.y = -2f; // press into the floor when grounded
float x = Input.GetAxisRaw("Horizontal");
float z = Input.GetAxisRaw("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
cc.Move(move.normalized * moveSpeed * Time.deltaTime);
if (grounded && Input.GetButtonDown("Jump"))
velocity.y = jumpForce;
// release the jump button early to cut the rise = variable jump
if (Input.GetButtonUp("Jump") && velocity.y > 0)
velocity.y *= jumpCutMultiplier;
velocity.y += gravity * Time.deltaTime;
cc.Move(velocity * Time.deltaTime);
}
}The skeleton is fine. Implementing variable jump by damping upward velocity the moment the button is released is a clean, readable approach.
The catch: as written, diagonal movement is normalized with move.normalized, but it adds transform.right and transform.forward, so it drifts once the camera tilts. My case needed camera-relative movement, so I added a step to project the input vector onto the camera's facing direction before normalizing. The AI gets you to "moves with a CharacterController," but it can't read your game's specific camera design. That gap is what "80% done" really means.
Hand the enemy AI the state-machine skeleton
For enemy behavior, generating the skeleton and filling it in beat writing from scratch. Asking for "patrol → detect player → chase → on lost sight go to last position → return to patrol after a timeout, as an enum state machine" yields a template that rarely misses a branch.
public enum EnemyState { Patrol, Chase, Investigate }
public class EnemyBrain : MonoBehaviour
{
[SerializeField] float sightRange = 12f;
[SerializeField] float loseSightAfter = 3f;
EnemyState state = EnemyState.Patrol;
float lostTimer;
Vector3 lastSeen;
void Update()
{
bool canSee = CanSeePlayer();
switch (state)
{
case EnemyState.Patrol:
Patrol();
if (canSee) state = EnemyState.Chase;
break;
case EnemyState.Chase:
ChasePlayer();
if (canSee) { lastSeen = PlayerPos(); lostTimer = 0; }
else { lostTimer += Time.deltaTime;
if (lostTimer > loseSightAfter) state = EnemyState.Investigate; }
break;
case EnemyState.Investigate:
MoveTo(lastSeen);
if (canSee) state = EnemyState.Chase;
else if (ReachedLastSeen()) state = EnemyState.Patrol;
break;
}
}
}What helps here is that the AI lays out the easy-to-forget "what happens after losing sight" as a slot up front. The bodies of CanSeePlayer() and Patrol() depend on project specifics — raycast layer masks, whether you use a NavMesh — so you end up writing those yourself. Skeleton from the AI, internals by hand: that division meshed best.
With shaders, expect a draft and no more
For common effects like a damage flash, having Antigravity sketch a draft saves time. I asked for "not URP Shader Graph, but HLSL — a simple fragment that pushes color toward white/red on hit." Here's the result.
// simple hit flash (_FlashAmount from 0 to 1)
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed3 flash = lerp(col.rgb, fixed3(1, 0.2, 0.2), _FlashAmount);
return fixed4(flash, col.a);
}The idea is sound. But shader syntax and variable names change with Unity's render pipeline (Built-in / URP / HDRP). The AI sometimes mixes pipelines — even when I said URP, it slipped in Built-in-style code. I use it on the assumption that I'll take the draft and adapt it to my pipeline. Trusting a whole shader as-is is still premature, honestly.
Performance investigation is where the AI earns its keep
The surprise was that pinpointing bottlenecks turned out more reliable than code generation. Even a vague "Update is heavy, the frame rate drops" gets Antigravity to list the usual suspects in order.
In my code, for instance, it flagged a spot calling GetComponent every frame inside Update, and another that rebuilt a list with new each frame. Cache the first, reuse the second, and the allocations vanish.
// Before: per-frame GetComponent + new list = generates GC
void Update() {
var rb = GetComponent<Rigidbody>();
var hits = new List<Collider>(Physics.OverlapSphere(transform.position, 3f));
// ...
}
// After: cache the reference, reuse the buffer
Rigidbody rb;
readonly Collider[] buffer = new Collider[16];
void Awake() => rb = GetComponent<Rigidbody>();
void Update() {
int n = Physics.OverlapSphereNonAlloc(transform.position, 3f, buffer);
// ...
}Steering toward allocation-free APIs like OverlapSphereNonAlloc is the kind of move you won't reach for unless you're used to reading the docs. When the profiler lights up red and I can't see why, I now describe the situation to Antigravity first to narrow it down, then confirm with the Unity Profiler — and that order made investigation faster.
Where handing off to the AI didn't pay off
To be honest, some tasks clearly don't benefit.
One is game feel. How good the jump feels, the weight of a hit, the softness of camera follow — those numbers you can only settle by playing it and feeling it with your own hands. Asking the AI to "make it feel good" doesn't land; that's yours to decide by playing.
The other is scene structure and prefab design. Which objects to parent, where to carve out a prefab — that depends heavily on how you plan to extend the game. Leaving it to the AI tended to produce a structure that's hard to revise later. I've settled on keeping the design decisions myself and handing the AI the implementation legwork.
Your next step
If you're adding Antigravity to Unity, I'd suggest not asking for a big feature right away — try it once on a small problem with a known-good answer, like a player controller. Jot down what you had to fix in the returned code, and you'll start to see the context the AI can't read in your project: camera design, render pipeline, naming conventions. Once you know that boundary, deciding what to hand off and what to write by hand gets far faster.