Initial commit: Tiny Tackle Heroes Unity project
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BumpCombat.Enemies
|
||||
{
|
||||
/// <summary>Original Lancer pixels, posed from the same clock and tip as combat.</summary>
|
||||
[DisallowMultipleComponent]
|
||||
[DefaultExecutionOrder(1000)]
|
||||
public sealed class LancerThrustVisual : MonoBehaviour
|
||||
{
|
||||
private const string Lease = "LancerThrust";
|
||||
private const float RecoveryPoseDuration = 0.08f;
|
||||
private static readonly float[] TipPixels = { 22f, 20f, 19f, 30f, 28f, 26f };
|
||||
private static Sprite[] bodyFrames;
|
||||
private static Sprite[] spearFrames;
|
||||
private EnemyController controller;
|
||||
private EnemyAttack attack;
|
||||
private SpriteRenderer source;
|
||||
private SpriteRenderer bodyVisual;
|
||||
private SpriteRenderer spearVisual;
|
||||
private bool showing;
|
||||
private bool ownsLease;
|
||||
private float recoveryRemaining;
|
||||
|
||||
public SpriteRenderer BodyRenderer => bodyVisual;
|
||||
public SpriteRenderer SpearRenderer => spearVisual;
|
||||
public bool IsVisible => showing && bodyVisual != null && bodyVisual.enabled;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void ResetFrames()
|
||||
{
|
||||
ReleaseFrames(bodyFrames);
|
||||
ReleaseFrames(spearFrames);
|
||||
bodyFrames = null;
|
||||
spearFrames = null;
|
||||
}
|
||||
|
||||
private static void ReleaseFrames(Sprite[] frames)
|
||||
{
|
||||
if (frames == null) return;
|
||||
foreach (Sprite frame in frames)
|
||||
{
|
||||
if (frame != null) Destroy(frame);
|
||||
}
|
||||
}
|
||||
|
||||
public void Begin(EnemyAttack owner, EnemyController enemy)
|
||||
{
|
||||
attack = owner;
|
||||
controller = enemy;
|
||||
source = GetComponent<SpriteRenderer>();
|
||||
bodyFrames ??= LoadFrames("Body", new Vector2(0.5f, 0.5f));
|
||||
spearFrames ??= LoadFrames("Spear", new Vector2(0.6f, 0.56f));
|
||||
if (source == null || bodyFrames == null || spearFrames == null) return;
|
||||
if (bodyVisual == null) bodyVisual = CreateRenderer("Lancer Thrust Body");
|
||||
if (spearVisual == null) spearVisual = CreateRenderer("Lancer Thrust Spear");
|
||||
recoveryRemaining = 0f;
|
||||
showing = true;
|
||||
if (!ownsLease)
|
||||
{
|
||||
controller.AcquireSpriteVisualLease(Lease);
|
||||
ownsLease = true;
|
||||
}
|
||||
ApplyPose(0, LancerAttackMotion.WarningStartReachFraction);
|
||||
}
|
||||
|
||||
public void Stop(bool cancelled)
|
||||
{
|
||||
if (!showing) return;
|
||||
if (cancelled || !isActiveAndEnabled || controller == null
|
||||
|| controller.IsDead || controller.IsStunned)
|
||||
{
|
||||
Hide();
|
||||
return;
|
||||
}
|
||||
recoveryRemaining = RecoveryPoseDuration;
|
||||
ApplyPose(5, LancerAttackMotion.WarningEndReachFraction);
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!showing) return;
|
||||
if (controller == null || controller.IsDead || controller.IsStunned
|
||||
|| !attack.isActiveAndEnabled)
|
||||
{
|
||||
Hide();
|
||||
return;
|
||||
}
|
||||
if (controller.IsKnockbackActive || controller.IsLaunchVisualActive)
|
||||
{
|
||||
// Knockback can pause an uncancelled attack. Let Hurt/Launch show,
|
||||
// then resume this same state clock when the controller resumes.
|
||||
SuspendRenderers();
|
||||
return;
|
||||
}
|
||||
if (recoveryRemaining > 0f)
|
||||
{
|
||||
recoveryRemaining -= Time.deltaTime;
|
||||
if (recoveryRemaining <= 0f) Hide();
|
||||
else ApplyPose(5, LancerAttackMotion.WarningEndReachFraction);
|
||||
return;
|
||||
}
|
||||
float progress = controller.StateNormalizedTime;
|
||||
if (controller.State == EnemyState.Warning)
|
||||
{
|
||||
ApplyPose(Mathf.Min(2, Mathf.FloorToInt(progress * 3f)),
|
||||
LancerAttackMotion.EvaluateWarningReachFraction(progress));
|
||||
}
|
||||
else if (controller.State == EnemyState.Active)
|
||||
{
|
||||
ApplyPose(progress <= LancerAttackMotion.ActiveExtensionEndFraction ? 3 : 4,
|
||||
LancerAttackMotion.EvaluateActiveReachFraction(progress));
|
||||
}
|
||||
else Hide();
|
||||
}
|
||||
|
||||
private void ApplyPose(int frame, float reach)
|
||||
{
|
||||
if (!ownsLease)
|
||||
{
|
||||
controller.AcquireSpriteVisualLease(Lease);
|
||||
ownsLease = true;
|
||||
}
|
||||
Vector2 direction = attack.LockedDirection.sqrMagnitude > 0.0001f
|
||||
? attack.LockedDirection.normalized : Vector2.right;
|
||||
float scale = Mathf.Max(0.0001f, Mathf.Abs(transform.lossyScale.x));
|
||||
Vector2 hand = (Vector2)transform.position
|
||||
+ LancerAttackMotion.GetHandAnchorOffset(direction, scale);
|
||||
Vector2 tip = LancerAttackMotion.GetTipPosition(
|
||||
transform.position, direction, controller.AttackLength, reach, scale);
|
||||
bodyVisual.sprite = bodyFrames[frame];
|
||||
bodyVisual.flipX = direction.x < 0f;
|
||||
bodyVisual.color = source.color;
|
||||
bodyVisual.sortingLayerID = source.sortingLayerID;
|
||||
bodyVisual.sortingOrder = source.sortingOrder;
|
||||
bodyVisual.enabled = true;
|
||||
spearVisual.sprite = spearFrames[frame];
|
||||
spearVisual.color = source.color;
|
||||
spearVisual.sortingLayerID = source.sortingLayerID;
|
||||
spearVisual.sortingOrder = source.sortingOrder + 1;
|
||||
spearVisual.enabled = true;
|
||||
spearVisual.transform.SetPositionAndRotation(hand,
|
||||
Quaternion.Euler(0f, 0f, Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg));
|
||||
// Spear has no inherited scale: elite reach/width are already combat values.
|
||||
spearVisual.transform.localScale = new Vector3(
|
||||
Vector2.Distance(hand, tip) * 32f / TipPixels[frame],
|
||||
controller.AttackWidth * 32f / 8f, 1f);
|
||||
}
|
||||
|
||||
private SpriteRenderer CreateRenderer(string objectName)
|
||||
{
|
||||
GameObject child = new(objectName);
|
||||
SpriteRenderer renderer = child.AddComponent<SpriteRenderer>();
|
||||
if (objectName.EndsWith("Body")) child.transform.SetParent(transform, false);
|
||||
// An independent transform preserves world rotation/width under scaled elites.
|
||||
renderer.sharedMaterial = source.sharedMaterial;
|
||||
return renderer;
|
||||
}
|
||||
|
||||
private static Sprite[] LoadFrames(string part, Vector2 pivot)
|
||||
{
|
||||
Texture2D texture = Resources.Load<Texture2D>("Enemies/Lancer/Lancer-Thrust-" + part + "-v1");
|
||||
if (texture == null || texture.width != 600 || texture.height != 100) return null;
|
||||
Sprite[] frames = new Sprite[6];
|
||||
for (int i = 0; i < frames.Length; i++)
|
||||
{
|
||||
frames[i] = Sprite.Create(texture, new Rect(i * 100, 0, 100, 100), pivot, 32f,
|
||||
0, SpriteMeshType.FullRect);
|
||||
frames[i].name = "Lancer-Thrust-" + part + "-" + i;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
private void Hide()
|
||||
{
|
||||
showing = false;
|
||||
recoveryRemaining = 0f;
|
||||
SuspendRenderers();
|
||||
}
|
||||
|
||||
private void SuspendRenderers()
|
||||
{
|
||||
if (bodyVisual != null) bodyVisual.enabled = false;
|
||||
if (spearVisual != null) spearVisual.enabled = false;
|
||||
if (ownsLease && controller != null) controller.ReleaseSpriteVisualLease(Lease);
|
||||
ownsLease = false;
|
||||
}
|
||||
|
||||
private void OnDisable() => Hide();
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Hide();
|
||||
if (bodyVisual != null) Destroy(bodyVisual.gameObject);
|
||||
if (spearVisual != null) Destroy(spearVisual.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user