Add fixed-camera tower effect preview scene
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BumpCombat.Defense
|
||||
{
|
||||
public sealed class Defense25DPreview : MonoBehaviour
|
||||
{
|
||||
private const string BodyKey = "Bodies/Fire/T3-Idle";
|
||||
private const string FlightKey = "Effects/Fire/Meteor-Flight";
|
||||
private const string ImpactKey = "Effects/Fire/Meteor-Impact";
|
||||
private const float AuraFrameSeconds = 0.14f;
|
||||
|
||||
[SerializeField] private SpriteRenderer body, auraBack, auraFront, meteor;
|
||||
[SerializeField] private Sprite[] auraBackFrames, auraFrontFrames, meteorFlightFrames, meteorImpactFrames, bodyFrames;
|
||||
[SerializeField] private Transform groundAnchor, bodyVisual;
|
||||
[SerializeField] private float flightHeight = 1.7f;
|
||||
|
||||
public SpriteRenderer Body => body;
|
||||
public SpriteRenderer AuraBack => auraBack;
|
||||
public SpriteRenderer AuraFront => auraFront;
|
||||
public SpriteRenderer Meteor => meteor;
|
||||
public Sprite[] AuraBackFrames => auraBackFrames;
|
||||
public Sprite[] AuraFrontFrames => auraFrontFrames;
|
||||
public Sprite[] MeteorFlightFrames => meteorFlightFrames;
|
||||
public Sprite[] MeteorImpactFrames => meteorImpactFrames;
|
||||
public Sprite[] BodyFrames => bodyFrames;
|
||||
public Transform GroundAnchor => groundAnchor;
|
||||
public Transform BodyVisual => bodyVisual;
|
||||
public float CurrentMeteorHeight { get; private set; }
|
||||
public Vector2 CurrentMeteorGround { get; private set; }
|
||||
|
||||
public void Configure(Sprite[] back, Sprite[] front, Sprite[] flight, Sprite[] impact, Sprite[] character)
|
||||
{
|
||||
auraBackFrames = back; auraFrontFrames = front; meteorFlightFrames = flight;
|
||||
meteorImpactFrames = impact; bodyFrames = character;
|
||||
}
|
||||
|
||||
public static Vector3 ProjectGroundAndHeight(Vector2 groundPosition, float height) =>
|
||||
new Vector3(groundPosition.x - height, groundPosition.y + height, 0f);
|
||||
|
||||
private IEnumerator Start()
|
||||
{
|
||||
StartCoroutine(AnimateAura());
|
||||
StartCoroutine(AnimateBody());
|
||||
StartCoroutine(MoveTarget());
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitForSeconds(0.25f);
|
||||
yield return PlayMeteor();
|
||||
yield return new WaitForSeconds(0.55f);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator MoveTarget()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Vector3 p = groundAnchor.position;
|
||||
p.x = Mathf.Sin(Time.time * 0.7f) * 0.65f;
|
||||
groundAnchor.position = p;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator AnimateAura()
|
||||
{
|
||||
int frame = 0;
|
||||
while (true)
|
||||
{
|
||||
auraBack.sprite = auraBackFrames[frame];
|
||||
auraFront.sprite = auraFrontFrames[frame];
|
||||
frame = (frame + 1) % auraBackFrames.Length;
|
||||
yield return new WaitForSeconds(AuraFrameSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator AnimateBody()
|
||||
{
|
||||
int frame = 0;
|
||||
while (true)
|
||||
{
|
||||
body.sprite = bodyFrames[frame];
|
||||
frame = (frame + 1) % bodyFrames.Length;
|
||||
yield return new WaitForSeconds(DefenseTowerArt.FrameSeconds(BodyKey, frame == 0 ? bodyFrames.Length - 1 : frame - 1));
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator PlayMeteor()
|
||||
{
|
||||
CurrentMeteorGround = groundAnchor.position;
|
||||
meteor.enabled = true;
|
||||
CurrentMeteorHeight = flightHeight;
|
||||
float flightDuration = 0f;
|
||||
for (int i = 0; i < meteorFlightFrames.Length; i++) flightDuration += DefenseTowerArt.FrameSeconds(FlightKey, i);
|
||||
|
||||
float elapsed = 0f;
|
||||
for (int i = 0; i < meteorFlightFrames.Length; i++)
|
||||
{
|
||||
meteor.sprite = meteorFlightFrames[i];
|
||||
float frameSeconds = DefenseTowerArt.FrameSeconds(FlightKey, i);
|
||||
float frameElapsed = 0f;
|
||||
while (frameElapsed < frameSeconds)
|
||||
{
|
||||
CurrentMeteorHeight = flightHeight * (1f - elapsed / flightDuration);
|
||||
meteor.transform.position = ProjectGroundAndHeight(CurrentMeteorGround, CurrentMeteorHeight);
|
||||
float dt = Mathf.Min(Time.deltaTime, frameSeconds - frameElapsed);
|
||||
frameElapsed += dt;
|
||||
elapsed += dt;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
CurrentMeteorHeight = 0f;
|
||||
meteor.transform.position = ProjectGroundAndHeight(CurrentMeteorGround, 0f);
|
||||
for (int i = 0; i < meteorImpactFrames.Length; i++)
|
||||
{
|
||||
meteor.sprite = meteorImpactFrames[i];
|
||||
yield return new WaitForSeconds(DefenseTowerArt.FrameSeconds(ImpactKey, i));
|
||||
}
|
||||
meteor.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c00de25d48634556853f7a2df8445f18
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BumpCombat.Defense
|
||||
{
|
||||
public static class DefenseTowerArt
|
||||
{
|
||||
private const string ResourceRoot = "DefenseTowers-v1/";
|
||||
private static readonly Dictionary<string, Sprite[]> Frames = new();
|
||||
|
||||
public static ICollection<string> Keys => DefenseTowerArtDurations.Keys;
|
||||
|
||||
public static int ExpectedFrameCount(string key)
|
||||
{
|
||||
return DefenseTowerArtDurations.FrameCount(key);
|
||||
}
|
||||
|
||||
public static Sprite[] Load(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
return Array.Empty<Sprite>();
|
||||
}
|
||||
|
||||
if (!Frames.TryGetValue(key, out Sprite[] sprites))
|
||||
{
|
||||
sprites = Resources.LoadAll<Sprite>(ResourceRoot + key);
|
||||
Array.Sort(sprites, CompareFrameNames);
|
||||
Frames.Add(key, sprites);
|
||||
}
|
||||
return sprites;
|
||||
}
|
||||
|
||||
public static float FrameSeconds(string key, int frame)
|
||||
{
|
||||
return DefenseTowerArtDurations.Seconds(key, frame);
|
||||
}
|
||||
|
||||
public static string BodyKey(DefenseTowerKind kind, int tier, string motion)
|
||||
{
|
||||
string family = kind switch
|
||||
{
|
||||
DefenseTowerKind.Direct => "Fire",
|
||||
DefenseTowerKind.Summon => "Summon",
|
||||
DefenseTowerKind.Lightning => "Lightning",
|
||||
DefenseTowerKind.Cold => "Ice",
|
||||
DefenseTowerKind.Seismic => "Control",
|
||||
_ => "Support",
|
||||
};
|
||||
return $"Bodies/{family}/T{tier}-{motion}";
|
||||
}
|
||||
|
||||
private static int CompareFrameNames(Sprite left, Sprite right)
|
||||
{
|
||||
return FrameIndex(left.name).CompareTo(FrameIndex(right.name));
|
||||
}
|
||||
|
||||
private static int FrameIndex(string name)
|
||||
{
|
||||
int separator = name.LastIndexOf('_');
|
||||
return separator >= 0 && int.TryParse(name.Substring(separator + 1), out int index)
|
||||
? index : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3608a711d7f74976b57e54ef7981c28a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
Reference in New Issue
Block a user