Imported from souzalucy/unity_ai_self_learning_enemies (
AGENTS.md). Install upstream withnpx skills add souzalucy/unity_ai_self_learning_enemies. Copyright stays with the author.
AGENTS.md — Self-Learning Enemies for Unity
For AI coding agents and developers working on this project. This file explains the architecture, conventions, dependencies, and next steps.
Project Overview
Self-Learning Enemies is a plug-and-play reinforcement learning framework for Unity that creates intelligent enemy AI across multiple game genres (RPG, Shooter, Racing). It wraps Unity's official ML-Agents toolkit (PyTorch-based PPO/SAC) behind a composable component architecture.
Goal: Drop this into any Unity project, snap a few components onto a prefab, run one CLI command, and get a trained neural network driving smart enemy behavior — with zero RL code from the user.
Status: Core framework complete. Ready for integration testing inside a real Unity project with ML-Agents.
Architecture Deep Dive
The 4-Layer Abstraction
EnemyBrain (Agent)
├── ObservationSource[] → CollectObservations(VectorSensor)
├── ActionEffect[] → ApplyActions(float[] discrete, float[] continuous)
└── RewardSource[] → CalculateReward() → float
Discovery: EnemyBrain.Awake() calls GetComponents<T>() for each layer. No manual wiring.
Action Mapping: ConfigureActionSpace() concatenates all DiscreteBranchSizes and sums ContinuousActionCount, then stores offset arrays so it can slice the global action buffer back into per-effect slices at dispatch time.
Observation Ordering: Sources iterated in sibling index order. Must be deterministic. Size mismatches caught and logged.
GenreProfile System
GenreProfile is a ScriptableObject preset — it doesn't drive runtime behavior directly:
- Pre-configures
BehaviorParametersobservation/action sizes - Provides
survivalRewardPerSecond,deathPenalty,objectiveCompleteRewarddefaults - References the correct training YAML path
Default profiles created via GenreProfile.CreateRPGDefaults() (etc.) — factory methods called by EnemyBrainEditor menu items.
Training Pipeline
Unity (C#) Python (mlagents-learn)
────────── ────────────────────────
EnemyBrain.OnActionReceived() ──→ PPO/SAC policy update
↓ ↓
CollectObservations() ←── New action selection
↓
ApplyActions() + CalculateReward()
Communication via gRPC over localhost (transparently handled by ML-Agents).
File Map
Core (Core/)
| File | Role |
|---|---|
GenreProfile.cs |
ScriptableObject with enum, presets, factory methods |
ObservationSource.cs / ActionEffect.cs / RewardSource.cs |
Abstract bases for the 4-layer abstraction |
EnemyBrain.cs (+ partials: StepLogic, ActionMapping, ComponentDiscovery, Heuristic, Telemetry) |
Main Agent: lifecycle, public API, action mapping, discovery, heuristic controls |
IStatusProvider.cs / ITargetProvider.cs |
Decoupled status/target interfaces + SimpleStatusProvider / SimpleTargetProvider |
SoundEventManager.cs |
Global sound event system (Emit, GetRecentSounds) |
CurriculumManager.cs |
Lesson-based difficulty progression + GetParameter |
SquadBrain.cs |
Multi-agent group coordination (SimpleMultiAgentGroup) |
DemoRecorderHelper.cs |
GAIL demo recording wrapper |
DebugGizmos.cs |
Scene view visualization overlay |
TrainingArenaBuilder.cs + RPGArenaBuilder / ShooterArenaBuilder / RacingTrackBuilder |
Procedural arena builders |
BT/ |
Behavior Tree nodes (BTNode, BTComposites, BTLeafs) |
Observations / Actions / Rewards
| Directory | Files |
|---|---|
Observations/ (8) |
ObsSelfTransform, ObsTargetTransform, ObsSelfStatus, ObsRaycastPerception, ObsGridSensor, ObsSoundPerception, ObsWaypointProgress, ObsBehaviorTreeSuggestions |
Actions/ (5) |
ActionNavMeshMovement, ActionRigidBodyMovement, ActionCombat, ActionItemUsage, ActionBehaviorTree |
Rewards/ (5) |
RewardCombatPerformance, RewardSurvival, RewardDistanceManagement, RewardWaypointProgress, RewardCoverUsage |
Editor, Training, Config
| File | Role |
|---|---|
Editor/EnemyBrainEditor.cs |
Custom inspector, live stats, Auto-Configure, profile creator |
Editor/MinigameSceneBuilder.cs |
One-click scene generation |
Editor/Tests/ (6 files, 61 tests) |
Editor unit tests |
Training/*.yaml (4) |
PPO / SAC / GAIL trainer configs |
SelfLearningEnemies.asmdef + Editor/*.asmdef |
Assembly definitions |
.quality-gate.yml + .editorconfig |
AI code-quality gate thresholds + C# style rules |
Total: 69 .cs files (~6,700 lines) — 24 Core, 5 Actions, 8 Observations, 5 Rewards, 8 Editor (incl. 6 tests), 19 Minigames — plus 4 .yaml, 13 .md, 3 .asmdef, and config.
Minigames (Playable Integration Layer)
Minigames/ turns the component library into three playable experiments. It is the "integration
glue" the framework deliberately leaves to you — a player controller, a game-loop manager, and
scripts that wire gameplay events into the reward sources.
| File | Role |
|---|---|
Minigames/Shared/MinigameSettings.cs |
ScriptableObject: genre, experiment mode, difficulty, prefabs |
Minigames/Shared/MinigameComposer.cs |
Public entry points (ConfigurePlayer/ConfigureEnemy) — orchestrates the genre composers |
Minigames/Composers/ (5) |
ComposerUtils (Ensure<T>), BehaviorConfigurator (BP sizing + experiment mode), RpgComposer, ShooterComposer, RacingComposer |
Minigames/Shared/MinigameManager.cs |
Game loop: spawn, win/lose, waves/laps, resets, reward API (delegates state to RoundStateMachine) |
Minigames/Shared/RoundStateMachine.cs |
Serializable win/lose state: score, wave, timer, game-over flag |
Minigames/Shared/EnemySpawner.cs |
Stateless factory for spawning the player + enemies |
Minigames/Racing/RacingRoundController.cs |
Lap/checkpoint bookkeeping for racing |
Minigames/Shared/MinigameHUD.cs |
IMGUI overlay (HP, score, timer, AI reward + last action) |
Minigames/Shared/PlayerController.cs |
WASD + mouse aim + hitscan fire (RPG/Shooter) |
Minigames/Shared/PlayerCarController.cs |
Steer/accel/brake car (Racing) |
Minigames/Shared/PlayerStatus.cs |
Player death → "player lost" |
Minigames/Shared/StatusDamageReceiver.cs |
ICombatTarget/IDamageable wrapper for SimpleStatusProvider |
Minigames/Shared/EnemyWiring.cs |
Death→ReportDeath, hit→RegisterHit, tactical moves |
Minigames/Shared/ActionAimAndShoot.cs |
New ActionEffect: 2 continuous aim + raycast hit/miss (Shooter) |
Minigames/Racing/TrackCheckpoint.cs |
Waypoint trigger → reward + lap counting |
Editor/MinigameSceneBuilder.cs |
One-click scene generation (Tools → … → Minigames) |
Also included: Core/EnemyBrain.Telemetry.cs (read-only reward/step/last-action accessors) and a
SimpleStatusProvider.Configure() method for sizing stats from settings.
Dependencies
Required (Unity)
- Unity 2021.3+
- Unity ML-Agents package (Release 21,
com.unity.ml-agents) - Unity NavMesh (built-in, for
ActionNavMeshMovement)
Required (Python — training only)
- Python 3.8–3.11
- mlagents:
pip install mlagents - PyTorch (auto-installed with mlagents)
Optional
ActionRigidBodyMovementusesWheelColliderfor racing; falls back to Rigidbody forces
Conventions
Naming
- Abstract bases:
ObservationSource,ActionEffect,RewardSource - Concrete:
Obs*for observations,Action*for actions,Reward*for rewards - Namespace:
SelfLearningEnemies(runtime),SelfLearningEnemies.Editor(editor)
Code Patterns
[Tooltip]on all serialized fieldsXmlDocon public API methods- Per-step accumulators reset in
CalculateReward()andOnEpisodeBegin() OnEpisodeBegin()called on all components byEnemyBrain- Optional deps use
TryGetComponent<T>()
API Surface for Game Integration
EnemyBrain.ReportObjectiveComplete()— enemy achieved goalEnemyBrain.ReportDeath()— enemy diedEnemyBrain.ValidateSetup()— debug diagnosticsRewardCombatPerformance.RegisterHit/Miss/Kill()— from damage pipelineRewardWaypointProgress.RegisterWaypointReached()— from checkpoint systemRewardSurvival.ReportDeath/ReportEpisodeComplete()— lifecycleICombatTarget.TakeDamage()— implement on damageable objectsActionCombat.OnAttackExecuted— UnityEvent for VFX/animationActionItemUsage.OnItemUsed— UnityEvent for item effects
Known Limitations
- ML-Agents must be installed —
asmdefreferencesUnity.ML-Agentsby name - BehaviorParameters must be kept in sync — the editor's Auto-Configure button and the runtime
BehaviorConfiguratorboth set sizes, but manual Inspector edits can still drift them - No built-in curriculum loader — YAML configs have commented-out blocks
- Racing forces are untuned — motor/brake/turn values need per-vehicle calibration
ObsSelfStatususes public fields — game health system must update them each frame- Squad coordination is opt-in —
SquadBrainprovides shared rewards, but eachEnemyBrainstill trains independently without it - Heuristic mode limited — 3 discrete + 2 continuous actions hardcoded
- No
OnValidateauto-sync — manual button in editor instead
Next Steps
Immediate — Integration Validation ✅
- Playable minigames for all three genres —
Minigames/(RPG "Arena Brawl", Shooter "Cover Shootout", Racing "Rival Time Trial") - Player controller, game-loop manager, HUD, and reward-wiring glue —
Minigames/Shared/ - One-click scene generation —
Tools → Self-Learning Enemies → Minigames - Test heuristic mode with WASD/Space — switch
MinigameSettings.experimentModeto Heuristic Only - Install into a real Unity project with ML-Agents and run
mlagents-learn rpg_trainer_config.yaml --run-id=testto verify training end-to-end
High Priority ✅
-
ObsGridSensor—Observations/ObsGridSensor.cs(Physics.OverlapBox grid, tag one-hot per cell) -
ObsSoundPerception—Observations/ObsSoundPerception.cs+Core/SoundEventManager.cs(global sound event system) -
ActionBehaviorTree—Actions/ActionBehaviorTree.cs+Observations/ObsBehaviorTreeSuggestions.cs+Core/BT/(hybrid BT + learned actions, FollowBT/OverrideBT branching) -
CurriculumManager—Core/CurriculumManager.cs(lesson thresholds, rolling reward tracking,GetParameterlesson value exposure) - GAIL imitation learning —
Training/gail_trainer_config.yaml+Core/DemoRecorderHelper.cs(demo recording + GAIL reward signal config)
Medium Priority ✅
-
IStatusProvider/ITargetProvider—Core/IStatusProvider.cs+Core/ITargetProvider.cs+ default impls (SimpleStatusProvider, SimpleTargetProvider) - Auto-configure BehaviorParameters —
Editor/EnemyBrainEditor.csAutoConfigureBP()via SerializedObject - Mid-episode safety —
EnemyBrain.SafeRefreshComponents()with component hash detection, called eachOnActionReceived - Training arena builders —
Core/TrainingArenaBuilder.cs(base),Core/RPGArenaBuilder.cs,Core/ShooterArenaBuilder.cs,Core/RacingTrackBuilder.cs
Lower Priority ✅
- Gizmos —
Core/DebugGizmos.cs(raycasts, cover, distance rings, waypoints, reward heatmap overlay) - TensorBoard guide —
Training/TensorBoard_Guide.md(metrics, interpretation, multi-run comparison) - ONNX warm-start —
Training/ONNX_WarmStart_Guide.md(resume, init_path, behavioral cloning pre-training) - Multi-agent SquadBrain —
Core/SquadBrain.cs(SimpleMultiAgentGroup, shared rewards, proximity bonuses) - WebGL/mobile ONNX —
Training/WebGL_Mobile_Guide.md(platform matrix, Barracuda fallback, profiling) - Editor unit tests —
Editor/Tests/EnemyBrainTests.cs+Editor/Tests/BehaviorTreeAndCurriculumTests.cs(26 tests total)
Adding a New Genre
- Add enum to
EnemyGenreinGenreProfile.cs - Add
Create*Defaults()factory method - Add button in
EnemyBrainEditor.OnInspectorGUI() - Add
[MenuItem]inEnemyBrainEditor - Create training YAML in
Training/ - Document in README files
- Add a genre composer class in
Minigames/Composers/and a dispatch case inMinigameComposer - Consider new Observation/Action/Reward components needed
Adding a New Component
- Inherit from
ObservationSource,ActionEffect, orRewardSource - Implement required abstract members
- Place in
Observations/,Actions/, orRewards/ - No registration —
EnemyBrainauto-discovers viaGetComponents<T>()
External Resources
- ML-Agents Docs
- Training Config Reference
- PPO Paper
- SAC Paper
- ICM Curiosity Paper
- Unity ML-Agents Forum
Deployment
Copy self_learning_enemies/ into Assets/SelfLearningEnemies/ of any Unity project with ML-Agents installed via Package Manager.
Project root: /home/lucy/Games/AI_tools/self_learning_enemies/