🎥 EG.Vision.System

Universal Camera & Coordinate Management Solution

v2025.1 - Production Ready
🎯

Universal Raycasting

One method handles both 3D objects and UI elements seamlessly. No more separate Physics.Raycast and EventSystem.RaycastAll calls.

🎥

Smart Camera Management

Automatic discovery, classification, and tracking of cameras. Intelligently handles main cameras, UI cameras, and overlays.

🗺️

Coordinate Conversion

Easy conversion between screen, world, viewport, and UI coordinates. Works with both orthographic and perspective cameras.

📱

Mobile Ready

Built-in Safe Area support for notches, Dynamic Island, and rounded corners. Automatically adapts to device orientation changes.

🔔

Event System

Real-time notifications for camera changes, resolution updates, and screen rotations. React to changes automatically.

⚙️

Zero Configuration

Works out of the box with any game project. Automatic detection of render pipeline, input systems, and platform settings.

📖 Introduction & Overview

EG.Vision.System is a comprehensive development framework that unifies camera management, coordinate conversion, raycasting, and mobile features into a single powerful API. It eliminates the complexity of fragmented camera and input systems, providing a clean, production-ready solution for any game project.

⭐ Why EG.Vision.System?

Replace 15+ APIs

One unified interface replaces multiple engine APIs

90% Less Code

Dramatically reduce development time and complexity

🛡️

Zero Configuration

Works out of the box with any game project

📱

Mobile Optimized

Full support for all mobile platforms and features

🏗️

SOLID Architecture

Production-tested clean architecture patterns

🔄

Universal Compatibility

All render pipelines, input systems, and platforms

🔔

Event System

Real-time notifications for camera changes, resolution updates, and screen rotations

📋

Comprehensive Documentation

Full documentation and working examples for every feature

🔧 Key Architecture Features

  • Automatic Camera Discovery: System continuously monitors scene for camera changes
  • Intelligent Classification: Automatically determines camera types (Main, UI, Overlay, Custom)
  • Efficient Coordinate System: Camera reference caching for maximum performance
  • Unified Raycasting: Single function for both 3D objects and UI elements
  • Mobile Optimization: Built-in Safe Area and orientation change support
  • Event-driven Architecture: Reactive programming through event system

🚀 Quick Start Guide

Get up and running with EG.Vision.System in just a few minutes.

📦 Installation

  1. Import the EG.Vision.System package into your game project
  2. Add the VisionManager component to any GameObject in the scene
  3. The system initializes automatically when the scene starts
✅ Done! You now have access to all camera and coordinate functionality through a single unified interface.

💻 Basic Usage

C#
// 1. Get reference to Vision System using EG.Vision.System.Interfaces; IVisionSystem vision = FindFirstObjectByType<VisionManager>(); // 2. Universal raycasting VisionRaycastResult result = vision.PerformRaycast(Input.mousePosition); if (result.hasHit) { Debug.Log($"Hit: {result.hitObject.name} (Type: {result.hitType})"); } // 3. Quick object detection GameObject clickedObject = vision.GetObjectUnderCursor(); // 4. Coordinate conversion Vector3 worldPos = vision.ScreenToWorld(Input.mousePosition, 10f); Vector2 screenPos = vision.WorldToScreen(transform.position); // 5. Event subscription vision.Events.OnPrimaryCameraChanged += (newCamera) => { Debug.Log($"Camera changed to: {newCamera.name}"); };

⚡ Core Methods - Quick Reference

C# Quick Reference
// Object detection vision.GetObjectUnderCursor() // Mouse clicks vision.GetObjectUnderTouch(0) // Touch input vision.PerformRaycast(screenPos) // Detailed raycasting // Coordinate conversion vision.ScreenToWorld(screenPos, depth) // Place objects in world vision.WorldToScreen(worldPos) // Position UI over objects vision.ConvertScreenToUI(screenPos, canvas) // Drag & drop to UI // Camera access vision.GetPrimaryCamera() // Main game camera vision.GetAllCameras() // All scene cameras // Mobile support vision.GetSafeAreaBounds() // Safe area for UI vision.IsPointInSafeArea(touchPos) // Touch validation

🔍 Initialization Check

