Initial commit: Tiny Tackle Heroes Unity project
This commit is contained in:
@@ -0,0 +1,874 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BumpCombat.Core;
|
||||
using BumpCombat.Player;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BumpCombat.Combat
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
[DefaultExecutionOrder(1000)]
|
||||
public sealed class ArtifactChargeVisual : MonoBehaviour
|
||||
{
|
||||
public const int FrameCount = 6;
|
||||
public const int FlashFrameCount = 4;
|
||||
public const int CellSize = 64;
|
||||
public const float PixelsPerUnit = 32f;
|
||||
public const float ChargeVisualY = 0f;
|
||||
public const float ReadyFlashDuration = 0.14f;
|
||||
public const float ReleaseVisualDuration = 0.24f;
|
||||
public const float SwitchVisualDuration = 0.36f;
|
||||
|
||||
private const int AuraSortingOffset = -1;
|
||||
private const int ReadyAuraSortingOffset = -1;
|
||||
private const int FlashSortingOffset = 1;
|
||||
private const int ReleaseSortingOffset = 1;
|
||||
private const string ResourceRoot = "Artifacts/ThreeColor-v1/Charge/";
|
||||
private const string AuraSuffix = "-Aura-v1";
|
||||
private const string ReadyAuraSuffix = "-ReadyAura-v1";
|
||||
private const string FlashSuffix = "-Flash-v1";
|
||||
private const string ReleaseSuffix = "-Release-v1";
|
||||
|
||||
private static readonly float[] AuraFrameDurations =
|
||||
{
|
||||
0.06f, 0.06f, 0.06f, 0.06f, 0.06f, 0.06f,
|
||||
};
|
||||
|
||||
private static readonly float[] ReadyAuraFrameDurations =
|
||||
{
|
||||
0.06f, 0.06f, 0.06f, 0.06f, 0.06f, 0.06f,
|
||||
};
|
||||
|
||||
private static readonly float[] FlashFrameDurations =
|
||||
{
|
||||
0.03f, 0.04f, 0.04f, 0.03f,
|
||||
};
|
||||
|
||||
private static readonly float[] ReleaseFrameDurations =
|
||||
{
|
||||
0.03f, 0.04f, 0.04f, 0.04f, 0.04f, 0.05f,
|
||||
};
|
||||
|
||||
private sealed class CachedFrames
|
||||
{
|
||||
public CachedFrames(Sprite[] frames, bool ownsSprites)
|
||||
{
|
||||
Frames = frames;
|
||||
OwnsSprites = ownsSprites;
|
||||
}
|
||||
|
||||
public Sprite[] Frames { get; }
|
||||
public bool OwnsSprites { get; }
|
||||
}
|
||||
|
||||
private sealed class VisualSlot
|
||||
{
|
||||
public VisualSlot(
|
||||
string objectName,
|
||||
int sortingOffset,
|
||||
float[] frameDurations)
|
||||
{
|
||||
ObjectName = objectName;
|
||||
SortingOffset = sortingOffset;
|
||||
FrameDurations = frameDurations;
|
||||
}
|
||||
|
||||
public string ObjectName { get; }
|
||||
public int SortingOffset { get; }
|
||||
public float[] FrameDurations { get; }
|
||||
public Sprite[] Frames;
|
||||
public GameObject Root;
|
||||
public SpriteRenderer Renderer;
|
||||
public int FrameIndex;
|
||||
public float FrameElapsed;
|
||||
public bool Loop;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, CachedFrames> FrameCache = new();
|
||||
|
||||
private readonly VisualSlot aura = new(
|
||||
"Artifact Charge Aura",
|
||||
AuraSortingOffset,
|
||||
AuraFrameDurations);
|
||||
private readonly VisualSlot readyAura = new(
|
||||
"Artifact Charge Ready Aura",
|
||||
ReadyAuraSortingOffset,
|
||||
ReadyAuraFrameDurations);
|
||||
private readonly VisualSlot flash = new(
|
||||
"Artifact Charge Flash",
|
||||
FlashSortingOffset,
|
||||
FlashFrameDurations);
|
||||
private readonly VisualSlot release = new(
|
||||
"Artifact Release",
|
||||
ReleaseSortingOffset,
|
||||
ReleaseFrameDurations);
|
||||
private readonly VisualSlot switchPulse = new(
|
||||
"Artifact Switch",
|
||||
FlashSortingOffset,
|
||||
AuraFrameDurations);
|
||||
|
||||
private ActiveArtifactController controller;
|
||||
private readonly Dictionary<string, Sprite[]> equipmentFrames = new();
|
||||
private readonly Dictionary<Sprite, Sprite> equipmentOriginals = new();
|
||||
private PlayerHealth playerHealth;
|
||||
private SpriteRenderer playerRenderer;
|
||||
private SpriteRenderer guardOutline;
|
||||
private readonly Dictionary<Sprite, Sprite> guardFrames = new();
|
||||
private RunManager subscribedRunManager;
|
||||
private ActiveArtifactDefinition chargingArtifact;
|
||||
private bool readyFlashPlayed;
|
||||
private int flashPlayCount;
|
||||
private int releasePlayCount;
|
||||
|
||||
public bool IsAuraVisible => IsVisible(aura);
|
||||
public bool IsSwitchVisible => IsVisible(switchPulse);
|
||||
public GameObject SwitchObject => switchPulse.Root;
|
||||
public bool IsReadyAuraVisible => IsVisible(readyAura);
|
||||
public bool IsFlashVisible => IsVisible(flash);
|
||||
public bool IsReleaseVisible => IsVisible(release);
|
||||
public GameObject AuraObject => aura.Root;
|
||||
public GameObject ReadyAuraObject => readyAura.Root;
|
||||
public GameObject FlashObject => flash.Root;
|
||||
public GameObject ReleaseObject => release.Root;
|
||||
public int FlashPlayCount => flashPlayCount;
|
||||
public int ReleasePlayCount => releasePlayCount;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void ResetFrameCache()
|
||||
{
|
||||
foreach (CachedFrames cached in FrameCache.Values)
|
||||
{
|
||||
if (!cached.OwnsSprites)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Sprite sprite in cached.Frames)
|
||||
{
|
||||
if (sprite == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Destroy(sprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyImmediate(sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FrameCache.Clear();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
controller = GetComponent<ActiveArtifactController>();
|
||||
playerHealth = GetComponent<PlayerHealth>();
|
||||
playerRenderer = FindPlayerRenderer();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (controller == null)
|
||||
{
|
||||
controller = GetComponent<ActiveArtifactController>();
|
||||
}
|
||||
|
||||
if (playerHealth == null)
|
||||
{
|
||||
playerHealth = GetComponent<PlayerHealth>();
|
||||
}
|
||||
|
||||
if (playerRenderer == null)
|
||||
{
|
||||
playerRenderer = FindPlayerRenderer();
|
||||
}
|
||||
|
||||
SubscribeController();
|
||||
SubscribePlayerHealth();
|
||||
RefreshRunManagerSubscription();
|
||||
RestoreCurrentChargeVisuals();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (guardOutline != null) guardOutline.enabled = false;
|
||||
RestoreEquipmentSprite();
|
||||
UnsubscribeController();
|
||||
UnsubscribePlayerHealth();
|
||||
UnsubscribeRunManager();
|
||||
ClearAllVisuals();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (guardOutline != null) Destroy(guardOutline.gameObject);
|
||||
UnsubscribeController();
|
||||
UnsubscribePlayerHealth();
|
||||
UnsubscribeRunManager();
|
||||
ClearAllVisuals();
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
RefreshEquipmentSprite();
|
||||
RefreshGuardOutline();
|
||||
RefreshRunManagerSubscription();
|
||||
if (subscribedRunManager != null
|
||||
&& (subscribedRunManager.IsSelectionOpen
|
||||
|| subscribedRunManager.IsGameOver
|
||||
|| subscribedRunManager.IsTitleScreen))
|
||||
{
|
||||
ClearAllVisuals();
|
||||
return;
|
||||
}
|
||||
|
||||
if (controller == null)
|
||||
{
|
||||
ClearAllVisuals();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!controller.isActiveAndEnabled)
|
||||
{
|
||||
ClearAllVisuals();
|
||||
return;
|
||||
}
|
||||
|
||||
if (controller.IsCharging)
|
||||
{
|
||||
UpdateChargeVisuals();
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearChargeHoldVisuals();
|
||||
}
|
||||
|
||||
SyncSlot(aura, true);
|
||||
SyncSlot(readyAura, true);
|
||||
SyncSlot(flash, true);
|
||||
SyncSlot(release, false);
|
||||
SyncSlot(switchPulse, true);
|
||||
AdvanceSlot(aura, Time.deltaTime);
|
||||
AdvanceSlot(readyAura, Time.deltaTime);
|
||||
AdvanceSlot(flash, Time.deltaTime);
|
||||
AdvanceSlot(release, Time.deltaTime);
|
||||
AdvanceSlot(switchPulse, Time.deltaTime);
|
||||
}
|
||||
|
||||
public void RefreshEquipmentSprite()
|
||||
{
|
||||
if (playerRenderer == null || playerRenderer.sprite == null) return;
|
||||
Sprite current = playerRenderer.sprite;
|
||||
Sprite original = equipmentOriginals.TryGetValue(current, out Sprite source) ? source : current;
|
||||
if (controller == null || !controller.isActiveAndEnabled || controller.CurrentArtifact == null)
|
||||
{
|
||||
playerRenderer.sprite = original;
|
||||
return;
|
||||
}
|
||||
string path = "Artifacts/ThreeColor-v1/Player/" + original.texture.name
|
||||
+ "-" + controller.CurrentArtifact.ArtifactColor + "-v1";
|
||||
if (!equipmentFrames.TryGetValue(path, out Sprite[] frames))
|
||||
{
|
||||
frames = Resources.LoadAll<Sprite>(path);
|
||||
equipmentFrames[path] = frames;
|
||||
}
|
||||
foreach (Sprite frame in frames)
|
||||
{
|
||||
if (frame.rect != original.rect) continue;
|
||||
equipmentOriginals[frame] = original;
|
||||
playerRenderer.sprite = frame;
|
||||
return;
|
||||
}
|
||||
playerRenderer.sprite = original;
|
||||
}
|
||||
|
||||
public void RefreshGuardOutline()
|
||||
{
|
||||
bool visible = isActiveAndEnabled && playerHealth != null
|
||||
&& playerHealth.isActiveAndEnabled && playerHealth.CurrentHealth > 0f
|
||||
&& playerHealth.IsGuarding && playerRenderer != null
|
||||
&& playerRenderer.enabled && playerRenderer.sprite != null
|
||||
&& (RunManager.Instance == null || !RunManager.Instance.IsGameOver);
|
||||
if (!visible)
|
||||
{
|
||||
if (guardOutline != null) guardOutline.enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (guardOutline == null)
|
||||
{
|
||||
var root = new GameObject("Player Guard Outline");
|
||||
root.transform.SetParent(playerRenderer.transform, false);
|
||||
guardOutline = root.AddComponent<SpriteRenderer>();
|
||||
}
|
||||
|
||||
Sprite current = playerRenderer.sprite;
|
||||
Sprite original = equipmentOriginals.TryGetValue(current, out Sprite source) ? source : current;
|
||||
if (!guardFrames.TryGetValue(original, out Sprite mask))
|
||||
{
|
||||
foreach (Sprite frame in Resources.LoadAll<Sprite>("Combat/Guard-v1/" + original.texture.name + "-outline"))
|
||||
{
|
||||
if (frame.rect != original.rect) continue;
|
||||
mask = frame;
|
||||
break;
|
||||
}
|
||||
guardFrames[original] = mask;
|
||||
}
|
||||
guardOutline.sprite = mask;
|
||||
guardOutline.enabled = mask != null;
|
||||
guardOutline.flipX = playerRenderer.flipX;
|
||||
guardOutline.flipY = playerRenderer.flipY;
|
||||
guardOutline.color = new Color(1f, 1f, 1f, playerRenderer.color.a);
|
||||
guardOutline.sharedMaterial = playerRenderer.sharedMaterial;
|
||||
guardOutline.sortingLayerID = playerRenderer.sortingLayerID;
|
||||
guardOutline.sortingOrder = playerRenderer.sortingOrder + 1;
|
||||
}
|
||||
|
||||
private void RestoreEquipmentSprite()
|
||||
{
|
||||
if (playerRenderer != null && playerRenderer.sprite != null
|
||||
&& equipmentOriginals.TryGetValue(playerRenderer.sprite, out Sprite original))
|
||||
playerRenderer.sprite = original;
|
||||
}
|
||||
|
||||
public void Connect(ActiveArtifactController artifactController)
|
||||
{
|
||||
if (controller == artifactController && controller != null)
|
||||
{
|
||||
SubscribeController();
|
||||
return;
|
||||
}
|
||||
|
||||
UnsubscribeController();
|
||||
controller = artifactController;
|
||||
SubscribeController();
|
||||
}
|
||||
|
||||
public void NotifyChargeEnded(bool preserveReadyFlash)
|
||||
{
|
||||
ClearChargeHoldVisuals();
|
||||
if (!preserveReadyFlash)
|
||||
{
|
||||
ClearSlot(flash);
|
||||
}
|
||||
}
|
||||
|
||||
public void NotifyArtifactUseFailed()
|
||||
{
|
||||
ClearAllVisuals();
|
||||
}
|
||||
|
||||
public void ClearAllVisuals()
|
||||
{
|
||||
ClearSlot(aura);
|
||||
ClearSlot(readyAura);
|
||||
ClearSlot(flash);
|
||||
ClearSlot(release);
|
||||
ClearSlot(switchPulse);
|
||||
}
|
||||
|
||||
private void SubscribeController()
|
||||
{
|
||||
if (controller == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
controller.OnStateChanged -= HandleControllerStateChanged;
|
||||
controller.OnArtifactChargeStarted -= HandleArtifactChargeStarted;
|
||||
controller.OnArtifactUseSucceeded -= HandleArtifactUseSucceeded;
|
||||
controller.OnArtifactSwitched -= HandleArtifactSwitched;
|
||||
controller.OnStateChanged += HandleControllerStateChanged;
|
||||
controller.OnArtifactChargeStarted += HandleArtifactChargeStarted;
|
||||
controller.OnArtifactUseSucceeded += HandleArtifactUseSucceeded;
|
||||
controller.OnArtifactSwitched += HandleArtifactSwitched;
|
||||
}
|
||||
|
||||
private void UnsubscribeController()
|
||||
{
|
||||
if (controller == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
controller.OnStateChanged -= HandleControllerStateChanged;
|
||||
controller.OnArtifactChargeStarted -= HandleArtifactChargeStarted;
|
||||
controller.OnArtifactUseSucceeded -= HandleArtifactUseSucceeded;
|
||||
controller.OnArtifactSwitched -= HandleArtifactSwitched;
|
||||
}
|
||||
|
||||
private void SubscribePlayerHealth()
|
||||
{
|
||||
if (playerHealth == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playerHealth.OnDamaged -= HandlePlayerDamaged;
|
||||
playerHealth.OnDied -= HandlePlayerDied;
|
||||
playerHealth.OnDamaged += HandlePlayerDamaged;
|
||||
playerHealth.OnDied += HandlePlayerDied;
|
||||
}
|
||||
|
||||
private void UnsubscribePlayerHealth()
|
||||
{
|
||||
if (playerHealth == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playerHealth.OnDamaged -= HandlePlayerDamaged;
|
||||
playerHealth.OnDied -= HandlePlayerDied;
|
||||
}
|
||||
|
||||
private void RefreshRunManagerSubscription()
|
||||
{
|
||||
RunManager current = RunManager.Instance;
|
||||
if (current == subscribedRunManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UnsubscribeRunManager();
|
||||
subscribedRunManager = current;
|
||||
if (subscribedRunManager != null)
|
||||
{
|
||||
subscribedRunManager.OnSelectionChanged += HandleSelectionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsubscribeRunManager()
|
||||
{
|
||||
if (subscribedRunManager != null)
|
||||
{
|
||||
subscribedRunManager.OnSelectionChanged -= HandleSelectionChanged;
|
||||
subscribedRunManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleArtifactChargeStarted(ActiveArtifactDefinition definition)
|
||||
{
|
||||
ClearSlot(switchPulse);
|
||||
chargingArtifact = definition;
|
||||
readyFlashPlayed = false;
|
||||
ClearSlot(readyAura);
|
||||
ClearSlot(flash);
|
||||
ClearSlot(aura);
|
||||
if (!Application.isPlaying || definition == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
aura.Frames = LoadFrames(
|
||||
GetResourcePath(definition, AuraSuffix),
|
||||
FrameCount);
|
||||
readyAura.Frames = LoadFrames(
|
||||
GetResourcePath(definition, ReadyAuraSuffix),
|
||||
FrameCount);
|
||||
flash.Frames = LoadFrames(
|
||||
GetResourcePath(definition, FlashSuffix),
|
||||
FlashFrameCount);
|
||||
if (aura.Frames.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CreateSlot(aura, true, Vector2.zero, 1f);
|
||||
}
|
||||
|
||||
private void RestoreCurrentChargeVisuals()
|
||||
{
|
||||
if (!Application.isPlaying
|
||||
|| controller == null
|
||||
|| !controller.IsCharging)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
chargingArtifact = controller.CurrentArtifact;
|
||||
if (chargingArtifact == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
aura.Frames = LoadFrames(
|
||||
GetResourcePath(chargingArtifact, AuraSuffix),
|
||||
FrameCount);
|
||||
readyAura.Frames = LoadFrames(
|
||||
GetResourcePath(chargingArtifact, ReadyAuraSuffix),
|
||||
FrameCount);
|
||||
flash.Frames = LoadFrames(
|
||||
GetResourcePath(chargingArtifact, FlashSuffix),
|
||||
FlashFrameCount);
|
||||
UpdateChargeVisuals();
|
||||
}
|
||||
|
||||
private void HandleControllerStateChanged()
|
||||
{
|
||||
if (controller == null || !controller.IsCharging)
|
||||
{
|
||||
ClearChargeHoldVisuals();
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateChargeVisuals();
|
||||
}
|
||||
|
||||
private void HandleArtifactUseSucceeded(
|
||||
ActiveArtifactDefinition definition,
|
||||
bool charged)
|
||||
{
|
||||
ClearSlot(switchPulse);
|
||||
ClearChargeHoldVisuals();
|
||||
if (!Application.isPlaying || definition == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
release.Frames = LoadFrames(
|
||||
GetResourcePath(definition, ReleaseSuffix),
|
||||
FrameCount);
|
||||
if (release.Frames.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 origin = controller.LastArtifactUsePosition;
|
||||
CreateSlot(release, false, origin, charged ? 1.2f : 1f);
|
||||
releasePlayCount++;
|
||||
}
|
||||
|
||||
private void HandleArtifactSwitched(ActiveArtifactDefinition definition)
|
||||
{
|
||||
ClearSlot(switchPulse);
|
||||
if (!Application.isPlaying || definition == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switchPulse.Frames = LoadFrames(
|
||||
GetResourcePath(definition, ReleaseSuffix), FrameCount);
|
||||
if (switchPulse.Frames.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Reuse the existing colored outward burst, following the player
|
||||
// for a single short cycle so movement does not leave it behind.
|
||||
CreateSlot(switchPulse, true, Vector2.zero, 1f);
|
||||
}
|
||||
|
||||
private void HandlePlayerDamaged(Vector2 _, float __)
|
||||
{
|
||||
ClearAllVisuals();
|
||||
}
|
||||
|
||||
private void HandlePlayerDied()
|
||||
{
|
||||
ClearAllVisuals();
|
||||
}
|
||||
|
||||
private void HandleSelectionChanged(bool isOpen)
|
||||
{
|
||||
if (isOpen)
|
||||
{
|
||||
ClearAllVisuals();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateChargeVisuals()
|
||||
{
|
||||
if (controller == null
|
||||
|| !controller.IsCharging
|
||||
|| chargingArtifact == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!controller.IsFullyCharged)
|
||||
{
|
||||
EnsureLoopSlot(aura, AuraSuffix, 1f);
|
||||
ClearSlot(readyAura);
|
||||
SetAlpha(
|
||||
aura,
|
||||
Mathf.Lerp(0.55f, 0.85f, controller.ChargeProgress));
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureLoopSlot(readyAura, ReadyAuraSuffix, 1f);
|
||||
ClearSlot(aura);
|
||||
if (!readyFlashPlayed)
|
||||
{
|
||||
readyFlashPlayed = true;
|
||||
flashPlayCount++;
|
||||
if (flash.Frames.Length > 0 && Application.isPlaying)
|
||||
{
|
||||
CreateSlot(flash, true, Vector2.zero, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureLoopSlot(
|
||||
VisualSlot slot,
|
||||
string suffix,
|
||||
float scale)
|
||||
{
|
||||
if (slot.Root != null || chargingArtifact == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
slot.Frames = LoadFrames(
|
||||
GetResourcePath(chargingArtifact, suffix),
|
||||
FrameCount);
|
||||
if (slot.Frames.Length == 0 || !Application.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CreateSlot(slot, true, Vector2.zero, scale);
|
||||
}
|
||||
|
||||
private void CreateSlot(
|
||||
VisualSlot slot,
|
||||
bool followPlayer,
|
||||
Vector2 worldOrigin,
|
||||
float scale)
|
||||
{
|
||||
ClearSlot(slot);
|
||||
slot.Root = new GameObject(slot.ObjectName);
|
||||
if (followPlayer)
|
||||
{
|
||||
slot.Root.transform.SetParent(transform, false);
|
||||
slot.Root.transform.localPosition = new Vector3(
|
||||
0f,
|
||||
ChargeVisualY,
|
||||
0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
slot.Root.transform.position = new Vector3(
|
||||
worldOrigin.x,
|
||||
worldOrigin.y + ChargeVisualY,
|
||||
transform.position.z);
|
||||
}
|
||||
|
||||
slot.Root.transform.localScale = Vector3.one * scale;
|
||||
slot.Renderer = slot.Root.AddComponent<SpriteRenderer>();
|
||||
slot.Renderer.color = Color.white;
|
||||
slot.Renderer.sprite = slot.Frames[0];
|
||||
slot.Renderer.enabled = true;
|
||||
slot.FrameIndex = 0;
|
||||
slot.FrameElapsed = 0f;
|
||||
slot.Loop = slot == aura || slot == readyAura;
|
||||
SyncSlot(slot, followPlayer);
|
||||
}
|
||||
|
||||
private void SyncSlot(VisualSlot slot, bool followPlayer)
|
||||
{
|
||||
if (slot.Root == null || slot.Renderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (followPlayer)
|
||||
{
|
||||
slot.Root.transform.SetParent(transform, false);
|
||||
slot.Root.transform.localPosition = new Vector3(
|
||||
0f,
|
||||
ChargeVisualY,
|
||||
0f);
|
||||
}
|
||||
|
||||
if (playerRenderer == null)
|
||||
{
|
||||
playerRenderer = FindPlayerRenderer();
|
||||
}
|
||||
|
||||
if (playerRenderer != null)
|
||||
{
|
||||
slot.Renderer.sortingLayerID = playerRenderer.sortingLayerID;
|
||||
slot.Renderer.sortingOrder =
|
||||
playerRenderer.sortingOrder + slot.SortingOffset;
|
||||
slot.Renderer.sharedMaterial = playerRenderer.sharedMaterial;
|
||||
}
|
||||
|
||||
Color color = Color.white;
|
||||
if (slot == aura
|
||||
&& controller != null
|
||||
&& controller.IsCharging
|
||||
&& !controller.IsFullyCharged)
|
||||
{
|
||||
color.a = Mathf.Lerp(0.55f, 0.85f, controller.ChargeProgress);
|
||||
}
|
||||
slot.Renderer.color = color;
|
||||
slot.Renderer.enabled = true;
|
||||
}
|
||||
|
||||
private static void SetAlpha(VisualSlot slot, float alpha)
|
||||
{
|
||||
if (slot.Renderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Color color = Color.white;
|
||||
color.a = Mathf.Clamp01(alpha);
|
||||
slot.Renderer.color = color;
|
||||
}
|
||||
|
||||
private static void AdvanceSlot(VisualSlot slot, float deltaTime)
|
||||
{
|
||||
if (slot.Root == null
|
||||
|| slot.Renderer == null
|
||||
|| slot.Frames == null
|
||||
|| slot.Frames.Length == 0
|
||||
|| deltaTime <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
slot.FrameElapsed += deltaTime;
|
||||
while (slot.FrameElapsed >= slot.FrameDurations[slot.FrameIndex])
|
||||
{
|
||||
slot.FrameElapsed -= slot.FrameDurations[slot.FrameIndex];
|
||||
int nextFrame = slot.FrameIndex + 1;
|
||||
if (nextFrame >= slot.Frames.Length)
|
||||
{
|
||||
if (!slot.Loop)
|
||||
{
|
||||
ClearSlot(slot);
|
||||
return;
|
||||
}
|
||||
|
||||
nextFrame = 0;
|
||||
}
|
||||
|
||||
slot.FrameIndex = nextFrame;
|
||||
slot.Renderer.sprite = slot.Frames[slot.FrameIndex];
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearChargeHoldVisuals()
|
||||
{
|
||||
ClearSlot(aura);
|
||||
ClearSlot(readyAura);
|
||||
}
|
||||
|
||||
private static bool IsVisible(VisualSlot slot)
|
||||
{
|
||||
return slot.Root != null
|
||||
&& slot.Renderer != null
|
||||
&& slot.Renderer.enabled;
|
||||
}
|
||||
|
||||
private SpriteRenderer FindPlayerRenderer()
|
||||
{
|
||||
SpriteRenderer rootRenderer = GetComponent<SpriteRenderer>();
|
||||
if (rootRenderer != null)
|
||||
{
|
||||
return rootRenderer;
|
||||
}
|
||||
|
||||
SpriteRenderer[] renderers = GetComponentsInChildren<SpriteRenderer>(true);
|
||||
foreach (SpriteRenderer renderer in renderers)
|
||||
{
|
||||
if (renderer != null && renderer.transform != transform)
|
||||
{
|
||||
return renderer;
|
||||
}
|
||||
}
|
||||
|
||||
return GetComponent<SpriteRenderer>();
|
||||
}
|
||||
|
||||
private static string GetResourcePath(
|
||||
ActiveArtifactDefinition definition,
|
||||
string suffix)
|
||||
{
|
||||
return ResourceRoot + definition.Effect + suffix;
|
||||
}
|
||||
|
||||
private static void ClearSlot(VisualSlot slot)
|
||||
{
|
||||
if (slot.Root != null)
|
||||
{
|
||||
slot.Root.SetActive(false);
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Destroy(slot.Root);
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyImmediate(slot.Root);
|
||||
}
|
||||
}
|
||||
|
||||
slot.Root = null;
|
||||
slot.Renderer = null;
|
||||
slot.FrameIndex = 0;
|
||||
slot.FrameElapsed = 0f;
|
||||
}
|
||||
|
||||
private static Sprite[] LoadFrames(
|
||||
string resourcePath,
|
||||
int expectedFrameCount)
|
||||
{
|
||||
if (FrameCache.TryGetValue(resourcePath, out CachedFrames cached))
|
||||
{
|
||||
return cached.Frames;
|
||||
}
|
||||
|
||||
Sprite[] importedFrames = Resources.LoadAll<Sprite>(resourcePath);
|
||||
if (importedFrames != null
|
||||
&& importedFrames.Length >= expectedFrameCount)
|
||||
{
|
||||
Array.Sort(
|
||||
importedFrames,
|
||||
(left, right) => String.CompareOrdinal(left.name, right.name));
|
||||
Sprite[] frames = new Sprite[expectedFrameCount];
|
||||
Array.Copy(importedFrames, frames, expectedFrameCount);
|
||||
FrameCache[resourcePath] = new CachedFrames(frames, false);
|
||||
return frames;
|
||||
}
|
||||
|
||||
Texture2D texture = importedFrames != null
|
||||
&& importedFrames.Length > 0
|
||||
? importedFrames[0].texture
|
||||
: Resources.Load<Texture2D>(resourcePath);
|
||||
if (texture == null
|
||||
|| texture.width < CellSize * expectedFrameCount
|
||||
|| texture.height < CellSize
|
||||
|| texture.width % expectedFrameCount != 0)
|
||||
{
|
||||
Sprite[] missing = Array.Empty<Sprite>();
|
||||
FrameCache[resourcePath] = new CachedFrames(missing, false);
|
||||
return missing;
|
||||
}
|
||||
|
||||
int frameWidth = texture.width / expectedFrameCount;
|
||||
Sprite[] generatedFrames = new Sprite[expectedFrameCount];
|
||||
for (int i = 0; i < expectedFrameCount; i++)
|
||||
{
|
||||
generatedFrames[i] = Sprite.Create(
|
||||
texture,
|
||||
new Rect(i * frameWidth, 0f, frameWidth, CellSize),
|
||||
new Vector2(0.5f, 0.5f),
|
||||
PixelsPerUnit,
|
||||
0,
|
||||
SpriteMeshType.FullRect);
|
||||
generatedFrames[i].name =
|
||||
$"{resourcePath} Frame {i + 1}";
|
||||
generatedFrames[i].hideFlags = HideFlags.HideAndDontSave;
|
||||
}
|
||||
|
||||
FrameCache[resourcePath] = new CachedFrames(generatedFrames, true);
|
||||
return generatedFrames;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user