Log inGet started
← Explore
No cover image yet

Watchful Minds: NPC Brains and Perception Kit

aiKit 1.1.0: enemies, allies and creatures that see, hear, remember and act. Put a Brain on a character, pick one of 16 presets (guard, sniper, swarm, companion, phased boss and more) and press play. Sight cones walls block, hearing that fades with distance, light-sensitive sight, three perception styles you can pick by tag, fading memory, a debug view in the game view, multiplayer. One install.

0score
Updated Oct 20, 2026
ainpcenemybrainbehaviour-treestate-machineperceptionvision-conehearingstealthmultiplayerpresetskit

Watchful Minds: aiKit 1.1.0

Enemy, ally and creature brains for your world, without writing enemy logic. aiKit gives non-player characters senses, memory and decisions. It's made for stealth games, shooters, RPGs, horror games and tower defences.

Put a Brain on a character, pick a preset, press play. Change the preset while the game runs and the character behaves differently at once.

What you get

  • 16 ready presets. Twelve behaviours: guard on patrol, wanderer, melee chaser, ranged attacker that keeps its distance, coward that flees when hurt, ambusher, sniper, healer, swarm member, turret, companion that follows and helps a player, and a boss that changes tactics in three phases. Three perception styles: a sight-cone sentry, a blind listener that hears all round, and a light-sensitive night watch you can sneak past in the dark. Plus a training dummy.
  • Pick a perception style by its tags. AI.styles("night-levels"), AI.styles("stay-quiet"): each style is tagged by what it senses with (sight, hearing, light), what it suits (stealth, horror, guard posts, night levels) and how a player beats it (hide behind walls, stay quiet, stay in the dark).
  • Presets are plain data. Make "a guard that hears better but gives up later" by changing two numbers, no code: AI.definePreset("eagleGuard", { base = "guard", senses = { hearing = { sensitivity = 2 } }, search = { time = 25 } }).
  • Honest senses. Sight in a cone that walls block. Hearing of the noises your game makes (footsteps, gunshots, a thrown bottle), fading with distance and muffled by walls. Optional sight that needs light. A memory of where it last saw or heard someone, fading over time. Characters only ever act on where a player was.
  • Two ways to build your own brains. A small state machine and a small behaviour tree, both used by the built-in presets.
  • A debug view in the game view. Press V while playing to see every character's state, vision cone, hearing range, what it heard and the spots it remembers; B switches between full, labels-only and cones-only.
  • Multiplayer. Every player sees the same characters doing the same things, and any player can be the one they chase or follow.
  • Built on two published kits. navKit moves the characters; SecondWind handles health, damage, death and healing. Both come along with the one install.

The showcase

Press play and you stand in the middle of a ring of 15 areas, one per behaviour and perception style, each with a sign you can read from the spawn. Everything is moving from the first second. V toggles the debug view (or use the button in the bottom-right corner), G throws a bottle, F fires a shot.

Use it in your world

1. Install aiKit.package (guid 549ff927-79bc-4db2-b5b2-35d5f3fd59cb) into /source. navKit, SecondWind, Lifeline and MatchFlow come along with it.

zeromind_install guid=549ff927-79bc-4db2-b5b2-35d5f3fd59cb at=/source

2. Add to scenes/main.scene/build.luau. At the very top:

local AI = require("aiKit.api")
local NK = require("navKit.api")

and at the end of function content(), just before its end:

	-- the ground the characters can walk on (baked when play starts)
	entity.spawn("NavMap").component.add(NK.MAP, { kind = "grid", size = { x = 40, y = 6, z = 40 } })

	-- a wall to hide behind (wide enough to hide the spawn point from the guard)
	local wall = entity.spawn("Wall", { position = { 0, 1, 9 }, scale = { 16, 2, 0.5 } })
	wall.component.add("Model", { model = "cube" })
	wall.component.add("BoxCollider", {})

	-- a guard walking a square behind it
	local guard = entity.spawnSynced("Guard", { position = { -6, 0, 12 } })
	guard.component.add("aiKit.Brain", {
		preset = "guard",
		route = { { -6, 0, 12 }, { 6, 0, 12 }, { 6, 0, 18 }, { -6, 0, 18 } },
	})

	-- V (or the button in the bottom-right corner) shows what it sees, hears and remembers
	AI.addDebugToggle()

3. Press play. The guard walks its square behind the wall. Step into its view: it stares ("Suspicious?"), then runs at you ("Chase!"). Duck behind the wall and it goes to where it last saw you, searches, gives up and returns to its route. Change preset = "guard" to "nightwatch", "turret" or "chaser" to see the difference.

This example was checked by installing aiKit into a brand-new, empty world and pasting it in as written. The package's 72 tests run there after the one install, as they do here (tools.use("tests", "run", { suite = "aiKit" })).

The package README has the full preset reference, the noise table, how to build your own brains, the API and every component field.

Cost

With 30 characters running at once on a desktop: about 2.0 ms per frame of aiKit's own script time with the debug view off (about 3.1 ms counting the engine calls its brains make), about 2.9 ms with it on. navKit adds about 3.8 ms and SecondWind with Lifeline about 3.6 ms, so roughly 0.35 ms per character all in.

Known limits

Open engine issues aiKit is affected by:

  • No engine debug drawing in play cameras (OrigoZero/zero#19540). aiKit draws its own debug view.
  • Tops of 3D text are clipped (OrigoZero/zero#19525): state labels and signs.
  • Kits register late in a browser tab (OrigoZero/zero#19309): a character spawned before its Brain registers starts thinking a moment later.
  • Message subscriptions made in edit mode stop in play (OrigoZero/zero#13591). aiKit subscribes again every play session, so its own messages are not affected; SecondWind's own cross-player requests can go unanswered.

Behaviour to know about:

  • Characters nobody owns are run by the room host; in a brand-new editor world that is every character. aiKit applies its own hits and heals there; SecondWind traps and a game's own HK.damage calls do not reach them.

Not verified yet:

  • Two live players, fully. The basics were checked with two windows: both see the same characters doing the same things; characters see, chase and attack the second player; the second player's noises reach them; their debug view works. Still to re-check with two players: hits across machines, the companion following the second player, and a preset switch from the non-host.
  • The web browser. aiKit uses no desktop-only call but has not been run in a browser tab.

Design limits:

  • Characters have no colliders: players walk through them, and a shot counts as a hit when its line passes close enough to the body.
  • Light is an estimate.
  • Territory is a circle.
  • Hearing does not travel around corners.
  • A long editor session slows multiplayer.

MIT licensed. Notes on how it was built are in NOTES.md.

Comments · 0

Open discussion, no rating attached. Ask questions, swap notes, follow threads.

Sign in to join the discussion.