AO Engine Game Development
This skill provides expert guidance for developing 2D multiplayer games in the custom AO engine. The engine uses an entity-component system with built-in networking and does NOT support Unity APIs.
Core Principles
When working with the AO engine, follow these essential guidelines:
Imports and Namespacing
Always start files with `using AO;`Only import from the C# System namespaceDo not attempt to import any other external librariesEntity Management
Create entities using `Entity.Create()`Make entities visible to other clients with `Network.Spawn()` (server-only)Find all instances of a component type: `foreach (var player in Scene.Components<MyPlayer>())`Find specific entities by name: `Entity.FindByName()`Check entity existence with `.Alive()` instead of null checksUse `Entity.LocalScale` instead of `.Scale`Timing and Updates
Use `Time.TimeSinceStartup` for timers and comparisonsDO NOT use coroutines or async timing methodsCompare timestamps directly for time-based logicMath and Vectors
This is a 2D engine - use `Vector2` instead of `Vector3`Use standard library math functionsIf a math function doesn't exist, implement it using standard library primitivesUse `Vector4` to represent colors (RGBA)Random Number Generation
Use the `RNG` class for all randomizationInitialize with `RNG.Seed(1337)` (or your chosen seed)Use `RangeInt` or `RangeFloat` methods onlyDo not attempt other RNG methodsComponent System
Do not use `HasComponent` - only `GetComponent` and `AddComponent` existAll game classes inherit from `Component`, not Unity base classesNo Unity APIs are availableFile Naming
Use PascalCase for all file namesMobile-First Design
These games target mobile platformsDO NOT use keyboard inputDesign for touch interactionsAsset Management
Loading Assets
1. Check available assets by listing files in the `/res` directory (including nested folders)
2. Reference assets using: `Assets.GetAsset<TYPE>("path")`
3. Do NOT prefix paths with `/res`
Common Asset Types
`Texture` - image files`AudioAsset` - sound files`SpineSkeletonAsset` - skeletal animationsSprite Rendering
Sprite Renderer Usage
Type name: `Sprite_Renderer` (note the underscore)Set color using `.Tint` property with `Vector4` valuesDo NOT try to use `.Color` propertyAll UI and sprite rendering code must run on the clientNetworking
Client vs Server
Check execution context with `Network.IsClient` or `Network.IsServer` booleansMovement and basic networking are handled automaticallyEntity spawning must occur on the server: `Network.Spawn(entity)`SyncVars
SyncVars automatically synchronize primitive values between server and all clients.
**Declaration:**
```csharp
public partial class BattlePlayer : Player
{
public SyncVar<int> Health = new(100);
}
```
**Usage:**
Access value: `Health.Value`Change value (server only): `Health.Set(newValue)`React to changes: `Health.OnSync((oldValue, newValue) => { /* handle change */ })`**Supported Types:**
`int`, `float`, `bool`, `string``Vector2`, `Vector3`, `Vector4``Entity`**Important:**
Always call the constructor when declaring SyncVarsOnly use SyncVars when state must be synchronizedServer is authoritative - only server can call `.Set()`Remote Procedure Calls (RPCs)
**ClientRpc** - Server calls function on client(s)
```csharp
public partial class MyPlayer : Player
{
[ClientRpc]
public void PlaySuccessSound()
{
// Plays on client
}
void SomeServerFunction()
{
// Call from server using CallClient_ prefix
CallClient_PlaySuccessSound();
}
}
```
**RPC Requirements:**
ClientRpc methods must be `public`Call with `CallClient_[FUNCTION_NAME]` prefixClasses containing RPCs must use `partial` keywordYou can pass entities as parameters to RPCsLocal Client Checks
In Player classes, use `IsLocal` checks for client-only behavior:
```csharp
if (IsLocal)
{
// Create camera control, input handlers, etc.
}
```
Example Patterns
Timer Pattern
```csharp
private float lastActionTime;
void Update()
{
if (Time.TimeSinceStartup - lastActionTime > 5.0f)
{
DoAction();
lastActionTime = Time.TimeSinceStartup;
}
}
```
Networked Entity Pattern
```csharp
public partial class GameManager : Component
{
void SpawnPlayer()
{
if (!Network.IsServer) return;
var playerEntity = Entity.Create();
var player = playerEntity.AddComponent<MyPlayer>();
Network.Spawn(playerEntity);
}
}
```
Client-Side Rendering Pattern
```csharp
public class PlayerVisuals : Component
{
void Start()
{
if (!Network.IsClient) return;
var sprite = Entity.AddComponent<Sprite_Renderer>();
sprite.Texture = Assets.GetAsset<Texture>("characters/player");
sprite.Tint = new Vector4(1, 1, 1, 1); // White, full opacity
}
}
```
Constraints and Limitations
No coroutines or Unity-style async patternsNo keyboard input (mobile-first)No Unity API access whatsoeverLimited to System namespace and AO namespace importsNo Component existence checks - use GetComponent directly2D only - no Vector3 except for colors/special casesBest Practices
1. Always check `Network.IsServer` before spawning entities or modifying authoritative state
2. Always check `Network.IsClient` before creating UI or visual elements
3. Use SyncVars sparingly - only for state that truly needs synchronization
4. Use ClientRpcs for one-off events (sounds, visual effects)
5. Remember that movement and basic networking are handled automatically
6. Test on mobile resolution and with touch inputs in mind
7. Use `partial` keyword on classes with RPCs or SyncVars