C#
void Start() { IVisionSystem vision = FindFirstObjectByType<VisionManager>(); if (vision == null) { Debug.LogError("VisionManager not found in scene!"); return; } if (!vision.IsInitialized) { Debug.LogWarning("Vision system not yet initialized"); // System initializes automatically } }

🏗️ System Architecture

EG.Vision.System follows SOLID principles and clean architecture patterns, ensuring maintainability, extensibility, and performance.

📊 System Layers

👨‍💻 Your Game Code
🔌 IVisionSystem Interface (Single Entry Point)
⚙️ VisionManager (Component Bridge)
🧠 VisionSystem Core (Business Logic)

🔧 Subsystem Components

🎥

Camera System

CameraDiscoveryService
CameraProvider
CameraDetector

🗺️

Coordinate System

CoordinateProvider
Screen ↔ World
UI ↔ Canvas

🎯

Raycast System

RaycastService
3D + UI Unified
Multi-touch

📱

Screen Monitor

ScreenMonitor
Resolution Events
Safe Area

🔩 Core Components

VisionManager

Component providing the framework bridge. Add it to any game object in the scene to activate the system. It handles initialization, updates, and serves as the main entry point.

VisionSystem Core

Internal business logic coordinating all subsystems. Manages camera discovery lifecycle, coordinate conversions, and event distribution.

CameraDiscoveryService

Continuously monitors the scene for camera changes. Automatically discovers new cameras, classifies them by type, and notifies the system of changes. Operates on configurable intervals (default: 2 seconds).

CoordinateProvider

Handles all coordinate transformations between different spaces: screen coordinates, world positions, viewport coordinates, and local UI coordinates. Supports both orthographic and perspective cameras.

RaycastService

Provides unified raycasting that automatically determines whether to use Physics.Raycast for 3D objects or EventSystem.RaycastAll for UI elements. Returns comprehensive hit information.

ScreenMonitor

Monitors screen resolution and orientation changes, generating events when changes are detected. Essential for adaptive mobile applications.

🏛️ SOLID Principles Implementation

  • Single Responsibility: Each class has one focused purpose
  • Open/Closed: System is extensible through interfaces without modifying core code
  • Liskov Substitution: All implementations perfectly follow interface contracts
  • Interface Segregation: Focused, specific interfaces
  • Dependency Inversion: High-level modules depend on abstractions

🔄 System Lifecycle

C#
// Automatic initialization through VisionManager void Awake() // VisionManager.Awake() { if (initializeOnAwake && config != null) { Initialize(); // Create VisionSystem and all subsystems } } void Update() // VisionManager.Update() { visionSystem?.Update(); // Update ScreenMonitor } void OnDestroy() // VisionManager.OnDestroy() { Shutdown(); // Cleanup resources and stop all services }

💻 Complete API Reference

Main interface providing all Vision System functionality. Access via FindFirstObjectByType<VisionManager>().

🎯 Raycasting Methods

VisionRaycastResult PerformRaycast(Vector2 screenPosition, Camera camera = null)
Performs comprehensive raycasting to determine the object under a screen position. Automatically determines whether the object is a 3D world object or UI element.
Parameters:
screenPosition - Screen coordinates in pixels
camera - Optional camera (null = primary camera)
C# Example
VisionRaycastResult result = vision.PerformRaycast(Input.mousePosition); if (result.hasHit) { if (result.hitType == VisionRaycastHitType.World3D) { // Handle 3D object GameObject worldObject = result.hitObject; Debug.Log($"Hit 3D object: {worldObject.name} at {result.hitPoint}"); } else if (result.hitType == VisionRaycastHitType.UI) { // Handle UI element GameObject uiElement = result.hitObject; Debug.Log($"Hit UI element: {uiElement.name}"); } }
GameObject GetObjectUnderCursor(Camera camera = null)
Quick method to get the GameObject under the mouse cursor. Returns null if no object is found.
C# Example
GameObject hoveredObject = vision.GetObjectUnderCursor(); if (hoveredObject != null) { ShowTooltip(hoveredObject); } else { HideTooltip(); }
GameObject GetObjectUnderTouch(int touchIndex)
Get the GameObject under a specific touch. Designed for mobile touch input.
Parameters:
touchIndex - Index of the touch (0 for first touch)

🎥 Camera Management Methods

Camera GetPrimaryCamera()
Get the current primary camera for world rendering. System automatically detects MainCamera tag or camera with highest depth.
C# Example
Camera mainCam = vision.GetPrimaryCamera(); if (mainCam != null) { ApplyPostProcessing(mainCam); SetCinemachineTarget(mainCam); }
List<Camera> GetAllCameras()
Get all cameras currently tracked by the system.
List<Camera> GetCamerasByType(VisionCameraType type)
Get cameras filtered by type (Main, UI, Overlay, Custom).
Camera GetCameraByName(string name)
Find a camera by its GameObject name.

🗺️ Coordinate Conversion Methods

Vector3 ScreenToWorld(Vector2 screenPos, float depth = 0f, Camera camera = null)
Convert screen coordinates to world position. Automatically handles orthographic and perspective cameras.
Parameters:
screenPos - Screen coordinates in pixels
depth - World depth for perspective cameras, Z coordinate for orthographic
camera - Optional camera (null = primary camera)
C# Example
// Place object at mouse position in world Vector3 worldPos = vision.ScreenToWorld(Input.mousePosition, 10f); instantiatedObject.transform.position = worldPos; // For 2D games (orthographic) Vector3 worldPos2D = vision.ScreenToWorld(Input.mousePosition, 0f); player2D.transform.position = worldPos2D;
Vector2 WorldToScreen(Vector3 worldPos, Camera camera = null)
Convert world position to screen coordinates.
Vector2 ConvertScreenToUI(Vector2 screenPos, Canvas canvas)
Convert screen coordinates to UI local coordinates for a specific canvas.
bool IsPositionVisible(Vector3 worldPos, Camera camera = null)
Check if a world position is visible in the camera viewport.

📱 Mobile Safe Area Methods

Rect GetSafeAreaBounds()
Get safe area bounds for the current device. Safe area excludes notches, rounded corners, and other screen obstacles.
C# Example
Rect safeArea = vision.GetSafeAreaBounds(); // Adapt UI layout for safe area uiPanel.anchorMin = new Vector2( safeArea.x / Screen.width, safeArea.y / Screen.height ); uiPanel.anchorMax = new Vector2( (safeArea.x + safeArea.width) / Screen.width, (safeArea.y + safeArea.height) / Screen.height ); uiPanel.offsetMin = Vector2.zero; uiPanel.offsetMax = Vector2.zero;
bool IsPointInSafeArea(Vector2 screenPos)
Check if a screen position is within the safe area.
Vector2 ClampToSafeArea(Vector2 screenPos)
Clamp a screen position to the safe area bounds.

📊 Data Types

VisionRaycastResult

Property Type Description
hasHit bool Whether the raycast hit any object
hitType VisionRaycastHitType Type of hit object (None, World3D, UI)
hitObject GameObject GameObject that was hit by the raycast
hitPoint Vector3 Exact hit point in world coordinates
hitDistance float Distance from camera to hit point
camera Camera Camera used for raycasting

VisionCameraInfo

Property Type Description
cameraType VisionCameraType Camera classification (Main, UI, Overlay, Custom)
isOrthographic bool Whether camera uses orthographic projection
fieldOfView float Field of view for perspective cameras
orthographicSize float Size for orthographic cameras

💡 Practical Examples

Real-world implementation examples to get you started quickly.

🖱️ Example 1: Object Selection System

C#
public class ObjectSelector : MonoBehaviour { private IVisionSystem vision; private GameObject selectedObject; private GameObject hoveredObject; void Start() { vision = FindFirstObjectByType<VisionManager>(); } void Update() { // Highlight object under cursor GameObject newHovered = vision.GetObjectUnderCursor(); if (newHovered != hoveredObject) { if (hoveredObject != null) RemoveHighlight(hoveredObject); if (newHovered != null) AddHighlight(newHovered); hoveredObject = newHovered; } // Select object on click if (Input.GetMouseButtonDown(0)) { VisionRaycastResult result = vision.PerformRaycast(Input.mousePosition); if (result.hasHit && result.hitType == VisionRaycastHitType.World3D) { SelectObject(result.hitObject); } } } void SelectObject(GameObject obj) { if (selectedObject != null) RemoveSelection(selectedObject); selectedObject = obj; AddSelection(obj); // Show selection UI at object position Vector2 screenPos = vision.WorldToScreen(obj.transform.position); ShowSelectionUI(screenPos); } }

📱 Example 2: Mobile UI Adaptation

C#
public class MobileUIAdapter : MonoBehaviour { private IVisionSystem vision; [SerializeField] private RectTransform mainUI; [SerializeField] private RectTransform gameplayHUD; void Start() { vision = FindFirstObjectByType<VisionManager>(); // Subscribe to orientation changes vision.Events.OnOrientationChanged += HandleOrientationChange; vision.Events.OnResolutionChanged += HandleResolutionChange; // Initial setup AdaptToSafeArea(); } void HandleOrientationChange(ScreenOrientation oldOrient, ScreenOrientation newOrient) { // Wait one frame for screen size updates StartCoroutine(AdaptToSafeAreaNextFrame()); // Configure UI layout based on orientation if (IsLandscape(newOrient)) { SetLandscapeLayout(); } else { SetPortraitLayout(); } } void AdaptToSafeArea() { Rect safeArea = vision.GetSafeAreaBounds(); // Convert safe area to anchor coordinates Vector2 anchorMin = new Vector2( safeArea.x / Screen.width, safeArea.y / Screen.height ); Vector2 anchorMax = new Vector2( (safeArea.x + safeArea.width) / Screen.width, (safeArea.y + safeArea.height) / Screen.height ); // Apply to main UI mainUI.anchorMin = anchorMin; mainUI.anchorMax = anchorMax; mainUI.offsetMin = Vector2.zero; mainUI.offsetMax = Vector2.zero; } void OnDestroy() { if (vision != null) { vision.Events.OnOrientationChanged -= HandleOrientationChange; vision.Events.OnResolutionChanged -= HandleResolutionChange; } } }

✋ Example 3: Drag & Drop System

C#
public class DragDropManager : MonoBehaviour { private IVisionSystem vision; private GameObject draggedObject; private Vector3 dragOffset; private Vector3 originalPosition; void Start() { vision = FindFirstObjectByType<VisionManager>(); } void Update() { if (Input.GetMouseButtonDown(0)) { StartDrag(); } else if (Input.GetMouseButtonUp(0)) { EndDrag(); } else if (draggedObject != null) { UpdateDrag(); } } void StartDrag() { GameObject obj = vision.GetObjectUnderCursor(); if (obj != null && obj.CompareTag("Draggable")) { draggedObject = obj; originalPosition = obj.transform.position; Vector3 mouseWorld = vision.ScreenToWorld(Input.mousePosition, 10f); dragOffset = obj.transform.position - mouseWorld; // Visual feedback for drag start obj.GetComponent<Renderer>().material.color = Color.yellow; } } void UpdateDrag() { Vector3 mouseWorld = vision.ScreenToWorld(Input.mousePosition, 10f); draggedObject.transform.position = mouseWorld + dragOffset; } void EndDrag() { if (draggedObject != null) { // Check for valid drop zone VisionRaycastResult result = vision.PerformRaycast(Input.mousePosition); if (result.hasHit && result.hitObject.CompareTag("DropZone")) { // Valid drop DropObject(draggedObject, result.hitObject); } else { // Invalid drop - return to original position ReturnToOriginalPosition(draggedObject); } // Reset visual feedback draggedObject.GetComponent<Renderer>().material.color = Color.white; draggedObject = null; } } void ReturnToOriginalPosition(GameObject obj) { obj.transform.position = originalPosition; } }

👁️ Example 4: Off-Screen UI Indicator System

C#
public class OffScreenIndicator : MonoBehaviour { private IVisionSystem vision; [SerializeField] private Transform target; [SerializeField] private RectTransform indicator; [SerializeField] private Canvas canvas; void Start() { vision = FindFirstObjectByType<VisionManager>(); } void Update() { if (target == null) return; // Check if object is visible on screen bool isVisible = vision.IsPositionVisible(target.position); if (isVisible) { // Object visible - hide indicator indicator.gameObject.SetActive(false); } else { // Object not visible - show direction indicator indicator.gameObject.SetActive(true); UpdateIndicatorPosition(); } } void UpdateIndicatorPosition() { // Get object position on screen (even if outside bounds) Vector2 screenPos = vision.WorldToScreen(target.position); // Clamp position to safe area Vector2 clampedPos = vision.ClampToSafeArea(screenPos); // Convert to UI coordinates Vector2 uiPos = vision.ConvertScreenToUI(clampedPos, canvas); // Set indicator position indicator.localPosition = uiPos; // Rotate indicator toward object Vector2 direction = (screenPos - clampedPos).normalized; float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; indicator.rotation = Quaternion.AngleAxis(angle, Vector3.forward); } }

⚙️ Integration Guide

Step-by-step guide to integrate EG.Vision.System into your existing project.

📋 System Requirements

Engine Version: 2022.3 LTS or newer (compatible with development environment)
Render Pipelines: Built-in RP, URP, HDRP (all supported)
Input Systems: Legacy Input Manager, Input System Package (both supported)
Platforms: Windows, Mac, Linux, iOS, Android, WebGL

📝 Step-by-Step Integration

1. Basic Setup

  1. Import the EG.Vision.System package into your game project
  2. Add the VisionManager component to any GameObject in the scene
  3. The system initializes automatically when the scene starts

2. System Access

C# - Recommended Patterns
// Method 1: Direct reference (recommended for performance) public class GameManager : MonoBehaviour { [SerializeField] private VisionManager visionManager; private IVisionSystem vision; void Start() { vision = visionManager; // VisionManager implements IVisionSystem } } // Method 2: Runtime search (simpler, but slightly slower) public class PlayerController : MonoBehaviour { private IVisionSystem vision; void Start() { vision = FindFirstObjectByType<VisionManager>(); } }
✅ Zero Configuration: EG.Vision.System works out of the box with any game project. No additional setup required!

⚡ Performance Considerations

🚀 Performance Optimized: EG.Vision.System uses caching, object pooling, and efficient algorithms to minimize performance impact.
  • Camera Discovery: Operates on configurable intervals to avoid frame rate impact
  • Coordinate Conversion: Uses cached camera references and optimized math
  • Event System: Minimal allocation with efficient event dispatching
  • Raycasting: Intelligent choice between 3D and UI raycasting

🔧 System Configuration

C#
// VisionConfig - system settings [CreateAssetMenu(fileName = "VisionConfig", menuName = "EG/Vision System/System Config")] public class VisionConfig : ScriptableObject { [Header("Discovery Settings")] [SerializeField] private bool autoDetectCameras = true; [SerializeField] private float discoveryInterval = 2f; [Header("Debug & Logging")] [SerializeField] private bool enableDebugLogging = false; }

🎥 Integration with Existing Systems

C# - Cinemachine Integration
// Cinemachine integration public class CinemachineIntegration : MonoBehaviour { private IVisionSystem vision; void Start() { vision = FindFirstObjectByType<VisionManager>(); vision.Events.OnPrimaryCameraChanged += UpdateVirtualCameras; } void UpdateVirtualCameras(Camera newCamera) { // Update all Cinemachine virtual cameras var virtualCameras = FindObjectsByType<CinemachineVirtualCamera>(FindObjectsSortMode.None); foreach (var vcam in virtualCameras) { // Configure Cinemachine to work with new camera vcam.m_OutputCamera = newCamera; } } }

🔮 Advanced Features

Unlock the full potential of EG.Vision.System with advanced features and customization options.

🎥 Custom Camera Discovery

The system automatically discovers and classifies cameras, but you can override this behavior:

C#
// Override primary camera (useful for cutscenes) vision.SetPrimaryCamera(cutsceneCamera); // Get cameras by type var mainCameras = vision.GetCamerasByType(VisionCameraType.Main); var uiCameras = vision.GetCamerasByType(VisionCameraType.UI); // Find camera by name Camera securityCam = vision.GetCameraByName("SecurityCamera"); // Force camera refresh after scene changes vision.ForceRefreshCameras(); // Get detailed camera information VisionCameraInfo info = vision.GetCameraInfo(); if (info.isOrthographic) { Debug.Log($"Orthographic size: {info.orthographicSize}"); } else { Debug.Log($"Field of view: {info.fieldOfView}"); }

⚡ Event-driven Architecture

C#
public class EventDrivenGameManager : MonoBehaviour { private IVisionSystem vision; void Start() { vision = FindFirstObjectByType<VisionManager>(); // Subscribe to all relevant events vision.Events.OnPrimaryCameraChanged += HandleCameraChange; vision.Events.OnCameraDiscovered += HandleNewCamera; vision.Events.OnResolutionChanged += HandleResolutionChange; vision.Events.OnOrientationChanged += HandleOrientationChange; } void HandleCameraChange(Camera newCamera) { Debug.Log($"Primary camera changed to: {newCamera?.name ?? "null"}"); // Update camera-dependent systems UpdatePostProcessing(newCamera); UpdateAudioListener(newCamera); UpdateCameraEffects(newCamera); } void HandleNewCamera(Camera newCamera) { Debug.Log($"New camera discovered: {newCamera.name}"); // Auto-configure new cameras if (newCamera.CompareTag("SecurityCamera")) { SetupSecurityCamera(newCamera); } } void OnDestroy() { // Unsubscribe from events to prevent memory leaks if (vision != null) { vision.Events.OnPrimaryCameraChanged -= HandleCameraChange; vision.Events.OnCameraDiscovered -= HandleNewCamera; vision.Events.OnResolutionChanged -= HandleResolutionChange; vision.Events.OnOrientationChanged -= HandleOrientationChange; } } }

📈 Performance Patterns

💡 Cache Vision System Reference: Store IVisionSystem reference instead of repeatedly calling FindFirstObjectByType.
C# - Good vs Bad Patterns
// Good: Cache reference private IVisionSystem vision; void Start() { vision = FindFirstObjectByType<VisionManager>(); } void Update() { // Fast: Use cached reference GameObject obj = vision.GetObjectUnderCursor(); } // Bad: Don't do this void Update() { // Slow: Search every frame IVisionSystem vision = FindFirstObjectByType<VisionManager>(); GameObject obj = vision.GetObjectUnderCursor(); }

🐛 Logging and Debugging

C#
// Enable detailed logging in VisionConfig public class VisionDebugger : MonoBehaviour { private IVisionSystem vision; void Start() { vision = FindFirstObjectByType<VisionManager>(); // Log system status LogSystemStatus(); // Monitor raycast performance StartCoroutine(MonitorRaycastPerformance()); } [ContextMenu("Log System Status")] void LogSystemStatus() { if (vision == null) return; Debug.Log($"=== Vision System Status ==="); Debug.Log($"Initialized: {vision.IsInitialized}"); var primaryCamera = vision.GetPrimaryCamera(); Debug.Log($"Primary Camera: {(primaryCamera != null ? primaryCamera.name : "None")}"); var allCameras = vision.GetAllCameras(); Debug.Log($"Total Cameras: {allCameras.Count}"); foreach (var cam in allCameras) { if (cam != null) { var info = vision.GetCameraInfo(cam); Debug.Log($" - {cam.name}: Type={info.cameraType}, Depth={cam.depth}"); } } } }

🎮 Custom Input System Integration

C# - New Input System
// New Input System integration using UnityEngine.InputSystem; public class NewInputIntegration : MonoBehaviour { private IVisionSystem vision; private InputAction clickAction; private InputAction positionAction; void Start() { vision = FindFirstObjectByType<VisionManager>(); // Setup Input Actions clickAction = new InputAction("Click", InputActionType.Button, "<Mouse>/leftButton"); positionAction = new InputAction("Position", InputActionType.Value, "<Mouse>/position"); clickAction.performed += OnClick; clickAction.Enable(); positionAction.Enable(); } void OnClick(InputAction.CallbackContext context) { Vector2 mousePos = positionAction.ReadValue<Vector2>(); VisionRaycastResult result = vision.PerformRaycast(mousePos); if (result.hasHit) { Debug.Log($"Clicked: {result.hitObject.name}"); } } void OnDestroy() { clickAction?.Disable(); positionAction?.Disable(); } }

🔧 Troubleshooting

Common issues and their solutions to help you get the most out of EG.Vision.System.

⚠️ Common Issues and Solutions

❌ Error "Vision System not initialized"

Problem: Attempting to use vision system before initialization.
C# Solution
// Solution: Check initialization before use void Start() { IVisionSystem vision = FindFirstObjectByType<VisionManager>(); if (vision == null) { Debug.LogError("VisionManager not found in scene!"); return; } if (!vision.IsInitialized) { Debug.LogWarning("Vision system not yet initialized"); // Wait for initialization or force initialize StartCoroutine(WaitForInitialization()); } } IEnumerator WaitForInitialization() { while (!vision.IsInitialized) { yield return null; } // Now safe to use system InitializeMySystem(); }

❌ Raycast not hitting UI elements

Problem: UI raycasting not working properly.

Solutions:

  • Ensure EventSystem is present in the scene
  • Check that UI elements have Raycast Target enabled
  • Ensure GraphicRaycaster is on Canvas
  • Check that UI elements aren't blocked by other UI
  • Verify Canvas layer sorting (Order in Layer)

❌ Null camera references

Problem: GetPrimaryCamera() returns null.

Solutions:

  • Ensure at least one active camera exists in the scene
  • Add "MainCamera" tag to your primary camera
  • Force refresh cameras after scene changes
  • Check that camera is enabled and GameObject is active

✅ Best Practices

🎯 Recommended Setup: Follow these best practices for optimal performance and reliability.

✅ System Initialization

  • Place VisionManager on a persistent GameObject (like GameManager)
  • Initialize early in the game lifecycle
  • Cache IVisionSystem reference for performance
  • Subscribe to events for reactive programming

❓ Frequently Asked Questions (FAQ)

Q: Can I use multiple VisionManagers in one scene?

A: No, use only one VisionManager per scene. The system automatically manages all cameras.

Q: Does the system support multi-scene setups?

A: Yes, place VisionManager in the main scene and it will track cameras across all loaded scenes.

Q: Does it work with Cinemachine?

A: Yes, the system automatically detects cameras created by Cinemachine. Use events for synchronization.

Q: Does it impact performance?

A: Minimally. The system is optimized with caching and efficient algorithms. Camera discovery happens every 2 seconds by default.

📈 Performance Guide

Optimize your implementation for maximum performance and efficiency.

🚀 System Optimizations

⚡ Built-in Optimizations: EG.Vision.System is designed with performance in mind and includes many built-in optimizations.

Automatic Optimizations

  • Camera Caching: Camera references are cached and updated only when necessary
  • Interval Discovery: New camera search happens every 2 seconds, not every frame
  • Efficient Events: Event system uses minimal memory allocation
  • Optimized Raycasting: Automatic choice between 3D and UI raycasting
  • Lazy Initialization: Subsystems are created only when needed

✅ Usage Recommendations

✅ Correct Patterns

C# - Optimized Patterns
// ✅ Cache system reference public class OptimizedController : MonoBehaviour { private IVisionSystem vision; // Cached reference private Camera cachedCamera; // Cached camera void Start() { vision = FindFirstObjectByType<VisionManager>(); cachedCamera = vision.GetPrimaryCamera(); // Subscribe to camera changes vision.Events.OnPrimaryCameraChanged += UpdateCachedCamera; } void UpdateCachedCamera(Camera newCamera) { cachedCamera = newCamera; } void Update() { // ✅ Use cached references if (Input.GetMouseButtonDown(0)) { GameObject obj = vision.GetObjectUnderCursor(cachedCamera); if (obj != null) ProcessClick(obj); } } }

❌ Inefficient Patterns

C# - Avoid These Patterns
// ❌ Don't do this - inefficient void Update() { // ❌ Search for system every frame IVisionSystem vision = FindFirstObjectByType<VisionManager>(); // ❌ Get camera every frame Camera cam = vision.GetPrimaryCamera(); // ❌ Unnecessary raycasts VisionRaycastResult result = vision.PerformRaycast(Input.mousePosition); }

⚙️ Performance Configuration Settings

C#
// Optimal VisionConfig settings for performance void OptimizeForPerformance() { // Increase discovery interval for static scenes // discoveryInterval = 5f; // Instead of 2f default // Disable auto-detection if cameras don't change // autoDetectCameras = false; // Disable debug logs in production // enableDebugLogging = false; }