This commit is contained in:
Scove
2026-03-26 20:27:19 +07:00
parent a94ab0e3f6
commit f42ef22a13
129 changed files with 5517 additions and 1134 deletions

View File

@@ -0,0 +1,170 @@
using UnityEngine;
using System.Text;
namespace OnlyScove.Scripts.GameSetup
{
[RequireComponent(typeof(CharacterController))]
public class CharacterAutoSetup : MonoBehaviour
{
[Header("Manual Overrides (If Detection Fails)")]
[SerializeField] private float defaultHeight = 1.8f;
[SerializeField] private float defaultShoulderWidth = 0.4f;
[Header("Settings")]
[SerializeField] private Transform modelRoot;
[SerializeField] private bool autoDetectOnStart = true;
[SerializeField] private float zCenterOffset = 0.05f;
private void Start()
{
if (autoDetectOnStart)
{
ApplyAutoSetup();
}
}
[ContextMenu("Apply Auto Setup")]
public void ApplyAutoSetup()
{
CharacterController controller = GetComponent<CharacterController>();
PlayerStateMachine stateMachine = GetComponent<PlayerStateMachine>();
Animator animator = GetComponentInChildren<Animator>();
StringBuilder sb = new StringBuilder();
sb.AppendLine($"<color=#4FC3F7><b>[AUTO-SETUP REPORT]</b></color> Character: <b>{gameObject.name}</b>");
sb.AppendLine("<color=#757575>------------------------------------------------------------</color>");
// 1. HEIGHT DETECTION
float finalHeight = defaultHeight;
string heightMethod = "Default Fallback";
if (animator != null && animator.GetBoneTransform(HumanBodyBones.Head) != null)
{
heightMethod = "Humanoid Bones (Feet to Head)";
Transform head = animator.GetBoneTransform(HumanBodyBones.Head);
// We measure from the local Y=0 (feet) to the head bone and add 10% for the skull/hair
float headHeight = transform.InverseTransformPoint(head.position).y;
finalHeight = headHeight * 1.12f; // 12% extra for the top of the skull
}
else
{
heightMethod = "Local Mesh Bounds";
Bounds localBounds = GetRelativeBounds();
if (localBounds.size.y > 0) finalHeight = localBounds.size.y;
}
sb.AppendLine(string.Format("<b>1. HEIGHT:</b> {0:F3}m ({1}) ➔ <color=#81C784>{2:F3}m</color>", finalHeight, heightMethod, finalHeight));
// 2. RADIUS DETECTION
float shoulderWidth = defaultShoulderWidth;
string radiusMethod = "Default Fallback";
if (animator != null && animator.GetBoneTransform(HumanBodyBones.Hips) != null)
{
radiusMethod = "Humanoid Bones (Shoulders)";
Transform leftArm = animator.GetBoneTransform(HumanBodyBones.LeftUpperArm);
Transform rightArm = animator.GetBoneTransform(HumanBodyBones.RightUpperArm);
if (leftArm != null && rightArm != null)
{
float distance = Vector3.Distance(leftArm.position, rightArm.position);
shoulderWidth = distance * 1.2f; // Add 20% for arm/shoulder thickness
}
}
else
{
radiusMethod = "Bounds Width Fallback";
Bounds b = GetRelativeBounds();
shoulderWidth = b.size.x * 0.25f;
}
float finalRadius = shoulderWidth / 2f;
sb.AppendLine(string.Format("<b>2. RADIUS:</b> {0:F3}m ({1}) [/ 2] ➔ <color=#81C784>{2:F3}m</color>", shoulderWidth, radiusMethod, finalRadius));
// 3. COLLISION PRECISION
float skinWidth = finalRadius * 0.10f;
sb.AppendLine(string.Format("<b>3. SKIN WIDTH:</b> {0:F3}m (Radius) [x 0.10] ➔ <color=#81C784>{1:F3}m</color>", finalRadius, skinWidth));
// 4. CAPSULE CENTER
// Center Y = (Height / 2) + SkinWidth
float centerY = (finalHeight / 2f) + skinWidth;
sb.AppendLine(string.Format("<b>4. CENTER Y:</b> ({0:F3}m / 2) + {1:F3}m (Skin) ➔ <color=#81C784>{2:F3}m</color>", finalHeight, skinWidth, centerY));
sb.AppendLine(string.Format("<b>5. CENTER Z:</b> [Fixed Offset] ➔ <color=#81C784>{0:F3}m</color>", zCenterOffset));
// 5. MOVEMENT CONSTRAINTS
float stepOffset = finalHeight * 0.15f;
sb.AppendLine(string.Format("<b>6. STEP OFFSET:</b> {0:F3}m (Height) [x 0.15] ➔ <color=#81C784>{1:F3}m</color>", finalHeight, stepOffset));
// 6. GROUND CHECK (STATE MACHINE)
sb.AppendLine("<color=#757575>------------------------------------------------------------</color>");
if (stateMachine != null)
{
float groundCheckRadius = finalRadius * 0.6f;
float groundCheckOffsetY = finalHeight / 24f;
Vector3 groundCheckOffset = new Vector3(0, groundCheckOffsetY, zCenterOffset);
stateMachine.SetGroundCheck(groundCheckRadius, groundCheckOffset);
sb.AppendLine("<b>7. GROUND CHECK SETUP:</b>");
sb.AppendLine(string.Format(" - Radius: {0:F3}m (Radius) [x 0.60] ➔ <color=#81C784>{1:F3}m</color>", finalRadius, groundCheckRadius));
sb.AppendLine(string.Format(" - Offset Y: {0:F3}m (Height) [/ 24] ➔ <color=#81C784>{1:F3}m</color>", finalHeight, groundCheckOffsetY));
sb.AppendLine(string.Format(" - Offset Z: [Sync Center Z] ➔ <color=#81C784>{0:F3}m</color>", zCenterOffset));
}
sb.AppendLine("<color=#757575>------------------------------------------------------------</color>");
// Apply to Controller
controller.height = finalHeight;
controller.radius = finalRadius;
controller.skinWidth = skinWidth;
controller.center = new Vector3(0, centerY, zCenterOffset);
controller.slopeLimit = 45f;
controller.stepOffset = stepOffset;
controller.minMoveDistance = 0.001f;
Debug.Log(sb.ToString());
}
private Bounds GetRelativeBounds()
{
Transform targetRoot = modelRoot != null ? modelRoot : transform;
Renderer[] renderers = targetRoot.GetComponentsInChildren<Renderer>();
if (renderers.Length == 0) return new Bounds(Vector3.zero, Vector3.zero);
// Using local bounds of SkinnedMeshRenderers for better accuracy on animated characters
Bounds combinedLocalBounds = new Bounds();
bool first = true;
foreach (Renderer renderer in renderers)
{
if (renderer is ParticleSystemRenderer) continue;
Bounds localB;
if (renderer is SkinnedMeshRenderer smr)
{
localB = smr.localBounds;
}
else
{
// For static meshes, convert world bounds back to local root space
Vector3 min = transform.InverseTransformPoint(renderer.bounds.min);
Vector3 max = transform.InverseTransformPoint(renderer.bounds.max);
localB = new Bounds((min + max) / 2f, max - min);
}
if (first)
{
combinedLocalBounds = localB;
first = false;
}
else
{
combinedLocalBounds.Encapsulate(localB);
}
}
return combinedLocalBounds;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e16a6690e589f0449ad89a6bf508ab62

View File

@@ -0,0 +1,26 @@
using UnityEngine;
namespace OnlyScove.Scripts.GameSetup
{
[CreateAssetMenu(fileName = "CharacterSetupSettings", menuName = "Setup/Character Setup Settings")]
public class CharacterSetupSettings : ScriptableObject
{
[Header("Movement Constraints")]
public float slopeLimit = 45f;
[Range(0.01f, 0.5f)] public float stepHeightRatio = 0.15f; // Step offset as % of height
[Header("Precision & Collision")]
public float skinWidthRatio = 0.1f; // Skin width as % of radius
public float minMoveDistance = 0.001f;
[Header("Dimension Multipliers")]
[Tooltip("Multiplies the detected shoulder width to define Radius.")]
public float radiusMultiplier = 0.8f;
[Tooltip("Multiplies the detected bounding box height.")]
public float heightMultiplier = 1.0f;
[Header("Center Offset")]
[Tooltip("Y-axis offset for the center of the capsule (0.5 means exact middle).")]
public float centerYRatio = 0.5f;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0d44cb4bd45c0e24bb3d8196a137db00