---
title: "persistent-entity"
---

﻿# Persistent Entity

> **Version**: v2.3.0+

Persistent Entity is a special type of entity that automatically migrates to the new scene during scene transitions. It is suitable for game objects that need to maintain state across scenes, such as players, game managers, audio managers, etc.

## Basic Concepts

In the ECS framework, entities have two lifecycle policies:

| Policy | Description | Default |
|--------|-------------|---------|
| `SceneLocal` | Scene-local entity, destroyed when scene changes | ✓ |
| `Persistent` | Persistent entity, automatically migrates during scene transitions | |

## Quick Start

### Creating a Persistent Entity

```typescript
import { Scene } from '@esengine/ecs-framework';

class GameScene extends Scene {
  protected initialize(): void {
    // Create a persistent player entity
    const player = this.createEntity('Player').setPersistent();
    player.addComponent(new Position(100, 200));
    player.addComponent(new PlayerData('Hero', 500));

    // Create a normal enemy entity (destroyed when scene changes)
    const enemy = this.createEntity('Enemy');
    enemy.addComponent(new Position(300, 200));
    enemy.addComponent(new EnemyAI());
  }
}
```

### Behavior During Scene Transitions

```typescript
import { Core, Scene } from '@esengine/ecs-framework';

// Initial scene
class Level1Scene extends Scene {
  protected initialize(): void {
    // Player - persistent, will migrate to the next scene
    const player = this.createEntity('Player').setPersistent();
    player.addComponent(new Position(0, 0));
    player.addComponent(new Health(100));

    // Enemy - scene-local, destroyed when scene changes
    const enemy = this.createEntity('Enemy');
    enemy.addComponent(new Position(100, 100));
  }
}

// Target scene
class Level2Scene extends Scene {
  protected initialize(): void {
    // New enemy
    const enemy = this.createEntity('Boss');
    enemy.addComponent(new Position(200, 200));
  }

  public onStart(): void {
    // Player has automatically migrated to this scene
    const player = this.findEntity('Player');
    console.log(player !== null); // true

    // Position and health data are fully preserved
    const position = player?.getComponent(Position);
    const health = player?.getComponent(Health);
    console.log(position?.x, position?.y); // 0, 0
    console.log(health?.value); // 100
  }
}

// Switch scenes
Core.create({ debug: true });
Core.setScene(new Level1Scene());

// Later switch to Level2
Core.loadScene(new Level2Scene());
// Player entity migrates automatically, Enemy entity is destroyed
```

## API Reference

### Entity Methods

#### setPersistent()

Marks the entity as persistent, preventing destruction during scene transitions.

```typescript
public setPersistent(): this
```

**Returns**: Returns the entity itself for method chaining

**Example**:
```typescript
const player = scene.createEntity('Player')
  .setPersistent();

player.addComponent(new Position(100, 200));
```

#### setSceneLocal()

Restores the entity to scene-local policy (default).

```typescript
public setSceneLocal(): this
```

**Returns**: Returns the entity itself for method chaining

**Example**:
```typescript
// Dynamically cancel persistence
player.setSceneLocal();
```

#### isPersistent

Checks if the entity is persistent.

```typescript
public get isPersistent(): boolean
```

**Example**:
```typescript
if (entity.isPersistent) {
  console.log('This is a persistent entity');
}
```

#### lifecyclePolicy

Gets the entity's lifecycle policy.

```typescript
public get lifecyclePolicy(): EEntityLifecyclePolicy
```

**Example**:
```typescript
import { EEntityLifecyclePolicy } from '@esengine/ecs-framework';

if (entity.lifecyclePolicy === EEntityLifecyclePolicy.Persistent) {
  console.log('Persistent entity');
}
```

### Scene Methods

#### findPersistentEntities()

Finds all persistent entities in the scene.

```typescript
public findPersistentEntities(): Entity[]
```

**Returns**: Array of persistent entities

**Example**:
```typescript
const persistentEntities = scene.findPersistentEntities();
console.log(`Scene has ${persistentEntities.length} persistent entities`);
```

#### extractPersistentEntities()

Extracts and removes all persistent entities from the scene (typically called internally by the framework).

```typescript
public extractPersistentEntities(): Entity[]
```

**Returns**: Array of extracted persistent entities

#### receiveMigratedEntities()

Receives migrated entities (typically called internally by the framework).

```typescript
public receiveMigratedEntities(entities: Entity[]): void
```

**Parameters**:
- `entities` - Array of entities to receive

## Use Cases

### 1. Player Entity Across Levels

