-
Notifications
You must be signed in to change notification settings - Fork 6
en Actor Entity System
Custom entities outside vanilla NPCs/projectiles: spawn, AI, layered draw, net sync.
The Actor system is suitable for custom entities that need to exist in the game world but don't belong to the NPC/Projectile category, such as:
- Custom traps, mechanisms
- Environmental decorations
- Interactive world objects
| Component | Description |
|---|---|
Actor |
Entity base class, defines position, velocity, hitbox, draw layers, etc. |
ActorLoader |
Global manager (ModSystem), handles spawning, updating, drawing, destroying |
GlobalActor |
Global hook class, can modify all Actor behavior across mods |
ActorDrawLayer |
Draw layer enumeration |
- Maximum simultaneous count: 32,767 (
short.MaxValue) - Global array
ActorLoader.Actors[]stores all active entities
using InnoVault.Actors;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
public class FloatingOrb : Actor
{
private int timer;
public override void OnSpawn(params object[] args) {
Width = 32;
Height = 32;
timer = 300; // Alive for 5 seconds
}
public override void AI() {
timer--;
Velocity = new Vector2(0, MathF.Sin(timer * 0.1f) * 2f);
Rotation += 0.05f;
if (timer <= 0) {
Active = false; // Mark for destruction
}
}
public override bool PreDraw(SpriteBatch spriteBatch, ref Color drawColor) {
// Custom draw logic
return true; // Return true to continue default draw
}
}// Generic spawn (recommended)
int whoAmI = ActorLoader.NewActor<FloatingOrb>(spawnPosition, initialVelocity);
// Spawn by ID
int id = ActorLoader.GetActorID<FloatingOrb>();
int whoAmI = ActorLoader.NewActor(id, spawnPosition);When calling
NewActoron the client, a network request is automatically sent to the server to create the entity. The return value on the client is-1, with the actual slot assigned by the server.
// External destruction
ActorLoader.KillActor(whoAmI);
// Self-destruction (in AI)
Active = false;| Property | Type | Description | SyncVar |
|---|---|---|---|
Active |
bool |
Whether alive | ✅ |
Width |
int |
Hitbox width (pixels) | ✅ |
Height |
int |
Hitbox height (pixels) | ✅ |
Position |
Vector2 |
World coordinates (top-left) | ✅ |
Velocity |
Vector2 |
Velocity (per tick) | ✅ |
Rotation |
float |
Rotation angle (radians) | ✅ |
Scale |
float |
Scale multiplier, default 1.0 | ✅ |
| Property | Type | Description |
|---|---|---|
Size |
Vector2 |
(Width, Height) * Scale |
HitBox |
Rectangle |
Collision rectangle |
Center |
Vector2 |
Center coordinates |
| Property | Type | Description |
|---|---|---|
ID |
int |
Actor type ID (assigned at registration) |
WhoAmI |
int |
Slot index in the world array |
HoverTP |
bool |
Whether mouse is hovering over the entity |
InScreen |
bool |
Whether on the player's screen |
DrawExtendMode |
int |
Off-screen draw extension range, default 160 pixels |
NetUpdate |
bool |
Set to true to trigger network sync |
DrawLayer |
ActorDrawLayer |
Draw layer |
| Hook | Timing | Description |
|---|---|---|
OnSpawn(params object[] args) |
On spawn | Initialize size, state |
AI() |
Every frame | Main logic, Position += Velocity runs automatically after |
PreDraw(SpriteBatch, ref Color) |
Before draw | Return false to skip default draw |
PostDraw(SpriteBatch, Color) |
After draw | Additional draw effects |
Control draw order by setting the DrawLayer property:
| Enum Value | Description |
|---|---|
ActorDrawLayer.BeforeTiles |
Before tile drawing |
ActorDrawLayer.AfterTiles |
After tile drawing (PostDrawTiles) |
ActorDrawLayer.BeforePlayers |
Before player drawing |
ActorDrawLayer.AfterPlayers |
After player drawing |
ActorDrawLayer.Default |
Default layer (at the DrawInfernoRings hook) |
public override void OnSpawn(params object[] args) {
DrawLayer = ActorDrawLayer.BeforePlayers; // Draw behind players
}The SetScaleCentered method scales with center as the pivot, automatically compensating Position to prevent hitbox offset:
SetScaleCentered(2.0f); // Scale to 2x, keeping center unchangedAll [SyncVar] fields on an Actor are automatically synced via SyncVarManager:
public class NetActor : Actor
{
[SyncVar]
public int Phase;
public override void AI() {
if (Phase != newPhase) {
Phase = newPhase;
NetUpdate = true; // Mark for sync
}
}
}Underlying calls:
// Send
actor.SendSyncData(writer); // ≡ SyncVarManager.Send(this, writer)
// Receive
actor.ReceiveSyncData(reader); // ≡ SyncVarManager.Receive(this, reader)GlobalActor allows cross-mod modification of all Actor behavior, similar to tModLoader's GlobalNPC:
using InnoVault.Actors;
public class MyGlobalActor : GlobalActor
{
public override bool PreAI(Actor actor) {
// Return false to skip this Actor's AI
return true;
}
public override void PostAI(Actor actor) {
// Processing after AI runs
}
public override void OnSpawn(Actor actor) {
// Callback when any Actor spawns
}
public override bool PreDraw(SpriteBatch spriteBatch, Actor actor, Color drawColor) {
// Return false to skip draw
return true;
}
public override void PostDraw(SpriteBatch spriteBatch, Actor actor, Color drawColor) {
// Additional draw
}
}| Method | Description |
|---|---|
NewActor<T>(position, velocity) |
Spawn Actor via generic type |
NewActor(typeID, position, velocity) |
Spawn by type ID |
AddActor(type, slot, position, velocity) |
Directly add to specified slot |
KillActor(whoAmI, network) |
Destroy specified Actor |
FindNextFreeSlot() |
Find a free slot |
GetActorID<T>() |
Get the ID for a type |
DrawActors(spriteBatch, layer) |
Draw Actors of specified layer |
using InnoVault.Actors;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
public class DamageOrb : Actor
{
[SyncVar]
public int Damage = 20;
private int lifetime;
public override void OnSpawn(params object[] args) {
Width = 24;
Height = 24;
lifetime = 180;
DrawLayer = ActorDrawLayer.AfterPlayers;
}
public override void AI() {
lifetime--;
// Track nearest player
Player target = Main.player[Player.FindClosest(Position, Width, Height)];
Vector2 dir = (target.Center - Center);
if (dir.Length() > 0) {
dir.Normalize();
}
Velocity = Vector2.Lerp(Velocity, dir * 5f, 0.02f);
Rotation += 0.1f;
// Hit detection
if (HitBox.Intersects(target.Hitbox)) {
// Deal damage...
Active = false;
}
if (lifetime <= 0) {
Active = false;
}
}
public override bool PreDraw(SpriteBatch spriteBatch, ref Color drawColor) {
// Custom draw
return false; // Skip default draw
}
}
// Spawning
public class MyItem : ModItem
{
public override bool? UseItem(Player player) {
ActorLoader.NewActor<DamageOrb>(player.Center, Vector2.Zero);
return true;
}
}| Previous | Home | Next |
|---|---|---|
| SyncVar Network Sync | Home | DataModules System |