From d1447ce866003d88760d1f1f9910a151cbe6cd08 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 Aug 2025 18:41:08 +0000 Subject: [PATCH 1/4] Initial plan From 693f80977113f36a726513ee50f15487a3b84cac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 Aug 2025 18:47:54 +0000 Subject: [PATCH 2/4] Add runtime API support for characterGO and related properties Co-authored-by: dyfios <16926525+dyfios@users.noreply.github.com> --- .../Character/Scripts/CharacterEntity.cs | 146 +++++++++++++ .../CharacterEntityRuntimeTests.cs | 206 ++++++++++++++++++ .../CharacterEntityRuntimeTests.cs.meta | 11 + 3 files changed, 363 insertions(+) create mode 100644 Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeTests.cs create mode 100644 Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeTests.cs.meta diff --git a/Assets/StraightFour/Entity/Character/Scripts/CharacterEntity.cs b/Assets/StraightFour/Entity/Character/Scripts/CharacterEntity.cs index d8d8251..e676a43 100644 --- a/Assets/StraightFour/Entity/Character/Scripts/CharacterEntity.cs +++ b/Assets/StraightFour/Entity/Character/Scripts/CharacterEntity.cs @@ -72,6 +72,143 @@ public override string entityTag { [Tooltip("Whether or not to fix the height if below ground.")] public bool fixHeight = true; + /// + /// Set the character GameObject at runtime. + /// + /// The new character GameObject to use. + /// Whether or not to synchronize the change. + /// Whether or not the setting was successful. + public bool SetCharacterGO(GameObject newCharacterGO, bool synchronize = true) + { + if (newCharacterGO == null) + { + LogSystem.LogError("[CharacterEntity->SetCharacterGO] Character GameObject cannot be null."); + return false; + } + + // Store old character GameObject + GameObject oldCharacterGO = characterGO; + + // Set the new character GameObject + characterGO = newCharacterGO; + characterGO.SetActive(true); + characterGO.transform.SetParent(transform); + characterGO.transform.localPosition = characterObjectOffset; + characterGO.transform.localRotation = characterObjectRotation; + + // Update meshes for the new character GameObject + List ms = new List(); + foreach (MeshFilter filt in characterGO.GetComponentsInChildren()) + { + ms.Add(filt.sharedMesh); + } + SetRenderers(ms.ToArray()); + + // Calculate new bounds + Bounds bounds = new Bounds(Vector3.zero, Vector3.zero); + foreach (Mesh m in meshes) + { + m.RecalculateBounds(); + bounds.Encapsulate(m.bounds); + } + originalMeshSize = bounds.size; + + // Clean up old character GameObject + if (oldCharacterGO != null) + { + DestroyImmediate(oldCharacterGO); + } + + if (synchronize && synchronizer != null) + { + // Note: Synchronization logic would go here if needed + } + + return true; + } + + /// + /// Set the character object offset at runtime. + /// + /// The new offset to apply. + /// Whether or not to synchronize the change. + /// Whether or not the setting was successful. + public bool SetCharacterObjectOffset(Vector3 newOffset, bool synchronize = true) + { + if (characterGO == null) + { + LogSystem.LogError("[CharacterEntity->SetCharacterObjectOffset] No character GameObject."); + return false; + } + + characterObjectOffset = newOffset; + characterGO.transform.localPosition = characterObjectOffset; + + if (synchronize && synchronizer != null) + { + // Note: Synchronization logic would go here if needed + } + + return true; + } + + /// + /// Set the character object rotation at runtime. + /// + /// The new rotation to apply. + /// Whether or not to synchronize the change. + /// Whether or not the setting was successful. + public bool SetCharacterObjectRotation(Quaternion newRotation, bool synchronize = true) + { + if (characterGO == null) + { + LogSystem.LogError("[CharacterEntity->SetCharacterObjectRotation] No character GameObject."); + return false; + } + + characterObjectRotation = newRotation; + characterGO.transform.localRotation = characterObjectRotation; + + if (synchronize && synchronizer != null) + { + // Note: Synchronization logic would go here if needed + } + + return true; + } + + /// + /// Set the character label offset at runtime. + /// + /// The new offset to apply. + /// Whether or not to synchronize the change. + /// Whether or not the setting was successful. + public bool SetCharacterLabelOffset(Vector3 newOffset, bool synchronize = true) + { + if (characterGO == null) + { + LogSystem.LogError("[CharacterEntity->SetCharacterLabelOffset] No character GameObject."); + return false; + } + + characterLabelOffset = newOffset; + + // Find the character label and update its position + TextMeshProUGUI[] labels = characterGO.GetComponentsInChildren(); + foreach (TextMeshProUGUI label in labels) + { + label.transform.localPosition = characterLabelOffset; + break; // Assume there's only one label per character + } + + if (synchronize && synchronizer != null) + { + // Note: Synchronization logic would go here if needed + } + + return true; + } + /// /// Minimum height to allow character entity to be at. /// @@ -89,6 +226,15 @@ public override string entityTag { /// private GameObject characterGO; + /// + /// Get the character GameObject. + /// + /// The current character GameObject. + public GameObject GetCharacterGO() + { + return characterGO; + } + /// /// Meshes on the character model. /// diff --git a/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeTests.cs b/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeTests.cs new file mode 100644 index 0000000..fd3b9ba --- /dev/null +++ b/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeTests.cs @@ -0,0 +1,206 @@ +// Copyright (c) 2019-2025 Five Squared Interactive. All rights reserved. + +using System.Collections; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using FiveSQD.StraightFour.Entity; +using System; +using FiveSQD.StraightFour; +using FiveSQD.StraightFour.Synchronization; +using UnityEditor; +using TMPro; + +public class CharacterEntityRuntimeTests +{ + [UnityTest] + public IEnumerator CharacterEntityRuntimeTests_SetCharacterObjectOffset() + { + // Initialize World Engine and Load World. + GameObject WEGO = new GameObject(); + StraightFour we = WEGO.AddComponent(); + we.characterControllerPrefab = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Entity/Character/Prefabs/UserAvatar.prefab"); + we.characterControllerLabelPrefab = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Entity/Character/Prefabs/CharacterPrefabLabel.prefab"); + we.skyMaterial = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Environment/Materials/skybox.mat"); + yield return null; + StraightFour.LoadWorld("test"); + + // Create character entity + GameObject go = new GameObject(); + CharacterEntity character = go.AddComponent(); + Guid entityID = Guid.NewGuid(); + + Vector3 initialOffset = new Vector3(1, 2, 3); + character.Initialize(entityID, null, initialOffset, Quaternion.identity, Vector3.zero); + + // Test getting initial offset + Assert.AreEqual(initialOffset, character.characterObjectOffset); + + // Test setting new offset + Vector3 newOffset = new Vector3(4, 5, 6); + bool result = character.SetCharacterObjectOffset(newOffset); + Assert.IsTrue(result); + Assert.AreEqual(newOffset, character.characterObjectOffset); + + // Verify the actual GameObject transform was updated + GameObject characterGO = character.GetCharacterGO(); + Assert.IsNotNull(characterGO); + Assert.AreEqual(newOffset, characterGO.transform.localPosition); + + // Clean up + character.Delete(); + yield return null; + } + + [UnityTest] + public IEnumerator CharacterEntityRuntimeTests_SetCharacterObjectRotation() + { + // Initialize World Engine and Load World. + GameObject WEGO = new GameObject(); + StraightFour we = WEGO.AddComponent(); + we.characterControllerPrefab = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Entity/Character/Prefabs/UserAvatar.prefab"); + we.characterControllerLabelPrefab = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Entity/Character/Prefabs/CharacterPrefabLabel.prefab"); + we.skyMaterial = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Environment/Materials/skybox.mat"); + yield return null; + StraightFour.LoadWorld("test"); + + // Create character entity + GameObject go = new GameObject(); + CharacterEntity character = go.AddComponent(); + Guid entityID = Guid.NewGuid(); + + Quaternion initialRotation = Quaternion.Euler(45, 90, 180); + character.Initialize(entityID, null, Vector3.zero, initialRotation, Vector3.zero); + + // Test getting initial rotation + Assert.AreEqual(initialRotation, character.characterObjectRotation); + + // Test setting new rotation + Quaternion newRotation = Quaternion.Euler(30, 60, 90); + bool result = character.SetCharacterObjectRotation(newRotation); + Assert.IsTrue(result); + Assert.AreEqual(newRotation, character.characterObjectRotation); + + // Verify the actual GameObject transform was updated + GameObject characterGO = character.GetCharacterGO(); + Assert.IsNotNull(characterGO); + Assert.AreEqual(newRotation, characterGO.transform.localRotation); + + // Clean up + character.Delete(); + yield return null; + } + + [UnityTest] + public IEnumerator CharacterEntityRuntimeTests_SetCharacterLabelOffset() + { + // Initialize World Engine and Load World. + GameObject WEGO = new GameObject(); + StraightFour we = WEGO.AddComponent(); + we.characterControllerPrefab = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Entity/Character/Prefabs/UserAvatar.prefab"); + we.characterControllerLabelPrefab = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Entity/Character/Prefabs/CharacterPrefabLabel.prefab"); + we.skyMaterial = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Environment/Materials/skybox.mat"); + yield return null; + StraightFour.LoadWorld("test"); + + // Create character entity + GameObject go = new GameObject(); + CharacterEntity character = go.AddComponent(); + Guid entityID = Guid.NewGuid(); + + Vector3 initialLabelOffset = new Vector3(0, 2, 0); + character.Initialize(entityID, null, Vector3.zero, Quaternion.identity, initialLabelOffset); + + // Test getting initial label offset + Assert.AreEqual(initialLabelOffset, character.characterLabelOffset); + + // Test setting new label offset + Vector3 newLabelOffset = new Vector3(1, 3, 1); + bool result = character.SetCharacterLabelOffset(newLabelOffset); + Assert.IsTrue(result); + Assert.AreEqual(newLabelOffset, character.characterLabelOffset); + + // Verify the actual label transform was updated + GameObject characterGO = character.GetCharacterGO(); + Assert.IsNotNull(characterGO); + TextMeshProUGUI[] labels = characterGO.GetComponentsInChildren(); + if (labels.Length > 0) + { + Assert.AreEqual(newLabelOffset, labels[0].transform.localPosition); + } + + // Clean up + character.Delete(); + yield return null; + } + + [UnityTest] + public IEnumerator CharacterEntityRuntimeTests_SetCharacterGO() + { + // Initialize World Engine and Load World. + GameObject WEGO = new GameObject(); + StraightFour we = WEGO.AddComponent(); + we.characterControllerPrefab = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Entity/Character/Prefabs/UserAvatar.prefab"); + we.characterControllerLabelPrefab = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Entity/Character/Prefabs/CharacterPrefabLabel.prefab"); + we.skyMaterial = AssetDatabase.LoadAssetAtPath("Assets/StraightFour/Environment/Materials/skybox.mat"); + yield return null; + StraightFour.LoadWorld("test"); + + // Create character entity + GameObject go = new GameObject(); + CharacterEntity character = go.AddComponent(); + Guid entityID = Guid.NewGuid(); + + character.Initialize(entityID, null, Vector3.zero, Quaternion.identity, Vector3.zero); + + // Get initial character GameObject + GameObject initialCharacterGO = character.GetCharacterGO(); + Assert.IsNotNull(initialCharacterGO); + + // Create a new character GameObject (simple cube for testing) + GameObject newCharacterGO = GameObject.CreatePrimitive(PrimitiveType.Cube); + + // Test setting new character GameObject + bool result = character.SetCharacterGO(newCharacterGO); + Assert.IsTrue(result); + + // Verify the character GameObject was updated + GameObject currentCharacterGO = character.GetCharacterGO(); + Assert.AreEqual(newCharacterGO, currentCharacterGO); + Assert.AreEqual(character.transform, currentCharacterGO.transform.parent); + + // Test error handling with null GameObject + bool nullResult = character.SetCharacterGO(null); + Assert.IsFalse(nullResult); + + // Clean up + character.Delete(); + yield return null; + } + + [UnityTest] + public IEnumerator CharacterEntityRuntimeTests_ErrorHandling() + { + // Test error handling when character is not properly initialized + GameObject go = new GameObject(); + CharacterEntity character = go.AddComponent(); + + // These should all fail gracefully since no characterGO exists + bool offsetResult = character.SetCharacterObjectOffset(Vector3.one); + Assert.IsFalse(offsetResult); + + bool rotationResult = character.SetCharacterObjectRotation(Quaternion.identity); + Assert.IsFalse(rotationResult); + + bool labelOffsetResult = character.SetCharacterLabelOffset(Vector3.one); + Assert.IsFalse(labelOffsetResult); + + // GetCharacterGO should return null + GameObject characterGO = character.GetCharacterGO(); + Assert.IsNull(characterGO); + + // Clean up + character.Delete(); + yield return null; + } +} \ No newline at end of file diff --git a/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeTests.cs.meta b/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeTests.cs.meta new file mode 100644 index 0000000..1327db8 --- /dev/null +++ b/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7e8a9b0c1d2f3e4a5b6c7d8e9f0a1b2c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: \ No newline at end of file From 5ef317fdf475d1a73c71e289dbf593f80cea0022 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 Aug 2025 18:50:29 +0000 Subject: [PATCH 3/4] Add demonstration script and complete runtime API implementation Co-authored-by: dyfios <16926525+dyfios@users.noreply.github.com> --- .../EntityTests/CharacterEntityRuntimeDemo.cs | 110 ++++++++++++++++++ .../CharacterEntityRuntimeDemo.cs.meta | 11 ++ 2 files changed, 121 insertions(+) create mode 100644 Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeDemo.cs create mode 100644 Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeDemo.cs.meta diff --git a/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeDemo.cs b/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeDemo.cs new file mode 100644 index 0000000..3181322 --- /dev/null +++ b/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeDemo.cs @@ -0,0 +1,110 @@ +// Copyright (c) 2019-2025 Five Squared Interactive. All rights reserved. + +using UnityEngine; +using FiveSQD.StraightFour.Entity; +using System; + +/// +/// Example script demonstrating the new runtime APIs for CharacterEntity +/// +public class CharacterEntityRuntimeDemo : MonoBehaviour +{ + [Header("Character Entity Runtime API Demo")] + public CharacterEntity characterEntity; + + [Header("Test Values")] + public Vector3 newOffset = new Vector3(0, 1, 0); + public Vector3 newRotationEuler = new Vector3(0, 45, 0); + public Vector3 newLabelOffset = new Vector3(0, 2, 0); + public GameObject newCharacterPrefab; + + [Header("Controls")] + [Space] + public bool updateOffset; + public bool updateRotation; + public bool updateLabelOffset; + public bool updateCharacterGO; + + void Update() + { + if (characterEntity == null) return; + + // Update character object offset + if (updateOffset) + { + updateOffset = false; + bool success = characterEntity.SetCharacterObjectOffset(newOffset); + Debug.Log($"Set character object offset to {newOffset}: {(success ? "Success" : "Failed")}"); + } + + // Update character object rotation + if (updateRotation) + { + updateRotation = false; + Quaternion newRotation = Quaternion.Euler(newRotationEuler); + bool success = characterEntity.SetCharacterObjectRotation(newRotation); + Debug.Log($"Set character object rotation to {newRotationEuler}: {(success ? "Success" : "Failed")}"); + } + + // Update character label offset + if (updateLabelOffset) + { + updateLabelOffset = false; + bool success = characterEntity.SetCharacterLabelOffset(newLabelOffset); + Debug.Log($"Set character label offset to {newLabelOffset}: {(success ? "Success" : "Failed")}"); + } + + // Update character GameObject + if (updateCharacterGO && newCharacterPrefab != null) + { + updateCharacterGO = false; + bool success = characterEntity.SetCharacterGO(newCharacterPrefab); + Debug.Log($"Set character GameObject: {(success ? "Success" : "Failed")}"); + } + } + + [ContextMenu("Demo - Update All Properties")] + public void DemoUpdateAllProperties() + { + if (characterEntity == null) + { + Debug.LogError("No CharacterEntity assigned!"); + return; + } + + // Get current values + Vector3 currentOffset = characterEntity.characterObjectOffset; + Quaternion currentRotation = characterEntity.characterObjectRotation; + Vector3 currentLabelOffset = characterEntity.characterLabelOffset; + GameObject currentGO = characterEntity.GetCharacterGO(); + + Debug.Log("=== CharacterEntity Runtime API Demo ==="); + Debug.Log($"Current Offset: {currentOffset}"); + Debug.Log($"Current Rotation: {currentRotation.eulerAngles}"); + Debug.Log($"Current Label Offset: {currentLabelOffset}"); + Debug.Log($"Current GameObject: {(currentGO != null ? currentGO.name : "null")}"); + + // Update offset + Vector3 testOffset = currentOffset + Vector3.up; + if (characterEntity.SetCharacterObjectOffset(testOffset)) + { + Debug.Log($"✓ Successfully updated offset to {testOffset}"); + } + + // Update rotation + Quaternion testRotation = currentRotation * Quaternion.Euler(0, 45, 0); + if (characterEntity.SetCharacterObjectRotation(testRotation)) + { + Debug.Log($"✓ Successfully updated rotation to {testRotation.eulerAngles}"); + } + + // Update label offset + Vector3 testLabelOffset = currentLabelOffset + Vector3.forward; + if (characterEntity.SetCharacterLabelOffset(testLabelOffset)) + { + Debug.Log($"✓ Successfully updated label offset to {testLabelOffset}"); + } + + Debug.Log("=== Demo Complete ==="); + } +} \ No newline at end of file diff --git a/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeDemo.cs.meta b/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeDemo.cs.meta new file mode 100644 index 0000000..1f7aa00 --- /dev/null +++ b/Assets/StraightFour/Testing/EntityTests/CharacterEntityRuntimeDemo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: \ No newline at end of file From e6ffbb6a1daf2799335fa36715356311080f4cde Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 Aug 2025 18:52:11 +0000 Subject: [PATCH 4/4] Add comprehensive documentation for CharacterEntity runtime APIs Co-authored-by: dyfios <16926525+dyfios@users.noreply.github.com> --- ...haracterEntity_RuntimeAPI_Documentation.md | 189 ++++++++++++++++++ ...terEntity_RuntimeAPI_Documentation.md.meta | 7 + 2 files changed, 196 insertions(+) create mode 100644 Assets/StraightFour/Testing/EntityTests/CharacterEntity_RuntimeAPI_Documentation.md create mode 100644 Assets/StraightFour/Testing/EntityTests/CharacterEntity_RuntimeAPI_Documentation.md.meta diff --git a/Assets/StraightFour/Testing/EntityTests/CharacterEntity_RuntimeAPI_Documentation.md b/Assets/StraightFour/Testing/EntityTests/CharacterEntity_RuntimeAPI_Documentation.md new file mode 100644 index 0000000..e8501e5 --- /dev/null +++ b/Assets/StraightFour/Testing/EntityTests/CharacterEntity_RuntimeAPI_Documentation.md @@ -0,0 +1,189 @@ +# CharacterEntity Runtime API Documentation + +This document describes the new runtime APIs added to the `CharacterEntity` class that allow dynamic updates to character properties during gameplay. + +## Overview + +Previously, the properties `characterGO`, `characterObjectOffset`, `characterObjectRotation`, and `characterLabelOffset` could only be set during entity initialization. The new runtime APIs allow these properties to be modified at any time during execution, enabling dynamic character management. + +## New APIs + +### 1. SetCharacterGO(GameObject newCharacterGO, bool synchronize = true) + +**Purpose**: Replace the character GameObject at runtime with a new one. + +**Parameters**: +- `newCharacterGO`: The new character GameObject to use (cannot be null) +- `synchronize`: Whether to synchronize the change (default: true) + +**Returns**: `bool` - Whether the operation was successful + +**Example**: +```csharp +GameObject newCharacter = Resources.Load("NewCharacterPrefab"); +bool success = characterEntity.SetCharacterGO(newCharacter); +if (success) +{ + Debug.Log("Character GameObject updated successfully!"); +} +``` + +**Notes**: +- Automatically updates mesh renderers and bounds calculations +- Cleans up the old character GameObject +- Preserves current offset and rotation settings + +### 2. GetCharacterGO() + +**Purpose**: Get the current character GameObject. + +**Returns**: `GameObject` - The current character GameObject (can be null) + +**Example**: +```csharp +GameObject currentCharacter = characterEntity.GetCharacterGO(); +if (currentCharacter != null) +{ + Debug.Log($"Current character: {currentCharacter.name}"); +} +``` + +### 3. SetCharacterObjectOffset(Vector3 newOffset, bool synchronize = true) + +**Purpose**: Update the character object's position offset at runtime. + +**Parameters**: +- `newOffset`: The new position offset to apply +- `synchronize`: Whether to synchronize the change (default: true) + +**Returns**: `bool` - Whether the operation was successful + +**Example**: +```csharp +// Move character slightly up +Vector3 newOffset = new Vector3(0, 0.5f, 0); +bool success = characterEntity.SetCharacterObjectOffset(newOffset); +``` + +**Notes**: +- Immediately updates the character GameObject's local position +- Updates the internal `characterObjectOffset` property + +### 4. SetCharacterObjectRotation(Quaternion newRotation, bool synchronize = true) + +**Purpose**: Update the character object's rotation at runtime. + +**Parameters**: +- `newRotation`: The new rotation to apply +- `synchronize`: Whether to synchronize the change (default: true) + +**Returns**: `bool` - Whether the operation was successful + +**Example**: +```csharp +// Rotate character 45 degrees around Y axis +Quaternion newRotation = Quaternion.Euler(0, 45, 0); +bool success = characterEntity.SetCharacterObjectRotation(newRotation); +``` + +**Notes**: +- Immediately updates the character GameObject's local rotation +- Updates the internal `characterObjectRotation` property + +### 5. SetCharacterLabelOffset(Vector3 newOffset, bool synchronize = true) + +**Purpose**: Update the character label's position offset at runtime. + +**Parameters**: +- `newOffset`: The new label position offset to apply +- `synchronize`: Whether to synchronize the change (default: true) + +**Returns**: `bool` - Whether the operation was successful + +**Example**: +```csharp +// Move label higher above character +Vector3 newLabelOffset = new Vector3(0, 2.5f, 0); +bool success = characterEntity.SetCharacterLabelOffset(newLabelOffset); +``` + +**Notes**: +- Immediately updates the character label's local position +- Updates the internal `characterLabelOffset` property +- Finds TextMeshProUGUI components in character hierarchy + +## Error Handling + +All setter methods include proper error handling: + +- Return `false` if the operation fails +- Log appropriate error messages to the console +- Check for null character GameObject before operations +- Validate input parameters + +**Common Error Cases**: +- Calling methods before character is properly initialized +- Passing null GameObject to `SetCharacterGO()` +- Character GameObject has been destroyed + +## Backward Compatibility + +These new APIs are fully backward compatible: + +- Existing code continues to work unchanged +- Original initialization methods remain functional +- No breaking changes to existing interfaces +- Properties maintain their original getter behavior + +## Usage Examples + +### Dynamic Character Customization +```csharp +public class CharacterCustomizer : MonoBehaviour +{ + public CharacterEntity character; + public GameObject[] characterVariants; + + public void SwitchCharacterVariant(int index) + { + if (index < characterVariants.Length) + { + character.SetCharacterGO(characterVariants[index]); + } + } + + public void AdjustCharacterHeight(float heightOffset) + { + Vector3 currentOffset = character.characterObjectOffset; + currentOffset.y = heightOffset; + character.SetCharacterObjectOffset(currentOffset); + } +} +``` + +### Animation-Driven Updates +```csharp +public class CharacterAnimationController : MonoBehaviour +{ + public CharacterEntity character; + + void Update() + { + // Gradually rotate character + Quaternion currentRotation = character.characterObjectRotation; + Quaternion newRotation = currentRotation * Quaternion.Euler(0, Time.deltaTime * 90, 0); + character.SetCharacterObjectRotation(newRotation); + } +} +``` + +## Testing + +Comprehensive tests are provided in `CharacterEntityRuntimeTests.cs` that validate: + +- All new API methods function correctly +- Error handling works as expected +- GameObject transforms are updated properly +- Edge cases are handled gracefully + +A demonstration script `CharacterEntityRuntimeDemo.cs` is also provided showing practical usage examples. \ No newline at end of file diff --git a/Assets/StraightFour/Testing/EntityTests/CharacterEntity_RuntimeAPI_Documentation.md.meta b/Assets/StraightFour/Testing/EntityTests/CharacterEntity_RuntimeAPI_Documentation.md.meta new file mode 100644 index 0000000..bbf5a99 --- /dev/null +++ b/Assets/StraightFour/Testing/EntityTests/CharacterEntity_RuntimeAPI_Documentation.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: \ No newline at end of file