Scene Management¶
Scene Management in Evoker Engine provides a way to organize entities, manage multiple scenes, and work with cameras for rendering.
Overview¶
The scene system includes:
- Scene: Container for entities and their components
- Entity Management: Creating and destroying entities within scenes
- Scene Switching: Loading and unloading different scenes
- Camera System: Managing active cameras for rendering
Scene Class¶
public class Scene
{
public string Name { get; set; }
public ECSRegistry Registry { get; }
public Entity CreateEntity(string name = "Entity");
public void DestroyEntity(Entity entity);
public void Update(float deltaTime);
public IEnumerable<Entity> GetRootEntities();
public static Scene? GetActiveScene();
public static void SetActiveScene(Scene scene);
}
Creating Scenes¶
Basic Scene¶
using EvokerEngine.Scene;
using EvokerEngine.Core;
// Create a new scene
var scene = new Scene("Level 1");
// Create entities in the scene
var player = scene.CreateEntity("Player");
var enemy = scene.CreateEntity("Enemy");
// Add components
var transform = scene.Registry.AddComponent<TransformComponent>(player);
transform.Position = new Vector3(0, 0, 0);
Accessing Active Scene¶
// Get active scene from Application
var scene = Application.Instance.ActiveScene;
// Or use static method
var scene = Scene.GetActiveScene();
Entity Management¶
Creating Entities¶
var scene = Application.Instance.ActiveScene;
// Create entity with name
var player = scene.CreateEntity("Player");
// Add transform
var transform = scene.Registry.AddComponent<TransformComponent>(player);
transform.Position = new Vector3(0, 1, 0);
transform.Scale = Vector3.One;
// Add renderer
var renderer = scene.Registry.AddComponent<MeshRendererComponent>(player);
renderer.MeshId = 0;
renderer.MaterialId = 0;
Destroying Entities¶
// Destroy specific entity
scene.DestroyEntity(player);
// Destroy all enemies
var allTransforms = scene.Registry.GetAllComponents<TransformComponent>();
foreach (var transform in allTransforms)
{
// Check if enemy (example logic)
if (transform.Position.Y < -10)
{
scene.DestroyEntity(transform.Entity);
}
}
Finding Entities¶
// Get all root entities
var entities = scene.GetRootEntities();
foreach (var entity in entities)
{
Logger.Info($"Entity ID: {entity.Id}");
}
// Find entity by component
var healthComponents = scene.Registry.GetAllComponents<HealthComponent>();
foreach (var health in healthComponents)
{
if (health.MaxHealth == 100) // Player identification
{
var playerEntity = health.Entity;
break;
}
}
Camera System¶
Creating a Camera¶
// 3D Camera
var cameraEntity = scene.CreateEntity("MainCamera");
var cameraTransform = scene.Registry.AddComponent<TransformComponent>(cameraEntity);
cameraTransform.Position = new Vector3(0, 5, 10);
cameraTransform.Rotation = new Vector3(-30, 0, 0);
var camera3D = scene.Registry.AddComponent<Camera3DComponent>(cameraEntity);
camera3D.IsPrimary = true;
camera3D.FieldOfView = 60f;
camera3D.AspectRatio = 16f / 9f;
camera3D.NearPlane = 0.1f;
camera3D.FarPlane = 1000f;
2D Camera¶
var cameraEntity = scene.CreateEntity("2DCamera");
var camera2D = scene.Registry.AddComponent<Camera2DComponent>(cameraEntity);
camera2D.IsPrimary = true;
camera2D.ViewportSize = new Vector2(1280, 720);
camera2D.Zoom = 1f;
camera2D.Position = Vector2.Zero;
Camera Controls¶
public class CameraController : Layer
{
private Entity _camera;
private float _rotationX = 0f;
private float _rotationY = 0f;
public override void OnUpdate(float deltaTime)
{
var scene = Application.Instance.ActiveScene;
var transform = scene.Registry.GetComponent<TransformComponent>(_camera);
// WASD movement
var moveSpeed = 5f * deltaTime;
if (Input.IsKeyPressed(Key.W))
transform.Position += GetForward(transform) * moveSpeed;
if (Input.IsKeyPressed(Key.S))
transform.Position -= GetForward(transform) * moveSpeed;
if (Input.IsKeyPressed(Key.A))
transform.Position -= GetRight(transform) * moveSpeed;
if (Input.IsKeyPressed(Key.D))
transform.Position += GetRight(transform) * moveSpeed;
// Mouse look
var mouseDelta = Input.GetMouseDelta();
_rotationY += mouseDelta.X * 0.1f;
_rotationX -= mouseDelta.Y * 0.1f;
_rotationX = Math.Clamp(_rotationX, -89f, 89f);
transform.Rotation = new Vector3(_rotationX, _rotationY, 0);
}
private Vector3 GetForward(TransformComponent transform)
{
var yaw = transform.Rotation.Y * MathHelper.Deg2Rad;
return new Vector3(MathF.Sin(yaw), 0, MathF.Cos(yaw));
}
private Vector3 GetRight(TransformComponent transform)
{
return Vector3.Cross(GetForward(transform), Vector3.UnitY);
}
}
Scene Switching¶
Basic Scene Switching¶
public class SceneManager : Layer
{
private Dictionary<string, Scene> _scenes = new();
private Scene? _currentScene;
public void RegisterScene(string name, Scene scene)
{
_scenes[name] = scene;
}
public void LoadScene(string name)
{
if (_scenes.TryGetValue(name, out var scene))
{
// Cleanup current scene
if (_currentScene != null)
{
CleanupScene(_currentScene);
}
// Set new scene
_currentScene = scene;
Scene.SetActiveScene(scene);
// Initialize new scene
InitializeScene(scene);
Logger.Info($"Loaded scene: {name}");
}
}
private void CleanupScene(Scene scene)
{
// Destroy all entities
var entities = scene.GetRootEntities().ToList();
foreach (var entity in entities)
{
scene.DestroyEntity(entity);
}
}
private void InitializeScene(Scene scene)
{
// Setup scene-specific entities
}
}
Scene with Data Persistence¶
public class PersistentSceneManager
{
private Scene _currentScene;
private Dictionary<string, object> _persistentData = new();
public void SaveSceneData(string key, object data)
{
_persistentData[key] = data;
}
public T? GetSceneData<T>(string key)
{
if (_persistentData.TryGetValue(key, out var data))
{
return (T)data;
}
return default;
}
public void LoadSceneWithData(Scene scene)
{
_currentScene = scene;
Scene.SetActiveScene(scene);
// Restore persistent data
var playerPos = GetSceneData<Vector3>("player_position");
if (playerPos != default)
{
// Set player position
}
}
}
Complete Examples¶
Example 1: Multi-Scene Game¶
public class GameSceneManager : Layer
{
private Scene _menuScene;
private Scene _gameScene;
private Scene _currentScene;
public override void OnAttach()
{
// Create menu scene
_menuScene = CreateMenuScene();
// Create game scene
_gameScene = CreateGameScene();
// Start with menu
LoadScene(_menuScene);
}
private Scene CreateMenuScene()
{
var scene = new Scene("Main Menu");
// Create UI elements
var titleEntity = scene.CreateEntity("Title");
// Add UI components...
return scene;
}
private Scene CreateGameScene()
{
var scene = new Scene("Game Level");
// Create player
var player = scene.CreateEntity("Player");
var playerTransform = scene.Registry.AddComponent<TransformComponent>(player);
playerTransform.Position = new Vector3(0, 1, 0);
var health = scene.Registry.AddComponent<HealthComponent>(player);
health.MaxHealth = 100f;
health.CurrentHealth = 100f;
// Create camera
var camera = scene.CreateEntity("Camera");
var cameraTransform = scene.Registry.AddComponent<TransformComponent>(camera);
cameraTransform.Position = new Vector3(0, 5, 10);
var cam3D = scene.Registry.AddComponent<Camera3DComponent>(camera);
cam3D.IsPrimary = true;
// Create enemies
for (int i = 0; i < 5; i++)
{
var enemy = scene.CreateEntity($"Enemy_{i}");
var enemyTransform = scene.Registry.AddComponent<TransformComponent>(enemy);
enemyTransform.Position = new Vector3(i * 3, 0, 5);
}
return scene;
}
private void LoadScene(Scene scene)
{
_currentScene = scene;
Scene.SetActiveScene(scene);
Logger.Info($"Loaded scene: {scene.Name}");
}
public void StartGame()
{
LoadScene(_gameScene);
}
public void ReturnToMenu()
{
LoadScene(_menuScene);
}
public override void OnEvent(Event e)
{
if (e is KeyPressedEvent keyEvent)
{
if (keyEvent.KeyCode == Key.Escape)
{
if (_currentScene == _gameScene)
{
ReturnToMenu();
}
else if (_currentScene == _menuScene)
{
Application.Instance.Close();
}
}
if (keyEvent.KeyCode == Key.Enter && _currentScene == _menuScene)
{
StartGame();
}
}
}
}
Example 2: Level System¶
public class LevelManager : Layer
{
private int _currentLevel = 1;
private Scene _currentScene;
public void LoadLevel(int levelNumber)
{
_currentLevel = levelNumber;
// Create new scene for level
_currentScene = new Scene($"Level {levelNumber}");
Scene.SetActiveScene(_currentScene);
// Load level data
LoadLevelData(levelNumber);
// Create player
CreatePlayer();
// Create level geometry
CreateLevelGeometry(levelNumber);
Logger.Info($"Loaded Level {levelNumber}");
}
private void CreatePlayer()
{
var player = _currentScene.CreateEntity("Player");
var transform = _currentScene.Registry.AddComponent<TransformComponent>(player);
transform.Position = GetSpawnPoint(_currentLevel);
var health = _currentScene.Registry.AddComponent<HealthComponent>(player);
health.MaxHealth = 100f;
health.CurrentHealth = 100f;
}
private void CreateLevelGeometry(int level)
{
// Level-specific geometry
switch (level)
{
case 1:
CreateLevel1();
break;
case 2:
CreateLevel2();
break;
default:
CreateDefaultLevel();
break;
}
}
private Vector3 GetSpawnPoint(int level)
{
return level switch
{
1 => new Vector3(0, 1, 0),
2 => new Vector3(5, 1, 5),
_ => Vector3.Zero
};
}
public void NextLevel()
{
LoadLevel(_currentLevel + 1);
}
private void LoadLevelData(int levelNumber) { }
private void CreateLevel1() { }
private void CreateLevel2() { }
private void CreateDefaultLevel() { }
}
Example 3: Camera Follow System¶
public class CameraFollowSystem : Layer
{
private Entity _camera;
private Entity _target;
private Vector3 _offset = new Vector3(0, 5, 10);
private float _smoothSpeed = 5f;
public override void OnAttach()
{
var scene = Application.Instance.ActiveScene;
// Find or create camera
_camera = scene.CreateEntity("FollowCamera");
var cameraTransform = scene.Registry.AddComponent<TransformComponent>(_camera);
var cam3D = scene.Registry.AddComponent<Camera3DComponent>(_camera);
cam3D.IsPrimary = true;
// Find target (player)
var healthComponents = scene.Registry.GetAllComponents<HealthComponent>();
foreach (var health in healthComponents)
{
if (health.MaxHealth == 100f) // Identify player
{
_target = health.Entity;
break;
}
}
}
public override void OnUpdate(float deltaTime)
{
if (_target.Id == 0 || _camera.Id == 0)
return;
var scene = Application.Instance.ActiveScene;
var targetTransform = scene.Registry.GetComponent<TransformComponent>(_target);
var cameraTransform = scene.Registry.GetComponent<TransformComponent>(_camera);
if (targetTransform != null && cameraTransform != null)
{
// Calculate desired position
var desiredPos = targetTransform.Position + _offset;
// Smooth follow
cameraTransform.Position = Vector3.Lerp(
cameraTransform.Position,
desiredPos,
_smoothSpeed * deltaTime
);
// Look at target
var direction = targetTransform.Position - cameraTransform.Position;
var yaw = MathF.Atan2(direction.X, direction.Z) * MathHelper.Rad2Deg;
var pitch = MathF.Asin(-direction.Y / direction.Length()) * MathHelper.Rad2Deg;
cameraTransform.Rotation = new Vector3(pitch, yaw, 0);
}
}
}
Best Practices¶
✅ Do's¶
- Create scenes for different game states (menu, gameplay, pause)
- Use scene switching for level transitions
- Set camera as primary (IsPrimary = true)
- Clean up entities when switching scenes
- Use meaningful names for scenes and entities
❌ Don'ts¶
- Don't forget to set an active scene
- Don't create multiple primary cameras
- Don't leave destroyed entities in memory
- Don't perform heavy operations in scene Update()
Performance Tips¶
- Reuse scenes - Don't create new scenes every frame
- Pool entities - Reuse instead of creating/destroying
- Limit root entities - Use hierarchies for organization
- Batch scene loading - Load assets asynchronously
See Also¶
- ECS - Entity Component System
- Application - Application and active scene