ui基本完毕,修了一大把的bug
This commit is contained in:
@@ -0,0 +1,533 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
public class trackFractureController : MonoBehaviour
|
||||
{
|
||||
private const int TrackCount = 5;
|
||||
private static readonly int FadeId = Shader.PropertyToID("_Fade");
|
||||
|
||||
[System.Serializable]
|
||||
private class TrackSpriteFadeEntry
|
||||
{
|
||||
public SpriteRenderer spriteRendererA;
|
||||
public SpriteRenderer spriteRendererB;
|
||||
}
|
||||
|
||||
[Header("Tracks")]
|
||||
[SerializeField] private GameObject[] trackObjects = new GameObject[TrackCount];
|
||||
[SerializeField] private Material[] dissolveMaterials = new Material[TrackCount];
|
||||
[SerializeField] private TrackSpriteFadeEntry[] trackSpriteFadeEntries = new TrackSpriteFadeEntry[TrackCount];
|
||||
[SerializeField] private bool autoFetchFractureAndDrift = true;
|
||||
[SerializeField] private bool autoApplyDissolveMaterialOnStart = true;
|
||||
|
||||
[Tooltip("If true, each track gets its own runtime CLONE of its dissolve material. The clone REPLACES the " +
|
||||
"material in the renderer slot, so you can no longer drive _Fade through the original material asset " +
|
||||
"reference. Only needed when several tracks share one material asset. If false (default), the assigned " +
|
||||
"dissolve material is used directly on the renderer, so controlling that material's _Fade (via this " +
|
||||
"controller or externally) drives the dissolve.")]
|
||||
[SerializeField] private bool cloneDissolveMaterialAtRuntime = false;
|
||||
|
||||
[Header("Fracture Fade")]
|
||||
[Tooltip("float1: seconds to wait AFTER a track fractures before its dissolve _Fade starts changing.")]
|
||||
[SerializeField] private float fractureFadeDelay = 0.2f;
|
||||
|
||||
[Tooltip("Seconds over which the linked SpriteRenderer pair fades from alpha 1 to 0 after fracture.")]
|
||||
[SerializeField] private float spriteRendererFadeDuration = 0.2f;
|
||||
|
||||
[Tooltip("float2: seconds over which the dissolve _Fade is driven from 1 to 0 once the fade begins. " +
|
||||
"When this completes, the track's fragments are reclaimed asynchronously (collected one-by-one).")]
|
||||
[SerializeField] private float fractureFadeDuration = 1f;
|
||||
|
||||
[Tooltip("The _Fade value written when the fracture dissolve begins.")]
|
||||
[SerializeField] private float fractureFadeStartValue = 0f;
|
||||
|
||||
[Tooltip("The _Fade value written when the fracture dissolve completes.")]
|
||||
[SerializeField] private float fractureFadeEndValue = 1f;
|
||||
|
||||
[Header("Start Z Move")]
|
||||
[SerializeField] private bool playStartZMove = true;
|
||||
[SerializeField] private float startLocalZ = 0f;
|
||||
[SerializeField] private float endLocalZ = 0.286f;
|
||||
[SerializeField] private float moveDuration = 0.35f;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool enableDebugKeyTrigger = true;
|
||||
[SerializeField] private bool debugLogs = true;
|
||||
|
||||
private readonly FractureAndDrift[] fractureDrivers = new FractureAndDrift[TrackCount];
|
||||
private readonly Renderer[] trackRenderers = new Renderer[TrackCount];
|
||||
private readonly Material[] runtimeDissolveMaterials = new Material[TrackCount];
|
||||
// Original _Fade of each ASSIGNED material asset, captured before we touch it, so we can restore it
|
||||
// on teardown. Needed only when NOT cloning: we drive the asset directly, and Unity persists those
|
||||
// writes back to the .mat, so the value would otherwise stick after exiting play mode. NaN = not captured.
|
||||
private readonly float[] originalFadeValues = new float[TrackCount];
|
||||
private Coroutine startMoveRoutine;
|
||||
private readonly Coroutine[] fadeRoutines = new Coroutine[TrackCount];
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
CacheTrackReferences();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
CacheTrackReferences();
|
||||
ApplyRuntimeDissolveMaterials();
|
||||
SetAllTrackLocalZ(startLocalZ);
|
||||
|
||||
if (playStartZMove)
|
||||
{
|
||||
startMoveRoutine = StartCoroutine(AnimateTracksToEndZ());
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!enableDebugKeyTrigger)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Alpha1) || Input.GetKeyDown(KeyCode.Keypad1))
|
||||
{
|
||||
if (debugLogs) Debug.Log("[trackFractureController] Debug key 1 pressed.", this);
|
||||
TriggerTrackFracture(0);
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Alpha2) || Input.GetKeyDown(KeyCode.Keypad2))
|
||||
{
|
||||
if (debugLogs) Debug.Log("[trackFractureController] Debug key 2 pressed.", this);
|
||||
TriggerTrackFracture(1);
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Alpha3) || Input.GetKeyDown(KeyCode.Keypad3))
|
||||
{
|
||||
if (debugLogs) Debug.Log("[trackFractureController] Debug key 3 pressed.", this);
|
||||
TriggerTrackFracture(2);
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Alpha4) || Input.GetKeyDown(KeyCode.Keypad4))
|
||||
{
|
||||
if (debugLogs) Debug.Log("[trackFractureController] Debug key 4 pressed.", this);
|
||||
TriggerTrackFracture(3);
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Alpha5) || Input.GetKeyDown(KeyCode.Keypad5))
|
||||
{
|
||||
if (debugLogs) Debug.Log("[trackFractureController] Debug key 5 pressed.", this);
|
||||
TriggerTrackFracture(4);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
EnsureArraySizes();
|
||||
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
CacheTrackReferences();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// When not cloning, we drove the assigned .mat asset directly and Unity persists those writes.
|
||||
// Restore each asset's original _Fade so the value doesn't stick after play mode ends.
|
||||
RestoreOriginalFadeValues();
|
||||
|
||||
for (int i = 0; i < runtimeDissolveMaterials.Length; i++)
|
||||
{
|
||||
if (runtimeDissolveMaterials[i] != null)
|
||||
{
|
||||
Destroy(runtimeDissolveMaterials[i]);
|
||||
runtimeDissolveMaterials[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreOriginalFadeValues()
|
||||
{
|
||||
for (int i = 0; i < TrackCount; i++)
|
||||
{
|
||||
// Only restore assets we drove directly (clones are thrown away, no restore needed).
|
||||
if (runtimeDissolveMaterials[i] != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Material sourceMaterial = dissolveMaterials[i];
|
||||
if (sourceMaterial == null || float.IsNaN(originalFadeValues[i]) || !sourceMaterial.HasProperty(FadeId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sourceMaterial.SetFloat(FadeId, originalFadeValues[i]);
|
||||
originalFadeValues[i] = float.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
public void TriggerTrackFractureByNumber(int trackNumber)
|
||||
{
|
||||
TriggerTrackFracture(trackNumber - 1);
|
||||
}
|
||||
|
||||
public void TriggerTrackFracture(int trackIndex)
|
||||
{
|
||||
if (!IsValidTrackIndex(trackIndex))
|
||||
{
|
||||
Debug.LogWarning($"[trackFractureController] Invalid track index: {trackIndex}", this);
|
||||
return;
|
||||
}
|
||||
|
||||
FractureAndDrift fractureDriver = fractureDrivers[trackIndex];
|
||||
if (fractureDriver == null)
|
||||
{
|
||||
Debug.LogWarning($"[trackFractureController] Missing FractureAndDrift on track {trackIndex + 1}.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
fractureDriver.Shatter();
|
||||
|
||||
StartFractureFade(trackIndex);
|
||||
}
|
||||
|
||||
private void StartFractureFade(int trackIndex)
|
||||
{
|
||||
Material targetMaterial = GetFadeTargetMaterial(trackIndex);
|
||||
if (targetMaterial == null || !targetMaterial.HasProperty(FadeId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (fadeRoutines[trackIndex] != null)
|
||||
{
|
||||
StopCoroutine(fadeRoutines[trackIndex]);
|
||||
}
|
||||
|
||||
fadeRoutines[trackIndex] = StartCoroutine(FadeTrackOut(trackIndex, targetMaterial));
|
||||
}
|
||||
|
||||
private IEnumerator FadeTrackOut(int trackIndex, Material targetMaterial)
|
||||
{
|
||||
TrackSpriteFadeEntry spriteFadeEntry = GetSpriteFadeEntry(trackIndex);
|
||||
SetSpriteFadeEntryAlpha(spriteFadeEntry, 1f);
|
||||
|
||||
if (spriteRendererFadeDuration > 0f)
|
||||
{
|
||||
float spriteFadeElapsed = 0f;
|
||||
while (spriteFadeElapsed < spriteRendererFadeDuration)
|
||||
{
|
||||
spriteFadeElapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(spriteFadeElapsed / spriteRendererFadeDuration);
|
||||
SetSpriteFadeEntryAlpha(spriteFadeEntry, Mathf.Lerp(1f, 0f, t));
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSpriteFadeEntryAlpha(spriteFadeEntry, 0f);
|
||||
}
|
||||
|
||||
SetSpriteFadeEntryAlpha(spriteFadeEntry, 0f);
|
||||
|
||||
// float1: hold before the dissolve begins. Fragments have already started drifting during this time.
|
||||
if (fractureFadeDelay > 0f)
|
||||
{
|
||||
yield return new WaitForSeconds(fractureFadeDelay);
|
||||
}
|
||||
|
||||
// float2: drive _Fade from the configured start value to the configured end value over this duration.
|
||||
float duration = Mathf.Max(0.0001f, fractureFadeDuration);
|
||||
float startFade = Mathf.Clamp01(fractureFadeStartValue);
|
||||
float endFade = Mathf.Clamp01(fractureFadeEndValue);
|
||||
float elapsed = 0f;
|
||||
|
||||
targetMaterial.SetFloat(FadeId, startFade);
|
||||
|
||||
while (elapsed < duration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / duration);
|
||||
targetMaterial.SetFloat(FadeId, Mathf.Lerp(startFade, endFade, t));
|
||||
yield return null;
|
||||
}
|
||||
|
||||
targetMaterial.SetFloat(FadeId, endFade);
|
||||
fadeRoutines[trackIndex] = null;
|
||||
|
||||
// Fade finished: asynchronously reclaim (collect) this track's fragments rather than destroying
|
||||
// them all at once.
|
||||
FractureAndDrift fractureDriver = fractureDrivers[trackIndex];
|
||||
if (fractureDriver != null)
|
||||
{
|
||||
fractureDriver.ReclaimFragments();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTrackFadeByNumber(int trackNumber, float fadeValue)
|
||||
{
|
||||
SetTrackFade(trackNumber - 1, fadeValue);
|
||||
}
|
||||
|
||||
public void SetTrackFade(int trackIndex, float fadeValue)
|
||||
{
|
||||
if (!IsValidTrackIndex(trackIndex))
|
||||
{
|
||||
Debug.LogWarning($"[trackFractureController] Invalid track index: {trackIndex}", this);
|
||||
return;
|
||||
}
|
||||
|
||||
Material targetMaterial = GetFadeTargetMaterial(trackIndex);
|
||||
if (targetMaterial == null)
|
||||
{
|
||||
Debug.LogWarning($"[trackFractureController] Missing dissolve material on track {trackIndex + 1}.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!targetMaterial.HasProperty(FadeId))
|
||||
{
|
||||
Debug.LogWarning($"[trackFractureController] Material on track {trackIndex + 1} has no _Fade property.", targetMaterial);
|
||||
return;
|
||||
}
|
||||
|
||||
targetMaterial.SetFloat(FadeId, Mathf.Clamp01(fadeValue));
|
||||
}
|
||||
|
||||
public float GetTrackFade(int trackIndex)
|
||||
{
|
||||
if (!IsValidTrackIndex(trackIndex))
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
Material targetMaterial = GetFadeTargetMaterial(trackIndex);
|
||||
if (targetMaterial == null || !targetMaterial.HasProperty(FadeId))
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
return targetMaterial.GetFloat(FadeId);
|
||||
}
|
||||
|
||||
public void SetAllTrackFade(float fadeValue)
|
||||
{
|
||||
for (int i = 0; i < TrackCount; i++)
|
||||
{
|
||||
SetTrackFade(i, fadeValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void RestartStartZMove()
|
||||
{
|
||||
SetAllTrackLocalZ(startLocalZ);
|
||||
|
||||
if (startMoveRoutine != null)
|
||||
{
|
||||
StopCoroutine(startMoveRoutine);
|
||||
}
|
||||
|
||||
startMoveRoutine = StartCoroutine(AnimateTracksToEndZ());
|
||||
}
|
||||
|
||||
public void TriggerTrack1Fracture() => TriggerTrackFracture(0);
|
||||
public void TriggerTrack2Fracture() => TriggerTrackFracture(1);
|
||||
public void TriggerTrack3Fracture() => TriggerTrackFracture(2);
|
||||
public void TriggerTrack4Fracture() => TriggerTrackFracture(3);
|
||||
public void TriggerTrack5Fracture() => TriggerTrackFracture(4);
|
||||
|
||||
private void CacheTrackReferences()
|
||||
{
|
||||
EnsureArraySizes();
|
||||
|
||||
for (int i = 0; i < TrackCount; i++)
|
||||
{
|
||||
GameObject trackObject = trackObjects[i];
|
||||
fractureDrivers[i] = null;
|
||||
trackRenderers[i] = null;
|
||||
|
||||
if (trackObject == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (autoFetchFractureAndDrift)
|
||||
{
|
||||
fractureDrivers[i] = trackObject.GetComponent<FractureAndDrift>();
|
||||
if (fractureDrivers[i] == null)
|
||||
{
|
||||
fractureDrivers[i] = trackObject.GetComponentInChildren<FractureAndDrift>(true);
|
||||
}
|
||||
}
|
||||
|
||||
trackRenderers[i] = trackObject.GetComponent<Renderer>();
|
||||
if (trackRenderers[i] == null)
|
||||
{
|
||||
trackRenderers[i] = trackObject.GetComponentInChildren<Renderer>(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyRuntimeDissolveMaterials()
|
||||
{
|
||||
for (int i = 0; i < TrackCount; i++)
|
||||
{
|
||||
if (runtimeDissolveMaterials[i] != null)
|
||||
{
|
||||
Destroy(runtimeDissolveMaterials[i]);
|
||||
runtimeDissolveMaterials[i] = null;
|
||||
}
|
||||
|
||||
originalFadeValues[i] = float.NaN;
|
||||
|
||||
Material sourceMaterial = dissolveMaterials[i];
|
||||
Renderer trackRenderer = trackRenderers[i];
|
||||
if (sourceMaterial == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// The material actually used on the renderer AND driven for fade. When cloning is off this is the
|
||||
// assigned asset itself, so external control of that asset's _Fade drives the dissolve directly.
|
||||
Material appliedMaterial = sourceMaterial;
|
||||
if (cloneDissolveMaterialAtRuntime)
|
||||
{
|
||||
appliedMaterial = new Material(sourceMaterial) { name = sourceMaterial.name + "_Runtime" };
|
||||
runtimeDissolveMaterials[i] = appliedMaterial;
|
||||
}
|
||||
else if (sourceMaterial.HasProperty(FadeId))
|
||||
{
|
||||
// Not cloning: we mutate the asset directly, so remember its original _Fade to restore later.
|
||||
originalFadeValues[i] = sourceMaterial.GetFloat(FadeId);
|
||||
}
|
||||
|
||||
if (autoApplyDissolveMaterialOnStart && trackRenderer != null)
|
||||
{
|
||||
Material[] materials = trackRenderer.sharedMaterials;
|
||||
if (materials == null || materials.Length == 0)
|
||||
{
|
||||
trackRenderer.sharedMaterial = appliedMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool replaced = false;
|
||||
for (int j = 0; j < materials.Length; j++)
|
||||
{
|
||||
if (materials[j] == sourceMaterial)
|
||||
{
|
||||
materials[j] = appliedMaterial;
|
||||
replaced = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!replaced)
|
||||
{
|
||||
materials[0] = appliedMaterial;
|
||||
}
|
||||
|
||||
trackRenderer.sharedMaterials = materials;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator AnimateTracksToEndZ()
|
||||
{
|
||||
float duration = Mathf.Max(0.0001f, moveDuration);
|
||||
float elapsed = 0f;
|
||||
|
||||
while (elapsed < duration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / duration);
|
||||
float z = Mathf.Lerp(startLocalZ, endLocalZ, t);
|
||||
SetAllTrackLocalZ(z);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
SetAllTrackLocalZ(endLocalZ);
|
||||
startMoveRoutine = null;
|
||||
}
|
||||
|
||||
private void SetAllTrackLocalZ(float zValue)
|
||||
{
|
||||
for (int i = 0; i < TrackCount; i++)
|
||||
{
|
||||
GameObject trackObject = trackObjects[i];
|
||||
if (trackObject == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Transform targetTransform = trackObject.transform;
|
||||
Vector3 localPosition = targetTransform.localPosition;
|
||||
localPosition.z = zValue;
|
||||
targetTransform.localPosition = localPosition;
|
||||
}
|
||||
}
|
||||
|
||||
private Material GetFadeTargetMaterial(int trackIndex)
|
||||
{
|
||||
if (!IsValidTrackIndex(trackIndex))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// When cloning is on, drive the runtime clone. Otherwise drive the assigned asset directly - which is
|
||||
// also the material now used on the renderer, so controlling that asset's _Fade drives the dissolve.
|
||||
return runtimeDissolveMaterials[trackIndex] != null
|
||||
? runtimeDissolveMaterials[trackIndex]
|
||||
: dissolveMaterials[trackIndex];
|
||||
}
|
||||
|
||||
private void EnsureArraySizes()
|
||||
{
|
||||
if (trackObjects == null || trackObjects.Length != TrackCount)
|
||||
{
|
||||
System.Array.Resize(ref trackObjects, TrackCount);
|
||||
}
|
||||
|
||||
if (dissolveMaterials == null || dissolveMaterials.Length != TrackCount)
|
||||
{
|
||||
System.Array.Resize(ref dissolveMaterials, TrackCount);
|
||||
}
|
||||
|
||||
if (trackSpriteFadeEntries == null || trackSpriteFadeEntries.Length != TrackCount)
|
||||
{
|
||||
System.Array.Resize(ref trackSpriteFadeEntries, TrackCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsValidTrackIndex(int trackIndex)
|
||||
{
|
||||
return trackIndex >= 0 && trackIndex < TrackCount;
|
||||
}
|
||||
|
||||
private TrackSpriteFadeEntry GetSpriteFadeEntry(int trackIndex)
|
||||
{
|
||||
if (!IsValidTrackIndex(trackIndex) || trackSpriteFadeEntries == null || trackIndex >= trackSpriteFadeEntries.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return trackSpriteFadeEntries[trackIndex];
|
||||
}
|
||||
|
||||
private static void SetSpriteFadeEntryAlpha(TrackSpriteFadeEntry entry, float alpha)
|
||||
{
|
||||
if (entry == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetSpriteRendererAlpha(entry.spriteRendererA, alpha);
|
||||
SetSpriteRendererAlpha(entry.spriteRendererB, alpha);
|
||||
}
|
||||
|
||||
private static void SetSpriteRendererAlpha(SpriteRenderer spriteRenderer, float alpha)
|
||||
{
|
||||
if (spriteRenderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Color color = spriteRenderer.color;
|
||||
color.a = Mathf.Clamp01(alpha);
|
||||
spriteRenderer.color = color;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user