255 lines
11 KiB
C#
255 lines
11 KiB
C#
#if UNITY_EDITOR
|
|
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine;
|
|
|
|
public static class VerifyCampInteractionsAndPets
|
|
{
|
|
const string ScenePath = "Assets/Bundles/Scenes/Town/HomeCamp/HomeCamp.unity";
|
|
|
|
public static string Run()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("=== Automated Verification: Camp Interactions, Building Nav & Pet Wandering ===");
|
|
bool allPassed = true;
|
|
|
|
var scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Additive);
|
|
try
|
|
{
|
|
var root = scene.GetRootGameObjects().FirstOrDefault(g => g.name == "HomeCamp");
|
|
if (root == null)
|
|
{
|
|
sb.AppendLine("[FAIL] HomeCamp root not found in " + ScenePath);
|
|
return sb.ToString();
|
|
}
|
|
|
|
// Phase 1: Entities Existence, Scale & Component Checks
|
|
sb.AppendLine("\n[Phase 1: Scene Entities, 4x Scale & Component Checks]");
|
|
var explorerTf = root.transform.Find("Explorer");
|
|
if (explorerTf == null)
|
|
{
|
|
sb.AppendLine("[FAIL] Explorer missing in scene");
|
|
allPassed = false;
|
|
}
|
|
else
|
|
{
|
|
var ctrl = explorerTf.GetComponent<HotUpdate.Gameplay.CampExplorerController>();
|
|
bool scale4x = Mathf.Approximately(explorerTf.localScale.x, 4.0f);
|
|
if (scale4x)
|
|
{
|
|
sb.AppendLine($"[PASS] Explorer scaled 4x ({explorerTf.localScale}), pos={explorerTf.position}, Controller={ctrl != null}, Layer={explorerTf.gameObject.layer}");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine($"[FAIL] Explorer scale is {explorerTf.localScale}, expected (4, 4, 4)");
|
|
allPassed = false;
|
|
}
|
|
}
|
|
|
|
var petNames = new[] { "Pet_FireCat", "Pet_RockBall", "Pet_BudBud" };
|
|
foreach (var petName in petNames)
|
|
{
|
|
var petTf = root.transform.Find(petName);
|
|
if (petTf == null)
|
|
{
|
|
sb.AppendLine($"[FAIL] Companion {petName} missing in scene");
|
|
allPassed = false;
|
|
continue;
|
|
}
|
|
|
|
var wanderer = petTf.GetComponent<HotUpdate.Gameplay.CampPetWanderer>();
|
|
var anim = petTf.GetComponentInChildren<Animator>();
|
|
bool hasWanderer = wanderer != null;
|
|
bool hasAnim = anim != null && anim.runtimeAnimatorController != null;
|
|
bool correctLayer = petTf.gameObject.layer == 29;
|
|
bool isEnlarged = petTf.localScale.x >= 2.8f;
|
|
|
|
if (hasWanderer && hasAnim && correctLayer && isEnlarged)
|
|
{
|
|
sb.AppendLine($"[PASS] {petName} scaled 4x ({petTf.localScale}), Controller={anim.runtimeAnimatorController.name}, Layer={petTf.gameObject.layer}, WanderRadius={wanderer.wanderRadius}m");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine($"[FAIL] {petName} check failed: hasWanderer={hasWanderer}, hasAnim={hasAnim}, correctLayer={correctLayer}, isEnlarged={isEnlarged}");
|
|
allPassed = false;
|
|
}
|
|
}
|
|
|
|
// Phase 2: Companion Forward-Walking Facing Verification
|
|
sb.AppendLine("\n[Phase 2: Companion Forward-Walking Facing Verification]");
|
|
var fireCatTf = root.transform.Find("Pet_FireCat");
|
|
if (fireCatTf != null)
|
|
{
|
|
var wanderer = fireCatTf.GetComponent<HotUpdate.Gameplay.CampPetWanderer>();
|
|
var anim = fireCatTf.GetComponentInChildren<Animator>();
|
|
var motionHash = Animator.StringToHash("Motion");
|
|
|
|
// Initial state
|
|
if (!wanderer.IsMoving)
|
|
{
|
|
sb.AppendLine("[PASS] Pet_FireCat initially idle (IsMoving=False)");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine("[FAIL] Pet_FireCat initially moving");
|
|
allPassed = false;
|
|
}
|
|
|
|
// Trigger Wander MoveTo
|
|
Vector3 moveDelta = new Vector3(1.2f, 0, 0.8f);
|
|
Vector3 petTarget = fireCatTf.position + moveDelta;
|
|
wanderer.MoveTo(petTarget);
|
|
|
|
// Rotate towards movement target using wanderer facing convention
|
|
Vector3 lookDir = wanderer.modelFacesMinusZ ? -moveDelta.normalized : moveDelta.normalized;
|
|
fireCatTf.rotation = Quaternion.LookRotation(lookDir, Vector3.up);
|
|
|
|
// Companion front face is local -Z (Vector3.back in model space)
|
|
Vector3 visualFront = fireCatTf.TransformDirection(Vector3.back);
|
|
float facingError = Vector3.Angle(visualFront, moveDelta.normalized);
|
|
|
|
if (wanderer.IsMoving && anim.GetInteger(motionHash) == 1 && facingError < 1.0f)
|
|
{
|
|
sb.AppendLine($"[PASS] Pet Walk Facing verified: Model front face (-Z) aligns with move direction (+{moveDelta.normalized}), facingError={facingError:F2}° (NOT walking backwards)");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine($"[FAIL] Pet Walk Facing incorrect: facingError={facingError:F2}°, IsMoving={wanderer.IsMoving}");
|
|
allPassed = false;
|
|
}
|
|
|
|
// Stop Pet
|
|
wanderer.Stop();
|
|
if (!wanderer.IsMoving && anim.GetInteger(motionHash) == 0)
|
|
{
|
|
sb.AppendLine($"[PASS] Pet Stop triggered: IsMoving=False, Motion parameter = 0 (Idle)");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine($"[FAIL] Pet Stop did not return to Motion=0: IsMoving={wanderer.IsMoving}, Motion={anim.GetInteger(motionHash)}");
|
|
allPassed = false;
|
|
}
|
|
|
|
// Social greeting simulation
|
|
if (explorerTf != null)
|
|
{
|
|
explorerTf.position = fireCatTf.position + new Vector3(1.0f, 0, 0.8f);
|
|
Vector3 toExplorer = explorerTf.position - fireCatTf.position;
|
|
toExplorer.y = 0;
|
|
if (toExplorer.magnitude < wanderer.greetingDistance)
|
|
{
|
|
Vector3 greetDir = wanderer.modelFacesMinusZ ? -toExplorer.normalized : toExplorer.normalized;
|
|
fireCatTf.rotation = Quaternion.LookRotation(greetDir, Vector3.up);
|
|
Vector3 greetFront = fireCatTf.TransformDirection(Vector3.back);
|
|
float greetError = Vector3.Angle(greetFront, toExplorer.normalized);
|
|
if (greetError < 1.0f)
|
|
{
|
|
sb.AppendLine($"[PASS] Social Greeting: Pet front face directly faces Explorer (dist={toExplorer.magnitude:F2}m, greetError={greetError:F2}°)");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine($"[FAIL] Social Greeting face error: {greetError:F2}°");
|
|
allPassed = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Phase 3: Building Interaction & Arrival Callback Simulation
|
|
sb.AppendLine("\n[Phase 3: Building Navigation & Arrival Callback Simulation]");
|
|
if (explorerTf != null)
|
|
{
|
|
var explorerCtrl = explorerTf.GetComponent<HotUpdate.Gameplay.CampExplorerController>();
|
|
bool arrivalCallbackFired = false;
|
|
|
|
Vector3 mainHouseEntrance = new Vector3(0f, 0f, 3.5f);
|
|
Vector3 mainHouseFacing = Vector3.forward;
|
|
|
|
explorerTf.position = new Vector3(0f, 0f, -3.5f);
|
|
explorerCtrl.MoveTo(mainHouseEntrance, () => { arrivalCallbackFired = true; }, mainHouseFacing);
|
|
|
|
if (explorerCtrl.IsMoving)
|
|
{
|
|
sb.AppendLine($"[PASS] MoveTo building entrance initiated: IsMoving=True, Target={explorerCtrl.TargetPosition}");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine("[FAIL] MoveTo building entrance failed to start");
|
|
allPassed = false;
|
|
}
|
|
|
|
// Simulate movement until arrival
|
|
int steps = 0;
|
|
while (explorerCtrl.IsMoving && steps < 150)
|
|
{
|
|
Vector3 toTgt = explorerCtrl.TargetPosition - explorerTf.position;
|
|
toTgt.y = 0;
|
|
float dist = toTgt.magnitude;
|
|
if (dist <= explorerCtrl.stopDistance)
|
|
{
|
|
explorerCtrl.Stop();
|
|
break;
|
|
}
|
|
explorerTf.position += toTgt.normalized * Mathf.Min(explorerCtrl.moveSpeed * 0.05f, dist);
|
|
steps++;
|
|
}
|
|
|
|
float finalDist = Vector2.Distance(new Vector2(explorerTf.position.x, explorerTf.position.z), new Vector2(mainHouseEntrance.x, mainHouseEntrance.z));
|
|
float facingAngleDiff = Vector3.Angle(explorerTf.forward, mainHouseFacing);
|
|
|
|
if (!explorerCtrl.IsMoving && finalDist <= explorerCtrl.stopDistance + 0.05f && arrivalCallbackFired && facingAngleDiff < 5.0f)
|
|
{
|
|
sb.AppendLine($"[PASS] Explorer reached MainHouse entrance in {steps} steps! ArrivalCallbackFired=True, FacingAngleDiff={facingAngleDiff:F1}°, FinalPos={explorerTf.position}");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine($"[FAIL] Building navigation arrival failed: finalDist={finalDist:F3}, callback={arrivalCallbackFired}, facingDiff={facingAngleDiff:F1}°");
|
|
allPassed = false;
|
|
}
|
|
}
|
|
|
|
// Phase 4: Click Indicator Validation
|
|
sb.AppendLine("\n[Phase 4: Click Indicator Validation]");
|
|
if (explorerTf != null)
|
|
{
|
|
var explorerCtrl = explorerTf.GetComponent<HotUpdate.Gameplay.CampExplorerController>();
|
|
explorerCtrl.MoveTo(new Vector3(1.5f, 0, -2.0f));
|
|
var indicator = root.transform.Find("ClickIndicator");
|
|
if (indicator != null && indicator.gameObject.activeSelf)
|
|
{
|
|
sb.AppendLine($"[PASS] ClickIndicator instantiated and active at: {indicator.position}");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine("[FAIL] ClickIndicator not active during movement");
|
|
allPassed = false;
|
|
}
|
|
|
|
explorerCtrl.Stop();
|
|
if (indicator != null && !indicator.gameObject.activeSelf)
|
|
{
|
|
sb.AppendLine("[PASS] ClickIndicator deactivated upon arrival/stop");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine("[FAIL] ClickIndicator failed to deactivate upon stop");
|
|
allPassed = false;
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
EditorSceneManager.CloseScene(scene, true);
|
|
}
|
|
|
|
sb.AppendLine($"\nOverall Result: {(allPassed ? "ALL TESTS PASSED" : "TESTS FAILED")}");
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
#endif
|