ui基本完毕,修了一大把的bug

This commit is contained in:
FloatGaming
2026-07-13 02:28:39 +08:00
parent 1e20d73e90
commit fd22501f71
958 changed files with 378289 additions and 41038 deletions
@@ -0,0 +1,177 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Explosion3DTestController : MonoBehaviour
{
[Header("Trigger")]
[SerializeField] private bool enableDebugKey = true;
[SerializeField] private KeyCode triggerKey = KeyCode.Alpha0;
[Header("Target")]
[SerializeField] private GameObject intactObject;
[SerializeField] private GameObject fracturedPrefab;
[SerializeField] private Transform spawnRoot;
[SerializeField] private bool disableIntactObjectOnExplode = true;
[SerializeField] private bool destroyPreviousFractureInstance = true;
[Header("Explosion Force")]
[SerializeField] private float explosionForce = 2.5f;
[SerializeField] private float explosionRadius = 1.25f;
[SerializeField] private float upwardModifier = 0.15f;
[SerializeField] private float randomForceMultiplier = 0.2f;
[SerializeField] private float randomTorque = 8f;
[Header("Containment")]
[SerializeField] private bool keepFragmentsClose = true;
[SerializeField] private float maxFragmentSpeed = 1.2f;
[SerializeField] private float fragmentDrag = 4f;
[SerializeField] private float fragmentAngularDrag = 6f;
[SerializeField] private bool disableFragmentGravity = false;
[Header("Lifecycle")]
[SerializeField] private bool autoCleanupFragments = false;
[SerializeField] private float cleanupDelay = 5f;
private GameObject spawnedFractureRoot;
private bool exploded;
private void Update()
{
if (!enableDebugKey)
return;
if (Input.GetKeyDown(triggerKey))
{
TriggerExplosion();
}
}
[ContextMenu("Trigger Explosion")]
public void TriggerExplosion()
{
if (exploded)
return;
if (fracturedPrefab == null)
{
Debug.LogWarning("[Explosion3DTest] Missing fracturedPrefab.");
return;
}
Transform sourceTransform = intactObject != null ? intactObject.transform : transform;
Transform parent = spawnRoot != null ? spawnRoot : sourceTransform.parent;
if (destroyPreviousFractureInstance && spawnedFractureRoot != null)
{
Destroy(spawnedFractureRoot);
spawnedFractureRoot = null;
}
spawnedFractureRoot = Instantiate(
fracturedPrefab,
sourceTransform.position,
sourceTransform.rotation,
parent);
spawnedFractureRoot.name = fracturedPrefab.name + "_Exploded";
if (disableIntactObjectOnExplode && intactObject != null)
{
intactObject.SetActive(false);
}
ApplyExplosionToFragments(spawnedFractureRoot, sourceTransform.position);
exploded = true;
if (autoCleanupFragments && cleanupDelay > 0f)
{
StartCoroutine(CleanupAfterDelay(cleanupDelay));
}
}
[ContextMenu("Reset Explosion")]
public void ResetExplosion()
{
exploded = false;
if (spawnedFractureRoot != null)
{
Destroy(spawnedFractureRoot);
spawnedFractureRoot = null;
}
if (intactObject != null)
{
intactObject.SetActive(true);
}
}
private void ApplyExplosionToFragments(GameObject fractureRoot, Vector3 explosionCenter)
{
if (fractureRoot == null)
return;
Rigidbody[] rigidbodies = fractureRoot.GetComponentsInChildren<Rigidbody>(true);
for (int i = 0; i < rigidbodies.Length; i++)
{
Rigidbody rb = rigidbodies[i];
if (rb == null)
continue;
rb.isKinematic = false;
rb.useGravity = !disableFragmentGravity;
rb.linearDamping = fragmentDrag;
rb.angularDamping = fragmentAngularDrag;
Vector3 fragmentCenter = rb.worldCenterOfMass;
Vector3 direction = fragmentCenter - explosionCenter;
if (direction.sqrMagnitude < 0.0001f)
{
direction = Random.onUnitSphere;
}
float randomScale = 1f + Random.Range(-randomForceMultiplier, randomForceMultiplier);
Vector3 force = direction.normalized * Mathf.Max(0f, explosionForce * randomScale);
force += Vector3.up * upwardModifier;
rb.linearVelocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
rb.AddForce(force, ForceMode.Impulse);
if (randomTorque > 0f)
{
Vector3 torqueAxis = Random.onUnitSphere * randomTorque;
rb.AddTorque(torqueAxis, ForceMode.Impulse);
}
if (keepFragmentsClose)
{
LimitFragmentVelocity(rb);
}
}
}
private void LimitFragmentVelocity(Rigidbody rb)
{
if (rb == null)
return;
float maxSpeed = Mathf.Max(0.01f, maxFragmentSpeed);
if (rb.linearVelocity.sqrMagnitude > maxSpeed * maxSpeed)
{
rb.linearVelocity = rb.linearVelocity.normalized * maxSpeed;
}
}
private IEnumerator CleanupAfterDelay(float delay)
{
yield return new WaitForSeconds(delay);
if (spawnedFractureRoot != null)
{
Destroy(spawnedFractureRoot);
spawnedFractureRoot = null;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4049e381a6fd1314abb021f18344b744
@@ -0,0 +1,269 @@
using System.Collections.Generic;
using UnityEngine;
public class RuntimeVoxelExplosion3DTest : MonoBehaviour
{
[Header("Trigger")]
[SerializeField] private bool enableDebugKey = true;
[SerializeField] private KeyCode triggerKey = KeyCode.Alpha0;
[SerializeField] private bool supportKeypad0 = true;
[SerializeField] private bool debugLogs = true;
[Header("Target")]
[SerializeField] private GameObject targetObject;
[SerializeField] private Transform chunkParent;
[SerializeField] private bool disableTargetOnExplode = true;
[SerializeField] private bool destroyPreviousChunks = true;
[Header("Voxel Cut")]
[SerializeField] private int chunksX = 4;
[SerializeField] private int chunksY = 4;
[SerializeField] private int chunksZ = 4;
[SerializeField] private float chunkScaleMultiplier = 0.92f;
[SerializeField] private float occupancyPadding = 0.02f;
[SerializeField] private bool requireColliderOverlap = true;
[SerializeField] private int maxChunkCount = 128;
[Header("Explosion")]
[SerializeField] private float explosionForce = 1.2f;
[SerializeField] private float upwardForce = 0.08f;
[SerializeField] private float randomForceJitter = 0.12f;
[SerializeField] private float randomTorque = 5f;
[SerializeField] private float maxChunkSpeed = 0.9f;
[SerializeField] private float linearDamping = 5f;
[SerializeField] private float angularDamping = 7f;
[SerializeField] private bool disableGravity = false;
[Header("Cleanup")]
[SerializeField] private bool autoDestroyChunks = false;
[SerializeField] private float destroyDelay = 5f;
private readonly List<GameObject> spawnedChunks = new List<GameObject>();
private bool exploded;
private void Update()
{
if (!enableDebugKey)
return;
bool pressed = Input.GetKeyDown(triggerKey);
if (!pressed && supportKeypad0)
{
pressed = Input.GetKeyDown(KeyCode.Keypad0);
}
if (pressed)
{
if (debugLogs)
{
Debug.Log("[RuntimeVoxelExplosion3DTest] Explosion key pressed.");
}
TriggerExplosion();
}
}
[ContextMenu("Trigger Runtime Voxel Explosion")]
public void TriggerExplosion()
{
if (exploded)
{
if (debugLogs)
{
Debug.Log("[RuntimeVoxelExplosion3DTest] Already exploded, ignoring trigger.");
}
return;
}
GameObject target = targetObject != null ? targetObject : gameObject;
if (targetObject == null && debugLogs)
{
Debug.Log("[RuntimeVoxelExplosion3DTest] targetObject not assigned, using current GameObject.");
}
if (target == null)
{
Debug.LogWarning("[RuntimeVoxelExplosion3DTest] Missing targetObject.");
return;
}
Renderer[] renderers = target.GetComponentsInChildren<Renderer>(true);
if (renderers == null || renderers.Length == 0)
{
Debug.LogWarning("[RuntimeVoxelExplosion3DTest] Target has no renderer.");
return;
}
if (destroyPreviousChunks)
{
ClearSpawnedChunks();
}
Bounds bounds = CalculateCombinedBounds(renderers);
Collider[] colliders = target.GetComponentsInChildren<Collider>(true);
Material chunkMaterial = ResolveChunkMaterial(renderers);
int safeX = Mathf.Max(1, chunksX);
int safeY = Mathf.Max(1, chunksY);
int safeZ = Mathf.Max(1, chunksZ);
Vector3 cellSize = new Vector3(
bounds.size.x / safeX,
bounds.size.y / safeY,
bounds.size.z / safeZ);
int spawnedCount = 0;
for (int x = 0; x < safeX; x++)
{
for (int y = 0; y < safeY; y++)
{
for (int z = 0; z < safeZ; z++)
{
if (spawnedCount >= Mathf.Max(1, maxChunkCount))
break;
Vector3 center = new Vector3(
bounds.min.x + cellSize.x * (x + 0.5f),
bounds.min.y + cellSize.y * (y + 0.5f),
bounds.min.z + cellSize.z * (z + 0.5f));
if (requireColliderOverlap && colliders.Length > 0 && !CellOverlapsTarget(center, cellSize, colliders))
continue;
GameObject chunk = GameObject.CreatePrimitive(PrimitiveType.Cube);
chunk.name = $"RuntimeChunk_{x}_{y}_{z}";
chunk.transform.SetParent(chunkParent != null ? chunkParent : null, true);
chunk.transform.position = center;
chunk.transform.rotation = target.transform.rotation;
chunk.transform.localScale = Vector3.Scale(cellSize, Vector3.one * Mathf.Clamp(chunkScaleMultiplier, 0.01f, 1f));
Renderer chunkRenderer = chunk.GetComponent<Renderer>();
if (chunkRenderer != null && chunkMaterial != null)
{
chunkRenderer.sharedMaterial = chunkMaterial;
}
Rigidbody rb = chunk.AddComponent<Rigidbody>();
rb.mass = 0.08f;
rb.linearDamping = linearDamping;
rb.angularDamping = angularDamping;
rb.useGravity = !disableGravity;
ApplyChunkImpulse(rb, bounds.center);
spawnedChunks.Add(chunk);
spawnedCount++;
}
}
}
if (disableTargetOnExplode)
{
target.SetActive(false);
}
if (debugLogs)
{
Debug.Log($"[RuntimeVoxelExplosion3DTest] Spawned {spawnedCount} runtime chunks.");
}
exploded = true;
if (autoDestroyChunks && destroyDelay > 0f)
{
Invoke(nameof(ClearSpawnedChunks), destroyDelay);
}
}
[ContextMenu("Reset Runtime Voxel Explosion")]
public void ResetExplosion()
{
CancelInvoke(nameof(ClearSpawnedChunks));
ClearSpawnedChunks();
exploded = false;
if (targetObject != null)
{
targetObject.SetActive(true);
}
}
private Bounds CalculateCombinedBounds(Renderer[] renderers)
{
Bounds bounds = renderers[0].bounds;
for (int i = 1; i < renderers.Length; i++)
{
bounds.Encapsulate(renderers[i].bounds);
}
return bounds;
}
private Material ResolveChunkMaterial(Renderer[] renderers)
{
for (int i = 0; i < renderers.Length; i++)
{
if (renderers[i] != null && renderers[i].sharedMaterial != null)
return renderers[i].sharedMaterial;
}
return null;
}
private bool CellOverlapsTarget(Vector3 center, Vector3 cellSize, Collider[] colliders)
{
Bounds cellBounds = new Bounds(center, cellSize + Vector3.one * occupancyPadding);
for (int i = 0; i < colliders.Length; i++)
{
Collider col = colliders[i];
if (col == null || !col.enabled)
continue;
if (col.bounds.Intersects(cellBounds))
return true;
}
return false;
}
private void ApplyChunkImpulse(Rigidbody rb, Vector3 explosionCenter)
{
if (rb == null)
return;
Vector3 direction = rb.worldCenterOfMass - explosionCenter;
if (direction.sqrMagnitude < 0.0001f)
{
direction = Random.onUnitSphere;
}
float jitter = 1f + Random.Range(-randomForceJitter, randomForceJitter);
Vector3 impulse = direction.normalized * Mathf.Max(0f, explosionForce * jitter);
impulse += Vector3.up * upwardForce;
rb.linearVelocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
rb.AddForce(impulse, ForceMode.Impulse);
if (randomTorque > 0f)
{
rb.AddTorque(Random.onUnitSphere * randomTorque, ForceMode.Impulse);
}
if (rb.linearVelocity.sqrMagnitude > maxChunkSpeed * maxChunkSpeed)
{
rb.linearVelocity = rb.linearVelocity.normalized * maxChunkSpeed;
}
}
private void ClearSpawnedChunks()
{
for (int i = 0; i < spawnedChunks.Count; i++)
{
if (spawnedChunks[i] != null)
{
Destroy(spawnedChunks[i]);
}
}
spawnedChunks.Clear();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 619dd149d97efea45b1068f55dd7ed99