Core Architecture
✅ COMPLETE - VContainer dependency injection, EventManager, GameManager, InputManager all implemented and operational.
RentEarth is a survival action game built in Unity where players face off against relentless waves of insects and creatures from the natural world. The game features intense combat mechanics, strategic resource management, and progressive difficulty scaling. Players must adapt to different bug types, each with unique behaviors and attack patterns, while managing limited resources and upgrading their defensive capabilities.
The game is built using modern Unity development practices, leveraging powerful architectural patterns through dependency injection (VContainer) and asynchronous programming (UniTask). The core architecture emphasizes modularity, testability, and clean separation of concerns, making the codebase maintainable and scalable as new features are added.
VContainer is the fastest and most lightweight dependency injection (DI) framework for Unity, providing a robust foundation for managing object lifecycles and dependencies throughout RentEarth.
Unlike traditional Unity development where components find each other using FindObjectOfType or singleton patterns, VContainer enables proper inversion of control (IoC) where dependencies are explicitly declared and automatically injected.
In RentEarth, VContainer serves as the backbone of our architecture through the GameLifetimeScope class, which orchestrates all core managers (GameManager, EventManager, InputManager, etc.).
This approach eliminates tight coupling between systems, makes testing easier through dependency mocking, and provides clear visibility into system dependencies.
VContainer’s constructor and method injection automatically resolves dependencies when components are created, ensuring that all required references are available before a component begins its work.
The framework’s performance characteristics are crucial for games - VContainer generates optimal IL code at build time, resulting in zero reflection overhead at runtime.
This makes it ideal for performance-critical game environments where every frame counts.
Our implementation uses VContainer’s LifetimeScope to manage singleton managers that persist across scenes, while still maintaining the flexibility to add scoped dependencies for level-specific systems.
UniTask is a zero-allocation async/await framework specifically designed for Unity, replacing Unity’s older Coroutine system with modern C# async/await patterns. Unlike standard .NET Tasks which allocate garbage on every operation, UniTask is a value-type based implementation that provides true zero-allocation asynchronous programming, making it perfect for game development where garbage collection pauses can cause frame drops.
In RentEarth, UniTask powers all asynchronous operations including scene loading, resource streaming, and complex animation sequences.
The GameManager uses UniTask extensively for scene management, providing clean async/await syntax instead of callback-based coroutines.
This makes our code more readable and maintainable - for example, loading a scene with progress tracking becomes a straightforward async method rather than a complex IEnumerator with nested callbacks.
UniTask also integrates seamlessly with Unity’s lifecycle, providing PlayerLoop-based task scheduling that respects Unity’s frame timing.
The performance benefits of UniTask are substantial - it avoids the heap allocations associated with coroutines and standard Tasks, operates faster than Unity’s coroutine system, and provides better debugging support through proper stack traces. UniTask also includes powerful operators for working with Unity’s async operations, time-based delays, and cancellation tokens, giving us fine-grained control over asynchronous workflows. This is particularly important for RentEarth’ gameplay systems where we need responsive, non-blocking operations for enemy spawning, animation blending, and networked events.
R3 is a modern reimplementation of Reactive Extensions (Rx) specifically designed for Unity and other .NET platforms, created by the author of UniRx with over 10 years of reactive programming experience. R3 represents a complete rethinking of reactive patterns for game development, addressing fundamental limitations in traditional Rx implementations while embracing modern C# language features and Unity’s unique requirements.
Unlike traditional Reactive Extensions which were designed for general-purpose asynchronous programming, R3 is built from the ground up for game engines and real-time interactive applications. It provides reactive event streams that respond to frame-based timing, player input, game state changes, and other in-memory events common in game development.
Frame-Based Operations:
Traditional Rx libraries operate on wall-clock time (seconds, milliseconds) which poorly matches game engine execution models that run on frames. R3 introduces FrameProvider alongside the standard TimeProvider, enabling operators like IntervalFrame, DelayFrame, ThrottleFrame, and DebounceFrame that align naturally with Unity’s Update loop. This allows developers to write reactive code that executes “every 5 frames” or “debounce for 10 frames” rather than awkwardly converting frames to time estimates.
Simplified Error Handling:
Traditional Rx terminates entire subscription chains when exceptions occur, requiring complex error recovery patterns. R3 fundamentally redesigns error handling - exceptions trigger OnErrorResume methods that give developers explicit control over recovery behavior without terminating the stream. This “errors are values” approach means reactive pipelines are more resilient and easier to reason about.
Performance-First Design:
R3 eliminates the IScheduler abstraction that caused significant overhead in traditional Rx, replacing it with the more efficient TimeProvider pattern from modern .NET. The framework uses abstract classes instead of interfaces, enabling better JIT optimization and reducing virtual call overhead. Subject implementations avoid ImmutableArray allocations that plagued UniRx performance. Benchmarks show R3 operations executing 2-10x faster than traditional Rx equivalents with dramatically reduced GC pressure.
Explicit Lifecycle Management:
R3 consolidates OnError and OnCompleted into a single OnCompleted(Result result) method, making subscription lifecycle explicit and preventing common memory leak patterns. Subscriptions are tracked explicitly, making it easier to diagnose and prevent resource leaks that can accumulate during long game sessions.
Observable Streams: Observables represent sequences of events over time. In RentEarth, observables can represent:
// Time-based observable (uses TimeProvider)Observable.Interval(TimeSpan.FromSeconds(1)) .Subscribe(x => Debug.Log($"Second elapsed: {x}"));
// Frame-based observable (uses FrameProvider) - Unity-specificObservable.IntervalFrame(60) // Every 60 frames (~1 second at 60 FPS) .Subscribe(x => Debug.Log($"Frame count: {x}"));
// EveryUpdate provides frame-by-frame reactive updatesObservable.EveryUpdate() .Subscribe(_ => UpdateGameLogic());Operators - Transforming Event Streams: R3 provides comprehensive LINQ-like operators for transforming, filtering, and combining event streams:
Where, Distinct, DistinctUntilChanged, Skip, Take, First, LastSelect, SelectMany, Cast, OfTypeMerge, Zip, CombineLatest, WithLatestFromThrottle, Debounce, Delay, Timeout, Buffer, WindowThrottleFrame, DebounceFrame, DelayFrame, BufferFrame (Unity-specific)Scan, Aggregate, Count, Sum, Average// Practical game example: Combo input detectioninputStream .Buffer(TimeSpan.FromSeconds(0.5)) // Group inputs within 0.5s windows .Where(inputs => inputs.Count >= 3) // Require at least 3 inputs .Subscribe(inputs => TriggerComboAttack(inputs));
// Frame-based cooldown systemattackButton .ThrottleFrame(30) // Limit to once per 30 frames (~0.5s at 60 FPS) .Subscribe(_ => ExecuteAttack());Subjects - Event Broadcasters: Subjects act as both observable sources and observers, serving as event broadcasters that multiple systems can subscribe to:
Subject<T>: Basic event stream with no memory of past valuesBehaviorSubject<T>: Maintains current value, new subscribers immediately receive latest valueReplaySubject<T>: Buffers multiple past values, replaying them to new subscribersReactiveProperty<T>: Specialized BehaviorSubject with duplicate-elimination, ideal for data binding// Player health as ReactivePropertypublic class PlayerHealth : MonoBehaviour{ private ReactiveProperty<int> health = new(100);
// Expose as read-only observable public ReadOnlyReactiveProperty<int> Health => health;
public void TakeDamage(int amount) { health.Value -= amount; // Automatically notifies all subscribers }}
// UI subscribes to health changesplayerHealth.Health .Subscribe(newHealth => UpdateHealthBar(newHealth)) .AddTo(this); // Auto-dispose when GameObject is destroyedReactiveProperty - Data Binding Foundation: ReactiveProperty is R3’s most powerful feature for game UI and state management. It wraps a value and notifies observers whenever that value changes, but only when the new value differs from the current value (duplicate elimination). This makes it perfect for:
// Game pause statepublic class GameStateManager : MonoBehaviour{ private ReactiveProperty<bool> isPaused = new(false); public ReadOnlyReactiveProperty<bool> IsPaused => isPaused;
public void TogglePause() { isPaused.Value = !isPaused.Value; // All subscribers automatically notified }}
// Multiple systems react to pause stategameState.IsPaused .Subscribe(paused => Time.timeScale = paused ? 0 : 1) .AddTo(this);
gameState.IsPaused .Subscribe(paused => pauseMenu.SetActive(paused)) .AddTo(this);FrameProvider - Game Loop Integration:
The FrameProvider is R3’s killer feature for Unity development, providing frame-synchronized reactive operations:
// Track value changes every frameObservable.EveryValueChanged(enemy, e => e.transform.position) .Subscribe(pos => UpdateMinimapMarker(pos));
// Execute logic on specific Unity lifecycle eventsObservable.EveryUpdate() .Where(_ => isGameActive) .Subscribe(_ => UpdateGameSimulation());
Observable.EveryFixedUpdate() .Subscribe(_ => UpdatePhysicsSimulation());
Observable.EveryLateUpdate() .Subscribe(_ => UpdateCameraTracking());Automatic Cleanup with AddTo:
R3 provides AddTo(Component) extension method for automatic subscription disposal when GameObjects are destroyed, preventing memory leaks:
someObservable .Subscribe(value => HandleValue(value)) .AddTo(this); // Auto-unsubscribe when this GameObject is destroyedThis pattern is critical for Unity development where components are frequently created and destroyed. Without proper cleanup, subscriptions can leak, causing performance degradation and unexpected behavior.
Integration with Unity Events: R3 provides bridges to convert Unity’s event systems into observable streams:
// Convert UnityEvent to Observablebutton.onClick.AsObservable() .Subscribe(_ => OnButtonClicked());
// Convert Collision events to Observablethis.OnCollisionEnterAsObservable() .Subscribe(collision => HandleCollision(collision));Player Input Processing:
// Dash mechanic with cooldowndashInputObservable .ThrottleFrame(120) // 2-second cooldown at 60 FPS .Where(_ => !isDashing) .Subscribe(_ => ExecuteDash()) .AddTo(this);Enemy Spawn System:
// Wave-based spawning with increasing difficultyObservable.IntervalFrame(180) // Spawn every 3 seconds .Select((_, waveNumber) => waveNumber) .Select(wave => CalculateSpawnCount(wave)) .Subscribe(count => SpawnEnemies(count)) .AddTo(this);Health Monitoring:
// Player health tracking with critical health warningplayerHealth.Health .Where(hp => hp <= 20) .Subscribe(_ => TriggerLowHealthWarning()) .AddTo(this);
// Game over on zero healthplayerHealth.Health .Where(hp => hp <= 0) .First() // Only trigger once .Subscribe(_ => TriggerGameOver()) .AddTo(this);UI Data Binding:
// Reactive UI updatesplayerStats.Health .Subscribe(hp => healthText.text = $"HP: {hp}") .AddTo(this);
playerStats.Score .Subscribe(score => scoreText.text = $"Score: {score}") .AddTo(this);Combo Detection System:
// Detect rapid sequential attacks for combo systemattackInput .Buffer(TimeSpan.FromSeconds(1)) // 1-second combo window .Where(attacks => attacks.Count >= 3) .Subscribe(attacks => ExecuteComboAttack(attacks.Count)) .AddTo(this);R3 integrates seamlessly with our existing VContainer and EventManager architecture:
With VContainer:
ReactiveProperty fields that other systems subscribe to via dependency injectionWith EventManager:
AsObservable()With UniTask:
ToUniTask() for async/await integrationObservable.FromUniTask()Where and Distinct operators prevent unnecessary downstream processingThrottle and Debounce reduce event processing frequency for performance-critical systemsR3 enables RentEarth to implement complex reactive gameplay patterns (combo systems, cooldowns, state machines, UI binding) with clean, declarative code that performs excellently even in high-intensity scenarios with hundreds of entities and continuous input processing.
ObservableCollections GitHub Repository
ObservableCollections is a high-performance reactive collection library designed for .NET platforms including Unity, providing observable data structures that notify subscribers whenever the collection changes. Created by Cysharp (same creators as R3 and UniTask), ObservableCollections addresses the performance limitations of .NET’s standard ObservableCollection<T> while adding powerful view synchronization and reactive programming integration.
In game development, collections are fundamental - enemy lists, inventory items, active effects, spawned objects, quest objectives, and UI elements are all collections that change over time. ObservableCollections makes these dynamic collections reactive, enabling automatic UI updates, synchronized views, and efficient change propagation without manual polling or event wiring.
Zero-Allocation Generic Events:
Standard .NET ObservableCollection<T> uses non-generic NotifyCollectionChangedEventArgs which boxes value types (int, float, structs), causing garbage allocation on every change. ObservableCollections uses fully generic events (NotifyCollectionChangedEventHandler<T> and NotifyCollectionChangedEventArgs<T>) that eliminate boxing, resulting in zero-allocation change notifications even for value-type collections.
Rich Change Notifications: Unlike standard observable collections that only notify on add/remove/replace/move operations, ObservableCollections provides notifications for:
Sort and Reverse notifications are critical for game UI where leaderboards, inventory sorting, and priority-based lists need to reflect changes without full collection reconstruction.
Synchronized Views:
ObservableCollections’ most powerful feature is synchronized views via the ISynchronizedView<T, TView> interface. Views are derived collections that automatically stay synchronized with their source collection through transformations:
// Source collection of all enemiesObservableList<Enemy> allEnemies = new();
// View of only alive enemies (automatically updates)var aliveEnemies = allEnemies.CreateView(enemy => enemy.IsAlive);
// View sorted by distance from player (automatically re-sorts)var nearestEnemies = allEnemies .CreateSortedView(enemy => Vector3.Distance(player.position, enemy.position));
// View with transformed type (Enemy → UIElement)var enemyUIElements = allEnemies .CreateView(enemy => new EnemyHealthBar(enemy));Views eliminate the need for manually maintaining multiple synchronized lists - changes to the source collection automatically propagate through all views with efficient incremental updates.
Comprehensive Collection Types: ObservableCollections provides seven fundamental data structures, each with observable change notifications:
List<T>, dynamic array with index accessEach collection type provides the same interface as its standard .NET equivalent, making migration straightforward - simply replace List<T> with ObservableList<T> and add subscriptions.
Collection Change Observation:
ObservableList<Enemy> enemies = new();
// Subscribe to all changesenemies.ObserveCountChanged() .Subscribe(count => UpdateEnemyCounter(count));
// Subscribe to specific change typesenemies.ObserveAdd() .Subscribe(e => OnEnemySpawned(e.Value));
enemies.ObserveRemove() .Subscribe(e => OnEnemyDestroyed(e.Value));
enemies.ObserveReplace() .Subscribe(e => OnEnemyReplaced(e.OldValue, e.NewValue));
// Subscribe to any collection modificationenemies.ObserveChanged() .Subscribe(e => Debug.Log($"Collection changed: {e.Action}"));Filtered Views:
ObservableList<Entity> allEntities = new();
// View of only enemiesvar enemies = allEntities.CreateView(entity => entity is Enemy);
// View of entities within radius (predicate can access external state)var nearbyEntities = allEntities.CreateView(entity => Vector3.Distance(player.position, entity.position) < 50f);
// Adding to source automatically updates viewsallEntities.Add(new Enemy()); // Automatically appears in 'enemies' viewSorted Views:
ObservableList<Enemy> enemies = new();
// Sorted by health (ascending)var sortedByHealth = enemies.CreateSortedView( enemy => enemy.Health, comparer: Comparer<int>.Default);
// Sorted by distance (custom comparison)var sortedByDistance = enemies.CreateSortedView( enemy => Vector3.Distance(player.position, enemy.position));
// View automatically re-sorts when source collection changesenemies.Add(newEnemy); // Inserted at correct sorted positionenemies[0].Health -= 10; // Does NOT automatically re-sort (mutation detection limitation)enemies.Sort(); // Explicit sort triggers view updateTransformed Views (Type Conversion):
ObservableList<Enemy> enemies = new();
// Transform enemies into UI representationsvar healthBars = enemies.CreateView( enemy => new EnemyHealthBar(enemy));
// healthBars automatically contains HealthBar instances// Adding/removing enemies automatically adds/removes health barsRange Operations (Efficient Bulk Changes):
ObservableList<int> numbers = new();
// Add multiple items with single notificationnumbers.AddRange(new[] { 1, 2, 3, 4, 5 });
// Remove range with single notificationnumbers.RemoveRange(1, 3); // Remove 3 items starting at index 1
// Efficient for UI updates - one refresh instead of fiveRing Buffer for History Tracking:
// Track last 60 frames of player positionObservableRingBuffer<Vector3> positionHistory = new(60);
void Update(){ positionHistory.Add(transform.position); // Automatically evicts oldest}
// Useful for trail effects, undo systems, rolling averagesfloat averageSpeed = positionHistory .Zip(positionHistory.Skip(1), (a, b) => Vector3.Distance(a, b)) .Average();ObservableCollections provides seamless R3 integration through extension methods that convert collection change events into observables:
ObservableList<Enemy> enemies = new();
// All change events as observable streamenemies.ObserveChanged() .Subscribe(e => Debug.Log($"Action: {e.Action}, Index: {e.Index}"));
// Specific change typesenemies.ObserveAdd() .Subscribe(added => SpawnEnemyVisual(added.Value));
enemies.ObserveRemove() .Subscribe(removed => DestroyEnemyVisual(removed.Value));
// Count changes as reactive streamenemies.ObserveCountChanged() .Subscribe(count => UpdateWaveProgress(count));
// Combined with R3 operatorsenemies.ObserveAdd() .ThrottleFrame(10) // Batch spawn notifications .Subscribe(batch => SpawnEnemyBatch(batch));This integration enables powerful reactive patterns - collections become observable event sources that can be transformed, filtered, and combined using R3’s operator suite.
Enemy Management System:
public class EnemyManager : MonoBehaviour{ // Observable collection of all enemies public ObservableList<Enemy> AllEnemies { get; } = new();
// Synchronized view of alive enemies only public ISynchronizedView<Enemy> AliveEnemies { get; private set; }
// View sorted by distance from player public ISynchronizedView<Enemy> NearestEnemies { get; private set; }
void Start() { AliveEnemies = AllEnemies.CreateView(e => e.IsAlive);
NearestEnemies = AllEnemies.CreateSortedView(e => Vector3.Distance(player.position, e.position));
// UI updates automatically when enemy count changes AllEnemies.ObserveCountChanged() .Subscribe(count => enemyCountText.text = $"Enemies: {count}") .AddTo(this);
// Play sound when enemy spawns AllEnemies.ObserveAdd() .Subscribe(e => audioManager.PlaySpawnSound()) .AddTo(this); }
public void SpawnEnemy(Vector3 position) { var enemy = Instantiate(enemyPrefab, position, Quaternion.identity); AllEnemies.Add(enemy); // Automatically updates all views and triggers events }
public void RemoveEnemy(Enemy enemy) { AllEnemies.Remove(enemy); // Automatically updates all views Destroy(enemy.gameObject); }}Inventory System with Reactive UI:
public class Inventory : MonoBehaviour{ // Observable inventory items public ObservableList<InventoryItem> Items { get; } = new();
// View of equipped items private ISynchronizedView<InventoryItem> equippedItems;
// View of consumable items private ISynchronizedView<InventoryItem> consumables;
void Start() { equippedItems = Items.CreateView(item => item.IsEquipped); consumables = Items.CreateView(item => item.Type == ItemType.Consumable);
// UI automatically updates when inventory changes Items.ObserveChanged() .Subscribe(_ => RefreshInventoryUI()) .AddTo(this);
// Show notification when rare item added Items.ObserveAdd() .Where(e => e.Value.Rarity == Rarity.Legendary) .Subscribe(e => ShowItemAcquiredNotification(e.Value)) .AddTo(this); }
public void AddItem(InventoryItem item) { Items.Add(item); // Automatically updates views and triggers UI refresh }
public void SortByRarity() { Items.Sort((a, b) => b.Rarity.CompareTo(a.Rarity)); // Triggers sort notification }}Active Effects System:
public class PlayerEffects : MonoBehaviour{ // Track active status effects public ObservableList<StatusEffect> ActiveEffects { get; } = new();
// View of only buff effects private ISynchronizedView<StatusEffect> activeBuffs;
// View of only debuff effects private ISynchronizedView<StatusEffect> activeDebuffs;
void Start() { activeBuffs = ActiveEffects.CreateView(e => e.Type == EffectType.Buff); activeDebuffs = ActiveEffects.CreateView(e => e.Type == EffectType.Debuff);
// Update UI icons when effects change ActiveEffects.ObserveChanged() .Subscribe(_ => UpdateEffectIcons()) .AddTo(this);
// Log when critical debuffs applied activeDebuffs.ObserveAdd() .Where(e => e.Value.Severity == Severity.Critical) .Subscribe(e => Debug.LogWarning($"Critical debuff applied: {e.Value.Name}")) .AddTo(this); }
public void ApplyEffect(StatusEffect effect) { ActiveEffects.Add(effect); // Views and UI automatically update }
void Update() { // Remove expired effects ActiveEffects.RemoveAll(e => e.IsExpired); // Each removal triggers events and view updates }}Leaderboard System:
public class LeaderboardManager : MonoBehaviour{ // Observable leaderboard entries public ObservableList<LeaderboardEntry> Entries { get; } = new();
// Sorted view (top 10) private ISynchronizedView<LeaderboardEntry> topTen;
void Start() { // Create descending sorted view by score topTen = Entries.CreateSortedView( entry => -entry.Score, // Negative for descending order limit: 10); // Only keep top 10 in view
// UI updates automatically when leaderboard changes topTen.ObserveChanged() .Subscribe(_ => RefreshLeaderboardUI()) .AddTo(this); }
public void SubmitScore(string playerName, int score) { var entry = new LeaderboardEntry(playerName, score); Entries.Add(entry); // Automatically inserted in correct sorted position in view }}Performance Monitoring (Ring Buffer):
public class PerformanceMonitor : MonoBehaviour{ // Track last 120 frames of FPS private ObservableRingBuffer<float> fpsHistory = new(120);
void Update() { float currentFPS = 1f / Time.deltaTime; fpsHistory.Add(currentFPS); // Oldest value automatically evicted
// Calculate rolling average float averageFPS = fpsHistory.Average(); fpsText.text = $"FPS: {currentFPS:F0} (Avg: {averageFPS:F0})"; }}With VContainer:
ObservableList or other observable collections as public propertiesWith R3:
ObserveChanged(), ObserveAdd(), etc.With EventManager:
With UniTask:
ToUniTask() extensionObservableCollections enables RentEarth to implement complex data-driven systems (enemy management, inventory, effects, leaderboards) with automatic UI synchronization, efficient change propagation, and clean separation between data models and views - all with excellent performance characteristics suitable for real-time gameplay.
Cinemachine is Unity’s powerful procedural camera system that enables AAA-quality camera behaviors without complex manual scripting. Instead of writing custom camera controllers with follow logic, damping, and collision detection, Cinemachine provides a component-based approach where cameras are assembled from modular building blocks called Virtual Cameras. This shifts camera work from programming to design, allowing rapid iteration on camera feel and behavior.
In RentEarth, Cinemachine serves as the foundation for all camera systems, providing dynamic camera control that responds to gameplay situations. The framework’s Virtual Camera concept allows us to define multiple camera perspectives (combat camera, boss fight camera, death camera) and seamlessly blend between them based on game state. Each Virtual Camera can have its own priority, follow target, look-at target, and behavioral components, making it trivial to create context-sensitive camera work that enhances the player experience.
Dynamic Follow & Framing: Cinemachine’s Body components (Transposer, Framing Transposer, Orbital Transposer) provide sophisticated player-following logic with customizable damping, dead zones, and screen composition rules. This ensures the player character stays optimally framed even during intense combat sequences.
Smart Aim Handling: The Aim components manage where the camera looks, supporting hard look-at, composer-based framing, POV rotation, and more. For RentEarth’ combat scenarios, this allows the camera to intelligently frame both the player and approaching enemy threats.
Procedural Noise & Shake: The CinemachineBasicMultiChannelPerlin component enables realistic camera shake for impacts, explosions, and damage feedback. Unlike manual shake implementations, Cinemachine’s procedural noise is based on Perlin noise profiles, providing organic, non-repetitive camera movement that can be tuned with frequency and amplitude curves.
Collision Detection: Cinemachine’s Collider extension automatically handles camera collision with environment geometry, preventing the camera from clipping through walls or obstacles. This is crucial for RentEarth’ varied combat arenas where maintaining visibility is essential.
State-Driven Cameras: Integration with Unity’s Timeline and animation system allows camera cuts and blends to be choreographed alongside gameplay events. Boss introductions, player death sequences, and victory celebrations can all leverage cinematic camera work.
Input-Responsive Systems: Cinemachine’s input axes support player-controlled camera rotation, zoom, and positioning, enabling features like manual camera control during strategic moments or targeting specific enemies.
Cinemachine is designed for zero-garbage-collection camera updates, running all calculations on the same frame timeline as Unity’s camera system. The framework uses a priority-based blending system where multiple Virtual Cameras can coexist, with the highest-priority camera taking control. Transitions between cameras use sophisticated blending algorithms that can interpolate position, rotation, and lens properties over time, creating smooth cinematic transitions without writing custom interpolation code.
For RentEarth’ architecture, Cinemachine integrates seamlessly with our existing VContainer and UniTask infrastructure. Virtual Cameras can be activated/deactivated in response to EventManager triggers (player death, boss spawn, wave completion), and camera transitions can be awaited using UniTask for precise sequencing. The component-based design also supports dependency injection - custom Cinemachine extensions can receive injected dependencies for accessing game state when making camera decisions.
The separation between Virtual Cameras (what you want to shoot) and the Brain (how to execute the shot) provides excellent testability and designer iteration. Camera behaviors can be tuned in the editor without code changes, and multiple camera setups can be A/B tested simply by adjusting priorities. This design philosophy aligns perfectly with RentEarth’ emphasis on clean architecture and rapid iteration.
RentEarth embraces modern AI-assisted development workflows through the Model Context Protocol (MCP), enabling seamless collaboration between human developers and AI assistants directly within the Unity environment. MCP provides a standardized interface for AI tools to interact with development environments, allowing for intelligent code suggestions, automated refactoring, asset management, and context-aware assistance throughout the development process.
By integrating MCP into our development workflow, we accelerate iteration speed, reduce repetitive tasks, and enable developers to focus on creative problem-solving and game design rather than boilerplate code generation. AI assistants can understand the project’s architecture, suggest improvements aligned with our coding standards, and help maintain consistency across the codebase. This is particularly valuable for RentEarth’ complex systems involving VContainer dependency graphs, UniTask async patterns, and Cinemachine configurations.
Unity MCP is an open-source bridge that connects AI assistants (such as Claude, Cursor, and other LLM-powered tools) directly to the Unity Editor through the Model Context Protocol. This integration enables natural language control of Unity projects, allowing developers to leverage AI for automating workflows, managing assets, and accelerating game development tasks that would traditionally require manual editor interactions.
Unity MCP consists of two complementary components that work together:
Asset Management
Scene Manipulation
Script Creation & Editing
Workflow Automation
Unity MCP enhances RentEarth development by enabling AI assistants to:
For developers joining the RentEarth project, Unity MCP setup involves:
The system supports multiple simultaneous Unity instances and integrates seamlessly with popular AI coding clients, requiring minimal configuration beyond initial setup. For RentEarth contributors, Unity MCP accelerates onboarding by allowing new developers to query the AI about architecture decisions, quickly generate boilerplate code following project conventions, and receive context-aware assistance when implementing new gameplay systems.
Context7 is an open-source Model Context Protocol (MCP) server that provides AI assistants with up-to-date, version-specific documentation and code examples directly from the source. Unlike static documentation or LLM training data that may be outdated, Context7 dynamically retrieves the latest official documentation, ensuring AI-generated code and suggestions align with current best practices and API specifications.
Context7 acts as a real-time documentation bridge between AI assistants and official framework documentation. When an AI needs to reference Unity APIs, component behaviors, or implementation patterns, Context7 fetches the most recent documentation from Unity’s official sources and injects it directly into the AI’s context. This ensures that code suggestions, API usage examples, and architectural recommendations reflect the latest Unity version being used in the project.
Version-Specific Accuracy
Enhanced Code Generation
Contextual API Understanding
Integration with Existing Tools
For RentEarth contributors, Context7 enhances AI assistance in several ways:
https://context7.com?q=unityContext7 significantly reduces the friction of working with Unity’s extensive API surface, allowing both experienced developers and new contributors to leverage accurate, up-to-date documentation without leaving their development environment.
Location: Assets/Scripts/Core/GameLifetimeScope.cs
The GameLifetimeScope is the central orchestrator for RentEarth’ dependency injection container, extending VContainer’s LifetimeScope base class. This class is responsible for bootstrapping the entire game architecture by registering all core managers in their correct dependency order and ensuring they persist across scene transitions.
DontDestroyOnLoad() to persist across scene transitions, maintaining game state throughout the player’s sessionAsync Initialization:
The GameLifetimeScope supports async manager initialization through VContainer’s IStartable interface. TerrainManager, for example, implements async initialization to generate the initial terrain chunks without blocking the main thread, ensuring smooth game startup.
The RegisterOrCreateManager<T>() helper method demonstrates a smart pattern: it first checks if a manager already exists in the scene (useful during editor testing) and registers it if found, otherwise creates a new instance. This flexibility supports both runtime initialization and editor-based scene setup workflows.
Location: Assets/Scripts/Core/GameManager.cs
The GameManager serves as the universal game state controller, coordinating high-level game functionality including scene management, pause/resume mechanics, and integration with the main menu system. It acts as a facade for common game operations, providing a clean API for other systems to interact with.
Time.timeScale with corresponding event triggers for UI updatesCamera.main calls that use FindGameObjectWithTag internallyThe GameManager demonstrates proper dependency injection patterns - it receives EventManager and MainMenuManager through constructor injection, explicitly declaring its dependencies. It also properly manages the lifecycle of event subscriptions, subscribing in Start() and unsubscribing in OnDestroy() to prevent memory leaks.
Location: Assets/Scripts/Core/EventManager.cs
The EventManager implements a centralized event system using Unity’s UnityEvent framework, enabling decoupled communication between game systems. Rather than having systems directly reference each other, they communicate through events, making the architecture more modular and testable.
Input Events:
OnEscapePressed: Triggered when the Escape key is pressedOnPausePressed: Triggered when the Pause key is pressedGame State Events:
OnGamePaused: Fired when the game enters paused stateOnGameResumed: Fired when the game exits paused stateScene Events:
OnSceneLoadStarted: Notifies listeners when a scene begins loading (includes scene name)OnSceneLoadCompleted: Notifies listeners when a scene finishes loadingPlayer Events:
OnPlayerHealthChanged: Broadcasts player health updates (prepared for future implementation)OnPlayerDied: Signals player death eventUI Events:
OnMainMenuOpened: Indicates main menu has been displayedOnMainMenuClosed: Indicates main menu has been hiddenThe EventManager provides trigger helper methods (e.g., TriggerEscapePressed(), TriggerGamePaused()) that encapsulate event invocation, making it easy to fire events from anywhere in the codebase. This pattern ensures null-safety through the null-conditional operator (?.Invoke()) and provides a single source of truth for all game events. The debug helper LogActiveListeners() aids in troubleshooting by reporting listener counts for registered events.
Location: Assets/Scripts/Core/InputManager.cs
The InputManager handles all global system-level input using Unity’s new Input System package, translating raw keyboard input into high-level events through the EventManager. It focuses exclusively on global inputs (Escape, Pause, system hotkeys) while leaving game-specific inputs (player movement, combat) to dedicated controller classes.
IsKeyPressed() and IsKeyPressedThisFrame() helpers for other scripts that need to query input stateBy separating global inputs from gameplay inputs, the architecture remains clean and focused. The InputManager doesn’t need to know about player controllers, enemy AI, or game mechanics - it simply reports when system keys are pressed. This separation of concerns makes the codebase easier to understand and modify. Game-specific input handling can be implemented in player controller classes or action map systems without cluttering this core system.
Location: Assets/BugWars/UI/MainMenu/MainMenuManager.cs
The MainMenuManager controls the main menu user interface using Unity’s UI Toolkit (formerly UIElements), providing a modern, performant UI system built on retained-mode rendering. It manages menu visibility, button interactions, and integration with the game’s event system.
UIDocument component with UXML visual trees for declarative UI definition, separating presentation from logicQ<T>() query API (MainMenuContainer, SettingsButton, ExitButton)DisplayStyle.Flex and DisplayStyle.None, providing instant show/hide functionalityIsMenuVisible property to allow other systems to query menu stateThe MainMenuManager demonstrates proper component lifecycle management - it waits for VContainer to inject dependencies in Construct(), initializes UI elements in Start() after the UIDocument is configured, and cleans up event subscriptions in OnDestroy(). The OnExitButtonClicked() method includes platform-specific quit logic, stopping play mode in the editor and calling Application.Quit() in builds.
This component showcases the synergy between VContainer and UI Toolkit - dependencies are injected automatically, while UI elements are queried and configured using UI Toolkit’s retained-mode API. The result is clean, testable code that separates concerns between UI presentation and game logic.
Location: Assets/Scripts/Terrain/
RentEarth features a sophisticated procedural terrain generation system built on a chunk-based streaming architecture, providing infinite explorable worlds with optimal performance through intelligent culling and asynchronous loading.
Location: Assets/Scripts/Terrain/TerrainManager.cs
The TerrainManager orchestrates the entire terrain generation pipeline, managing chunk lifecycle from creation through rendering to eventual unloading based on player position and camera visibility.
Key Features:
Technical Implementation Details:
UniTask.WhenAll() for parallel chunk generation, significantly reducing initial world load timePerformance Optimizations:
The TerrainManager implements several critical optimizations:
Integration Points:
IStartable interface for automatic initializationLocation: Assets/Scripts/Terrain/TerrainChunk.cs
Individual terrain chunk responsible for mesh generation, physics collision, and visibility management.
Core Functionality:
MeshCollider component for terrain collision detection, enabling gameplay interactions with terrainShow()/Hide() methods for frustum culling, disabling MeshRenderer without destroying the chunk objectMesh Generation Pipeline:
Memory Management:
TerrainChunk implements proper cleanup in OnDestroy(), ensuring mesh memory is properly released when chunks are unloaded. This prevents memory leaks during extended gameplay sessions.
Location: Assets/BugWars/Prefabs/Character/Samurai/
RentEarth implements a high-performance 2D sprite rendering system in a 3D environment using a custom URP shader with GPU-based billboarding, mesh-based rendering, and frame-based animation driven by JSON sprite atlases.
The sprite rendering system replaces Unity’s traditional SpriteRenderer component with a MeshRenderer + custom shader approach, providing:
This approach is significantly more performant than traditional billboard systems that update transforms in LateUpdate(), as all rotation calculations happen on the GPU within the vertex shader.
Location: Assets/BugWars/Prefabs/Character/Samurai/SamuraiAnimatedSprite_Unity6.shader
Custom URP-compatible shader implementing GPU-based billboarding with sprite sheet animation support.
Shader Features:
Mode 0: No billboarding (standard object transform)Mode 1: Cylindrical billboarding (Y-axis locked, ideal for ground-based characters)Mode 2: Spherical billboarding (full camera facing)_FrameUVMin and _FrameUVMax shader properties for sprite atlas animation_FlipX and _FlipY parameters for directional character facing without mesh regenerationTechnical Implementation:
The shader’s vertex function calculates billboard rotation by constructing world-space quad positions from camera basis vectors:
Performance Benefits:
LateUpdate() overhead)Location: Assets/BugWars/Prefabs/Character/Samurai/SpriteAtlasData.cs
JSON-driven sprite atlas format for defining animation frames and sequences.
Atlas Structure:
{ "meta": { "version": "1.0", "size": { "w": 1024, "h": 1024 }, "frameSize": 128, "frameCount": 60 }, "frames": { "Idle_0": { "x": 0, "y": 0, "w": 128, "h": 128, "animation": "Idle", "index": 0, "uv": { "min": { "x": 0.0, "y": 0.875 }, "max": { "x": 0.125, "y": 1.0 } } } }, "animations": { "Idle": { "frames": ["Idle_0", "Idle_1", "Idle_2", "Idle_3"], "frameCount": 4, "fps": 8 } }}Key Components:
Data Classes:
SpriteAtlasData: Root container with Newtonsoft.Json deserializationFrameData: Individual frame definition with pixel coordinates and UV boundsAnimationData: Frame sequence with FPS and calculated frame durationLocation: Assets/BugWars/Prefabs/Character/Samurai/Samurai.cs
The Samurai class demonstrates the sprite rendering system’s practical implementation, serving as the player character and reference implementation for future entities.
Rendering Pipeline:
Component Replacement:
SpriteRenderer component (incompatible with custom materials)MeshFilter with procedurally-generated quad mesh (1x1 unit, centered)MeshRenderer with assigned SamuraiMaterial (uses custom shader)Animation State Mapping:
EntityAnimationState enum to Samurai-specific animation namesEntityAnimationState.Walk → "Walk" animation in SamuraiAtlas.jsonFrame-Based Animation:
_FrameUVMin: Frame’s minimum UV coordinates_FrameUVMax: Frame’s maximum UV coordinatesQuad Mesh Generation:
// 1x1 unit quad centered at origin with full UV mappingVector3[] vertices = { new Vector3(-0.5f, -0.5f, 0), // Bottom-left new Vector3( 0.5f, -0.5f, 0), // Bottom-right new Vector3(-0.5f, 0.5f, 0), // Top-left new Vector3( 0.5f, 0.5f, 0) // Top-right};Vector2[] uvs = { (0,0), (1,0), (0,1), (1,1) };The shader remaps these 0-1 UV coordinates to the actual frame bounds, allowing a single static mesh to display all animation frames.
Performance Optimizations:
Shader.PropertyToID() called once in static fieldsenableBillboard = false since shader handles rotationAvailable Animations:
The Samurai includes a comprehensive animation set mapped to universal entity states:
Idle, Walk, Run, JumpAttack_1, Attack_2, Attack_3Hurt, Dead, ShieldEach animation has configurable FPS in the atlas JSON, allowing independent playback speeds for different actions (e.g., fast attack animations, slow idle breathing).
The sprite rendering system integrates seamlessly with the Entity base class through several mechanisms:
MaterialPropertyBlock Sharing:
The Entity class provides GetSpritePropertyBlock() and ApplySpritePropertyBlock() helper methods, allowing derived classes like Samurai to extend the property block with additional shader parameters (frame UVs) while preserving Entity’s flip state.
Sprite Flipping:
Entity’s SetSpriteFlip(bool flipX, bool flipY) method updates shader parameters that the custom shader reads, enabling camera-relative directional flipping without mesh modifications. The Samurai shader includes _FlipX and _FlipY parameters that Entity controls based on movement direction.
Billboard Compatibility:
While Entity includes CPU-based billboarding via ApplyBillboard(), characters using the custom shader disable this (enableBillboard = false) since GPU-based billboarding is superior. Entity’s billboard system remains for entities using traditional rendering.
Animation State Abstraction:
Entity defines a universal EntityAnimationState enum with states like Idle, Walk, Attack_1, etc. Derived classes override OnAnimationStateChanged() to map these universal states to character-specific animation names in their atlas. This enables polymorphic animation control—calling entity.SetAnimationState(EntityAnimationState.Walk) works on any entity type.
Location: Assets/Scripts/Entity/
RentEarth implements a unified entity system providing a common foundation for all game actors (players, enemies, NPCs) with shared functionality for health, movement, and visual representation.
Location: Assets/Scripts/Entity/Entity.cs
Abstract base class defining core functionality shared across all game entities.
Key Features:
EntityAnimationState enum with states mappable to character-specific animations (Idle, Walk, Run, Jump, Attack variants, Hurt, Dead, Shield)_FlipX, _FlipY)TakeDamage(), Heal(), and Die() methods, with virtual methods allowing subclasses to customize behaviorTechnical Implementation:
RequireComponent attributes to ensure Rigidbody and CapsuleCollider are presentLateUpdate() to rotate sprites toward camera after all movement is complete (used for entities without custom shaders)Animation State System:
Entities use a two-tier animation system:
EntityAnimationState enumOnAnimationStateChanged() to map universal states to their specific animation namesThis abstraction allows gameplay code to call entity.SetAnimationState(EntityAnimationState.Attack_1) without knowing the entity’s specific animation implementation.
Sprite Flipping System:
The Entity class provides automatic camera-relative sprite flipping:
UpdateFacingDirection() calculates whether the entity is moving left or right relative to the cameraSetSpriteFlip() updates shader parameters via MaterialPropertyBlockDeath Handling:
The Die() virtual method provides a customization point for entity-specific death behaviors:
Location: Assets/Scripts/Entity/EntityManager.cs
Centralized registry and query system for all game entities, enabling efficient spatial queries and entity tracking.
Core Capabilities:
GetEntitiesInRadius(Vector3 pos, float radius) - Find all entities within circular areaGetClosestEntity(Vector3 position) - Find nearest entity to a pointGetEntitiesOfType<T>() returns only entities of specific types (e.g., all enemies, all players)GetEntityByName(string name) for quest systems and scripted encountersUse Cases:
Performance Considerations:
EntityManager caches entity references and uses efficient LINQ queries. For high-frequency spatial queries (hundreds per frame), consider implementing spatial partitioning (quadtree/octree) in future optimization passes.
Location: Assets/Scripts/Core/CameraManager.cs
The CameraManager provides centralized control over all camera systems in RentEarth, managing Cinemachine Virtual Cameras and providing utility methods for frustum culling and spatial queries.
Key Responsibilities:
FindObjectsOfType callsIsPointInFrustum(Vector3 point) - Check if world position is visibleIsBoundsInFrustum(Bounds bounds) - Check if entire bounding box is visibleGetFrustumPlanes() - Get current camera frustum planes for batch cullingGetDistanceFromCamera(Vector3 point) - Calculate distance for LOD systemsIntegration with Terrain System:
The CameraManager’s frustum culling methods are critical for terrain performance:
// TerrainManager uses CameraManager for chunk cullingvar frustumPlanes = cameraManager.GetFrustumPlanes();foreach (var chunk in chunks) { bool isVisible = cameraManager.IsBoundsInFrustum(chunk.Bounds); if (isVisible) chunk.Show(); else chunk.Hide();}Cinemachine Integration:
RefreshCameras() method re-scans scene for newly instantiated virtual camerasVContainer Integration:
Registered as singleton in GameLifetimeScope, allowing any game system to receive CameraManager via dependency injection for camera-aware functionality.
RentEarth’ systems work together through a well-orchestrated initialization and runtime interaction pattern, leveraging VContainer’s dependency injection and EventManager’s decoupled communication.
GameLifetimeScope Startup:
Core System Initialization (in dependency order):
World Generation:
Game Ready State:
Player Movement → Terrain Streaming:
Camera Movement → Frustum Culling:
Show() enables MeshRendererHide() disables MeshRenderer (saves draw calls)Input Events → Game State Changes:
EventManager.TriggerEscapePressed()MainMenuManager.ToggleMenu()Entity Interactions:
Die() method, unregister from EntityManagerEvent-Driven Communication Examples:
// System A triggers eventeventManager.TriggerGamePaused();
// System B responds (previously subscribed)void OnGamePaused() { // Pause terrain generation // Stop entity AI updates // Show pause menu}This architecture provides several key benefits:
Status: Currently in development
The combat system will feature:
Implemented Foundation:
Status: Planned
RentEarth will implement a multi-layered progression system:
System Dependencies:
Status: Design phase with technical foundation in place
Each bug type will feature unique characteristics:
Technical Foundation:
Attack_1 → "Bite" for ants, "Sting" for wasps)RentEarth employs a sophisticated camera system powered by Cinemachine to create dynamic, responsive camera behaviors that enhance gameplay clarity and cinematic presentation. The camera is not just a passive observer but an active participant in the game’s feel and player experience.
The CameraManager (detailed above) provides the programmatic interface, while Cinemachine Virtual Cameras define the actual camera behaviors and compositions.
Main Combat Camera (Priority: 10)
Boss Encounter Camera (Priority: 20)
Death Camera (Priority: 30)
Cutscene/Scripted Cameras (Priority: 100)
The camera system integrates with RentEarth’ event-driven architecture through EventManager subscriptions:
OnGamePaused → Reduces camera update rate and locks positionOnGameResumed → Restores normal camera behaviorOnPlayerHealthChanged → Triggers intensity-based FOV changes and trauma shakeOnPlayerDied → Activates death camera sequenceCamera state changes can be sequenced with UniTask for precise timing - for example, awaiting a camera blend completion before starting a boss fight introduction or victory celebration. Custom Cinemachine extensions can receive VContainer-injected dependencies to access GameManager state, enabling camera behaviors that respond to game conditions.
Core Architecture
✅ COMPLETE - VContainer dependency injection, EventManager, GameManager, InputManager all implemented and operational.
Terrain Generation System
✅ COMPLETE - Chunk-based procedural terrain with Perlin noise, async loading, frustum culling, and dynamic streaming.
Camera Management
✅ COMPLETE - CameraManager with Cinemachine integration, virtual camera control, frustum culling utilities, and spatial queries.
Entity System
✅ COMPLETE - Entity base class with health/damage, EntityManager with spatial queries, animation state system, sprite flipping.
Sprite Rendering System
✅ COMPLETE - Custom URP shader with GPU-based billboarding, mesh-based rendering, JSON sprite atlas system, frame-based animation, MaterialPropertyBlock optimization.
UI Foundation
✅ COMPLETE - UI Toolkit integration, MainMenuManager with event-driven visibility control.
Performance Foundation
✅ COMPLETE - UniTask async operations, background thread mesh generation, frustum culling, chunk streaming optimization, zero-allocation animation updates.
Physics & Movement
🚧 IN PROGRESS - Implement responsive character controller with custom physics tuning for tight, responsive gameplay feel. Physics foundation exists via Entity Rigidbody integration.
Cinemachine Virtual Cameras
🚧 IN PROGRESS - Configure specific Virtual Camera rigs for combat, boss encounters, and cinematic sequences with dynamic blending and procedural behaviors. CameraManager infrastructure complete.
AI Behavior Trees
📋 PLANNED - Develop modular AI system using behavior trees for complex enemy decision-making and state management. EntityManager provides spatial awareness foundation.
Combat System
📋 PLANNED - Create weapon framework with hit detection, damage calculation, and visual effects integration. Entity health system provides foundation.
Audio System
📋 PLANNED - Integrate spatial audio with dynamic mixing, music states, and procedural sound effect layers.
Save System
📋 PLANNED - Implement serialization framework for player progress, settings, and unlockables with cloud backup support.
Advanced Performance
📋 PLANNED - Profile and optimize rendering, physics, and gameplay systems to target 60 FPS on all supported platforms. Current optimizations already in place (culling, async).
Wave System
📋 PLANNED - Implement wave-based enemy spawning with difficulty scaling and progression mechanics.
private)readonly and const where appropriate to prevent accidental mutationsfeature/description, bugfix/issue-number, refactor/system-nameRentEarth’ success depends not only on solid development but also on effective marketing and community outreach. This section outlines our advertising strategy across multiple platforms and details the visual assets required to support these campaigns.
Itch.io is a premier indie game distribution platform with a vibrant community of indie game enthusiasts, developers, and players who actively seek out unique gaming experiences.
Platform Strategy:
action, survival, waves, unity, arcade, singleplayer, insects, combatBest Practices:
Hacker News is a technology-focused community with a strong interest in software development, game engines, and technical achievements. The audience skews toward engineers and technical professionals.
Platform Strategy:
Best Practices:
Craigslist offers localized community-based advertising with surprisingly engaged gaming communities in major metropolitan areas.
Platform Strategy:
Post Structure:
Best Practices:
Reddit’s r/sideproject is a community where creators share their projects, receive feedback, and connect with potential users. The subreddit has over 300k members interested in entrepreneurship, indie development, and creative projects.
Platform Strategy:
Post Structure Best Practices:
Engagement Guidelines:
Timing:
Additional Relevant Subreddits:
Effective game advertising requires a comprehensive suite of visual assets optimized for different platforms and use cases. This section details the infographics and image assets needed to maximize RentEarth’ marketing impact.
1. Hero Banner / Key Art
2. Gameplay Screenshots
3. Animated GIFs
4. Logo & Branding Assets
5. Infographics
Game Features Infographic
Enemy Types Infographic
Development Tech Stack Infographic
6. Social Media Assets
Twitter/X Card
Open Graph Image (Facebook/LinkedIn)
Instagram/Square Format
7. Video Assets
Launch Trailer
Gameplay Deep-Dive Video
Technical Developer Commentary
Press Kit Structure
BugWars_PressKit/├── Images/│ ├── KeyArt/│ │ ├── BugWars_KeyArt_4K.png│ │ └── BugWars_KeyArt_1080p.png│ ├── Screenshots/│ │ ├── BugWars_Screenshot_01.png│ │ └── ... (6-8 total)│ ├── Logos/│ │ ├── BugWars_Logo_Color.png│ │ ├── BugWars_Logo_White.png│ │ └── BugWars_Logo_Black.png│ └── Social/│ ├── TwitterCard_1200x675.png│ ├── OpenGraph_1200x630.png│ └── Square_1080x1080.png├── Videos/│ ├── BugWars_LaunchTrailer_1080p60.mp4│ └── BugWars_LaunchTrailer_4K60.mp4├── GIFs/│ ├── Combat_Loop.gif│ ├── Boss_Encounter.gif│ └── Swarm_Mechanics.gif├── Infographics/│ ├── GameFeatures_Infographic.png│ └── TechStack_Infographic.png└── BugWars_FactSheet.pdfFact Sheet Contents
Tools & Software:
Capture Best Practices:
Optimization Guidelines:
Last Updated: 2025-11-18 - Added comprehensive documentation for R3 (Reactive Extensions) and ObservableCollections, including core concepts, Unity integration, practical code examples, and RentEarth-specific use cases