From ba2588521d48a22d6230adfdcc25faefaba389a6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:41:20 +0000 Subject: [PATCH 1/4] Initial plan From d1ff6cf8197293598f0c24f3561ec224f842cd00 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:44:40 +0000 Subject: [PATCH 2/4] Add world offset auto-update feature for character entities Co-authored-by: dyfios <16926525+dyfios@users.noreply.github.com> --- .../Testing/WorldTests/WorldTests.cs | 65 +++++++++++++++++++ Assets/StraightFour/World.cs | 54 +++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/Assets/StraightFour/Testing/WorldTests/WorldTests.cs b/Assets/StraightFour/Testing/WorldTests/WorldTests.cs index 19f3198..2be0b9c 100644 --- a/Assets/StraightFour/Testing/WorldTests/WorldTests.cs +++ b/Assets/StraightFour/Testing/WorldTests/WorldTests.cs @@ -55,4 +55,69 @@ public IEnumerator WorldTests_World() Assert.IsTrue(world.cameraManager == null); Assert.IsTrue(world.materialManager == null); } + + [UnityTest] + public IEnumerator WorldTests_WorldOffsetUpdate() + { + // Initialize World Engine and Load World. + GameObject WEGO = new GameObject(); + StraightFour we = WEGO.AddComponent(); + we.skyMaterial = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Environment/Materials/Skybox.mat"); + we.liteProceduralSkyMaterial = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Environment/Materials/LiteProceduralSkybox.mat"); + yield return null; + StraightFour.LoadWorld("test"); + + // Create a character entity + GameObject characterGO = new GameObject("TestCharacter"); + CharacterEntity character = characterGO.AddComponent(); + character.Initialize(System.Guid.NewGuid(), null, Vector3.zero, Quaternion.identity, Vector3.zero); + character.SetInteractionState(FiveSQD.StraightFour.Entity.BaseEntity.InteractionState.Physical); + + // Set the tracked character + StraightFour.ActiveWorld.SetTrackedCharacterEntity(character); + StraightFour.ActiveWorld.enableAutoWorldOffsetUpdate = true; + StraightFour.ActiveWorld.worldOffsetUpdateThreshold = 100f; + + // Verify initial offset is zero + Assert.AreEqual(Vector3.zero, StraightFour.ActiveWorld.worldOffset); + + // Move character beyond threshold + character.SetPosition(new Vector3(150, 0, 0), false, false); + + // Wait a frame for Update to run + yield return null; + + // Verify world offset was updated + Vector3 expectedOffset = new Vector3(150, 0, 0); + Assert.AreEqual(expectedOffset, StraightFour.ActiveWorld.worldOffset); + + // Move character closer to new origin (within threshold) + character.SetPosition(new Vector3(160, 0, 10), false, false); + + // Wait a frame for Update to run + yield return null; + + // Offset should remain the same since distance from origin is still within threshold + Assert.AreEqual(expectedOffset, StraightFour.ActiveWorld.worldOffset); + + // Move character far again + character.SetPosition(new Vector3(300, 0, 0), false, false); + + // Wait a frame for Update to run + yield return null; + + // Verify offset updated again + expectedOffset = new Vector3(300, 0, 0); + Assert.AreEqual(expectedOffset, StraightFour.ActiveWorld.worldOffset); + + // Test disabling auto-update + StraightFour.ActiveWorld.enableAutoWorldOffsetUpdate = false; + character.SetPosition(new Vector3(500, 0, 0), false, false); + + // Wait a frame for Update to run + yield return null; + + // Offset should NOT have changed + Assert.AreEqual(new Vector3(300, 0, 0), StraightFour.ActiveWorld.worldOffset); + } } \ No newline at end of file diff --git a/Assets/StraightFour/World.cs b/Assets/StraightFour/World.cs index 9088901..c0cdbaf 100644 --- a/Assets/StraightFour/World.cs +++ b/Assets/StraightFour/World.cs @@ -223,6 +223,23 @@ public Vector3 worldOffset private Vector3 _worldOffset; + /// + /// The character entity being tracked for world offset updates. + /// + private CharacterEntity trackedCharacterEntity; + + /// + /// The distance threshold from origin that triggers a world offset update. + /// + [Tooltip("The distance threshold from origin that triggers a world offset update.")] + public float worldOffsetUpdateThreshold = 1000f; + + /// + /// Whether or not to enable automatic world offset updates. + /// + [Tooltip("Whether or not to enable automatic world offset updates.")] + public bool enableAutoWorldOffsetUpdate = true; + /// /// The GameObject for the mesh manager. /// @@ -371,6 +388,43 @@ public void Initialize(WorldInfo worldInfo) siteName = worldInfo.siteName; } + /// + /// Set the character entity to track for automatic world offset updates. + /// + /// The character entity to track. + public void SetTrackedCharacterEntity(CharacterEntity entity) + { + trackedCharacterEntity = entity; + } + + /// + /// Update method to check tracked character distance and update world offset if needed. + /// + void Update() + { + if (!enableAutoWorldOffsetUpdate || trackedCharacterEntity == null) + { + return; + } + + // Get the current position of the tracked character entity (world-space, not local) + Vector3 characterPosition = trackedCharacterEntity.GetPosition(false); + + // Calculate distance from origin + float distanceFromOrigin = characterPosition.magnitude; + + // If beyond threshold, update world offset to recenter around character + if (distanceFromOrigin > worldOffsetUpdateThreshold) + { + // Calculate new offset to recenter the world around the character + // We want to move the world offset to the character's position + Vector3 newOffset = new Vector3(characterPosition.x, worldOffset.y, characterPosition.z); + + // Update the world offset + worldOffset = newOffset; + } + } + /// /// Unload the world. /// From 0cd9c3202158e9d6dce996241a1f614147be905d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:47:31 +0000 Subject: [PATCH 3/4] Fix world offset calculation to properly recenter Unity coordinates Co-authored-by: dyfios <16926525+dyfios@users.noreply.github.com> --- .../Testing/WorldTests/WorldTests.cs | 42 +++++++++++++++---- Assets/StraightFour/World.cs | 19 +++++---- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/Assets/StraightFour/Testing/WorldTests/WorldTests.cs b/Assets/StraightFour/Testing/WorldTests/WorldTests.cs index 2be0b9c..d0ee8a1 100644 --- a/Assets/StraightFour/Testing/WorldTests/WorldTests.cs +++ b/Assets/StraightFour/Testing/WorldTests/WorldTests.cs @@ -81,43 +81,67 @@ public IEnumerator WorldTests_WorldOffsetUpdate() // Verify initial offset is zero Assert.AreEqual(Vector3.zero, StraightFour.ActiveWorld.worldOffset); - // Move character beyond threshold + // Move character beyond threshold (logical position) character.SetPosition(new Vector3(150, 0, 0), false, false); + // Unity position should be (150, 0, 0) with offset (0, 0, 0) + Assert.AreEqual(new Vector3(150, 0, 0), character.transform.position); + // Wait a frame for Update to run yield return null; - // Verify world offset was updated - Vector3 expectedOffset = new Vector3(150, 0, 0); + // Verify world offset was updated to recenter Unity position at origin + // New offset should be (0, 0, 0) - (150, 0, 0) = (-150, 0, 0) + Vector3 expectedOffset = new Vector3(-150, 0, 0); Assert.AreEqual(expectedOffset, StraightFour.ActiveWorld.worldOffset); + + // Character should now be at Unity origin while maintaining logical position + Assert.AreEqual(Vector3.zero, character.transform.position); + Assert.AreEqual(new Vector3(150, 0, 0), character.GetPosition(false)); - // Move character closer to new origin (within threshold) + // Move character to logical (160, 0, 10) - still close to Unity origin character.SetPosition(new Vector3(160, 0, 10), false, false); + // Unity position should be (160, 0, 10) + (-150, 0, 0) = (10, 0, 10) + Assert.AreEqual(new Vector3(10, 0, 10), character.transform.position); + // Wait a frame for Update to run yield return null; - // Offset should remain the same since distance from origin is still within threshold + // Offset should remain the same since Unity distance from origin is small Assert.AreEqual(expectedOffset, StraightFour.ActiveWorld.worldOffset); - // Move character far again + // Move character far again to logical (300, 0, 0) character.SetPosition(new Vector3(300, 0, 0), false, false); + // Unity position should be (300, 0, 0) + (-150, 0, 0) = (150, 0, 0) + Assert.AreEqual(new Vector3(150, 0, 0), character.transform.position); + // Wait a frame for Update to run yield return null; - // Verify offset updated again - expectedOffset = new Vector3(300, 0, 0); + // Verify offset updated again: new offset = (-150, 0, 0) - (150, 0, 0) = (-300, 0, 0) + expectedOffset = new Vector3(-300, 0, 0); Assert.AreEqual(expectedOffset, StraightFour.ActiveWorld.worldOffset); + + // Character should be back at Unity origin + Assert.AreEqual(Vector3.zero, character.transform.position); + Assert.AreEqual(new Vector3(300, 0, 0), character.GetPosition(false)); // Test disabling auto-update StraightFour.ActiveWorld.enableAutoWorldOffsetUpdate = false; character.SetPosition(new Vector3(500, 0, 0), false, false); + // Unity position should be (500, 0, 0) + (-300, 0, 0) = (200, 0, 0) + Assert.AreEqual(new Vector3(200, 0, 0), character.transform.position); + // Wait a frame for Update to run yield return null; // Offset should NOT have changed - Assert.AreEqual(new Vector3(300, 0, 0), StraightFour.ActiveWorld.worldOffset); + Assert.AreEqual(new Vector3(-300, 0, 0), StraightFour.ActiveWorld.worldOffset); + + // Character should still be at Unity (200, 0, 0) + Assert.AreEqual(new Vector3(200, 0, 0), character.transform.position); } } \ No newline at end of file diff --git a/Assets/StraightFour/World.cs b/Assets/StraightFour/World.cs index c0cdbaf..1191518 100644 --- a/Assets/StraightFour/World.cs +++ b/Assets/StraightFour/World.cs @@ -407,18 +407,23 @@ void Update() return; } - // Get the current position of the tracked character entity (world-space, not local) - Vector3 characterPosition = trackedCharacterEntity.GetPosition(false); + // Get the Unity position of the tracked character entity + Vector3 unityPosition = trackedCharacterEntity.transform.position; - // Calculate distance from origin - float distanceFromOrigin = characterPosition.magnitude; + // Calculate distance from Unity origin (0, 0, 0) + float distanceFromOrigin = unityPosition.magnitude; // If beyond threshold, update world offset to recenter around character if (distanceFromOrigin > worldOffsetUpdateThreshold) { - // Calculate new offset to recenter the world around the character - // We want to move the world offset to the character's position - Vector3 newOffset = new Vector3(characterPosition.x, worldOffset.y, characterPosition.z); + // Calculate new offset to recenter the Unity origin at the character + // We want Unity position to be (0, 0, 0) while preserving logical position + // Unity = Logical + offset + // Target: Unity' = (0, 0, 0), Logical' = Logical (preserved by setter) + // We have: Logical = Unity - offset + // So: Unity' = Logical + offset' = (Unity - offset) + offset' = (0, 0, 0) + // Therefore: offset' = offset - Unity + Vector3 newOffset = new Vector3(worldOffset.x - unityPosition.x, worldOffset.y, worldOffset.z - unityPosition.z); // Update the world offset worldOffset = newOffset; From c2b3f7e154c7b8995b50db6c2670c80df7e243fc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:48:56 +0000 Subject: [PATCH 4/4] Add documentation for world offset auto-update feature Co-authored-by: dyfios <16926525+dyfios@users.noreply.github.com> --- docs/configuration/README.md | 52 ++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index f700037..6942287 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -230,6 +230,58 @@ WorldInfo worldInfo = new WorldInfo bool success = StraightFour.LoadWorld("MyWorld", worldInfo); ``` +### World Offset Auto-Update + +The World class supports automatic world offset updates to maintain floating-point precision as characters move far from the Unity origin. This feature helps prevent precision-related issues and ensures smooth gameplay regardless of how far entities travel in the world. + +#### Configuration + +```csharp +// Enable automatic world offset updates +StraightFour.ActiveWorld.enableAutoWorldOffsetUpdate = true; + +// Set the distance threshold (in Unity units) that triggers an offset update +StraightFour.ActiveWorld.worldOffsetUpdateThreshold = 1000f; + +// Register a character entity to track +CharacterEntity playerCharacter = // ... your player character +StraightFour.ActiveWorld.SetTrackedCharacterEntity(playerCharacter); +``` + +#### How It Works + +1. **Distance Monitoring**: The World's Update() method continuously monitors the tracked character's Unity position (transform.position) +2. **Threshold Check**: When the character's distance from the Unity origin (0, 0, 0) exceeds the threshold, an update is triggered +3. **Offset Update**: The world offset is adjusted to recenter the Unity coordinate system at the character's location +4. **Position Preservation**: All entities' logical (world) positions are preserved while their Unity positions are shifted to stay near the origin + +**Example:** +- Character moves to Unity position (1500, 0, 0) with current offset (0, 0, 0) +- Distance 1500 exceeds threshold of 1000, triggering an update +- New offset is set to (-1500, 0, 0) +- Character's Unity position becomes (0, 0, 0) while maintaining logical position (1500, 0, 0) + +#### Benefits + +- **Improved Precision**: Keeps Unity transform positions close to origin, avoiding floating-point precision loss +- **Seamless Experience**: Characters can travel unlimited distances without precision degradation +- **Automatic**: No manual intervention needed once configured +- **Configurable**: Threshold can be adjusted based on world scale and precision requirements + +#### Default Values + +```csharp +public float worldOffsetUpdateThreshold = 1000f; // Unity units +public bool enableAutoWorldOffsetUpdate = true; // Enabled by default +``` + +#### Notes + +- Only horizontal (X, Z) axes are recentered; Y-axis offset remains unchanged +- The feature can be disabled by setting `enableAutoWorldOffsetUpdate = false` +- The tracked character entity can be changed at runtime using `SetTrackedCharacterEntity()` +- If no character is tracked, automatic updates will not occur + ## Unity Project Configuration ### Package Dependencies