```typescript
class PlayerSetupScene extends Scene {
  protected initialize(): void {
    // Player maintains state across all levels
    const player = this.createEntity('Player').setPersistent();
    player.addComponent(new Transform(0, 0));
    player.addComponent(new Health(100));
    player.addComponent(new Inventory());
    player.addComponent(new PlayerStats());
  }
}

class Level1 extends Scene { /* ... */ }
class Level2 extends Scene { /* ... */ }
class Level3 extends Scene { /* ... */ }

// Player entity automatically migrates between all levels
Core.setScene(new PlayerSetupScene());
// ... game progresses
Core.loadScene(new Level1());
// ... level complete
Core.loadScene(new Level2());
// Player data (health, inventory, stats) fully preserved
```

### 2. Global Managers

```typescript
class BootstrapScene extends Scene {
  protected initialize(): void {
    // Audio manager - persists across scenes
    const audioManager = this.createEntity('AudioManager').setPersistent();
    audioManager.addComponent(new AudioController());

    // Achievement manager - persists across scenes
    const achievementManager = this.createEntity('AchievementManager').setPersistent();
    achievementManager.addComponent(new AchievementTracker());

    // Game settings - persists across scenes
    const settings = this.createEntity('GameSettings').setPersistent();
    settings.addComponent(new SettingsData());
  }
}
```

### 3. Dynamically Toggling Persistence

```typescript
class GameScene extends Scene {
  protected initialize(): void {
    // Initially created as a normal entity
    const companion = this.createEntity('Companion');
    companion.addComponent(new Transform(0, 0));
    companion.addComponent(new CompanionAI());

    // Listen for recruitment event
    this.eventSystem.on('companion:recruited', () => {
      // After recruitment, become persistent
      companion.setPersistent();
      console.log('Companion joined the party, will follow player across scenes');
    });

    // Listen for dismissal event
    this.eventSystem.on('companion:dismissed', () => {
      // After dismissal, restore to scene-local
      companion.setSceneLocal();
      console.log('Companion left the party, will no longer persist across scenes');
    });
  }
}
```

## Best Practices

### 1. Clearly Identify Persistent Entities

```typescript
// Recommended: Mark immediately when creating
const player = this.createEntity('Player').setPersistent();

// Not recommended: Marking after creation (easy to forget)
const player = this.createEntity('Player');
// ... lots of code ...
player.setPersistent(); // Easy to forget
```

### 2. Use Persistence Appropriately

```typescript
// ✓ Entities suitable for persistence
const player = this.createEntity('Player').setPersistent();      // Player
const gameManager = this.createEntity('GameManager').setPersistent(); // Global manager
const audioManager = this.createEntity('AudioManager').setPersistent(); // Audio system

// ✗ Entities that should NOT be persistent
const bullet = this.createEntity('Bullet'); // Temporary objects
const enemy = this.createEntity('Enemy');   // Level-specific enemies
const particle = this.createEntity('Particle'); // Effect particles
```

### 3. Check Migrated Entities

```typescript
class NewScene extends Scene {
  public onStart(): void {
    // Check if expected persistent entities exist
    const player = this.findEntity('Player');
    if (!player) {
      console.error('Player entity did not migrate correctly!');
      // Handle error case
    }
  }
}
```

### 4. Avoid Circular References

```typescript
// ✗ Avoid: Persistent entity referencing scene-local entity
class BadScene extends Scene {
  protected initialize(): void {
    const player = this.createEntity('Player').setPersistent();
    const enemy = this.createEntity('Enemy');

    // Dangerous: player is persistent but enemy is not
    // After scene change, enemy is destroyed, reference becomes invalid
    player.addComponent(new TargetComponent(enemy));
  }
}

// ✓ Recommended: Use ID references or event system
class GoodScene extends Scene {
  protected initialize(): void {
    const player = this.createEntity('Player').setPersistent();
    const enemy = this.createEntity('Enemy');

    // Store ID instead of direct reference
    player.addComponent(new TargetComponent(enemy.id));

    // Or use event system for communication
  }
}
```

## Important Notes

1. **Destroyed entities will not migrate**: If an entity is destroyed before scene transition, it will not migrate even if marked as persistent.

2. **Component data is fully preserved**: All components and their state are preserved during migration.

3. **Scene reference is updated**: After migration, the entity's `scene` property will point to the new scene.

4. **Query system is updated**: Migrated entities are automatically registered in the new scene's query system.

5. **Delayed transitions also work**: Persistent entities migrate when using `Core.loadScene()` for delayed transitions as well.

## Related Documentation

- [Scene](./scene) - Learn the basics of scenes
- [SceneManager](./scene-manager) - Learn about scene transitions
- [WorldManager](./world-manager) - Learn about multi-world management
