using TMPro; using UnityEngine; public class ItemFeedbackEffect : MonoBehaviour { private const string SortingLayerName = "Foreground"; private const float Lifetime = 0.85f; private static Sprite ringSprite; private static Sprite dotSprite; private static Sprite sparkleSprite; private SpriteRenderer ringRenderer; private SpriteRenderer iconRenderer; private SpriteRenderer[] sparkleRenderers; private TextMeshPro labelText; private Color mainColor; private float elapsedTime; public static void Spawn(Vector3 worldPosition, Sprite itemSprite, GameManager.TutorialItemType itemType, bool blockedDamage) { GameObject effectObject = new GameObject(blockedDamage ? "Item Block Feedback" : "Item Pickup Feedback"); effectObject.transform.position = worldPosition + new Vector3(0f, blockedDamage ? 1.1f : 0.85f, 0f); ItemFeedbackEffect effect = effectObject.AddComponent(); effect.Initialize(itemSprite, itemType, blockedDamage); } private void Initialize(Sprite itemSprite, GameManager.TutorialItemType itemType, bool blockedDamage) { mainColor = GetItemColor(itemType, blockedDamage); sparkleRenderers = new SpriteRenderer[5]; ringRenderer = CreateSpriteChild("Ring", GetRingSprite(), Vector3.zero, 1.4f, 28); iconRenderer = CreateSpriteChild("Icon", itemSprite != null ? itemSprite : GetDotSprite(), new Vector3(0f, 0.03f, 0f), itemSprite != null ? 0.52f : 0.72f, 29); labelText = CreateLabel(GetLabel(itemType, blockedDamage)); for(int i = 0; i < sparkleRenderers.Length; i++) { float angle = (Mathf.PI * 2f / sparkleRenderers.Length) * i; Vector3 localPosition = new Vector3(Mathf.Cos(angle) * 0.58f, Mathf.Sin(angle) * 0.38f, 0f); sparkleRenderers[i] = CreateSpriteChild("Sparkle " + i, GetSparkleSprite(), localPosition, 0.18f, 30); } ApplyAlpha(1f); } private void Update() { elapsedTime += Time.unscaledDeltaTime; float t = Mathf.Clamp01(elapsedTime / Lifetime); float fade = 1f - Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(0.55f, 1f, t)); float popScale = Mathf.Lerp(0.72f, 1.16f, EaseOut(t)); transform.localScale = Vector3.one * popScale; transform.position += Vector3.up * (Time.unscaledDeltaTime * 0.28f); if(ringRenderer != null) { ringRenderer.transform.localScale = Vector3.one * Mathf.Lerp(0.82f, 1.38f, EaseOut(t)); } for(int i = 0; i < sparkleRenderers.Length; i++) { if(sparkleRenderers[i] == null) { continue; } sparkleRenderers[i].transform.localScale = Vector3.one * Mathf.Lerp(0.16f, 0.32f, EaseOut(t)); sparkleRenderers[i].transform.Rotate(Vector3.forward, 180f * Time.unscaledDeltaTime); } ApplyAlpha(fade); if(elapsedTime >= Lifetime) { Destroy(gameObject); } } private SpriteRenderer CreateSpriteChild(string objectName, Sprite sprite, Vector3 localPosition, float scale, int sortingOrder) { GameObject child = new GameObject(objectName, typeof(SpriteRenderer)); child.transform.SetParent(transform, false); child.transform.localPosition = localPosition; child.transform.localScale = Vector3.one * scale; SpriteRenderer spriteRenderer = child.GetComponent(); spriteRenderer.sprite = sprite; spriteRenderer.color = mainColor; spriteRenderer.sortingLayerName = SortingLayerName; spriteRenderer.sortingOrder = sortingOrder; return spriteRenderer; } private TextMeshPro CreateLabel(string text) { GameObject textObject = new GameObject("Label", typeof(TextMeshPro)); textObject.transform.SetParent(transform, false); textObject.transform.localPosition = new Vector3(0f, 0.62f, 0f); textObject.transform.localScale = Vector3.one * 0.28f; TextMeshPro textMesh = textObject.GetComponent(); textMesh.text = text; textMesh.alignment = TextAlignmentOptions.Center; textMesh.fontSize = 3.2f; textMesh.fontStyle = FontStyles.Bold; textMesh.color = Color.white; textMesh.textWrappingMode = TextWrappingModes.NoWrap; MeshRenderer textRenderer = textMesh.GetComponent(); textRenderer.sortingLayerName = SortingLayerName; textRenderer.sortingOrder = 31; return textMesh; } private void ApplyAlpha(float alpha) { if(ringRenderer != null) { ringRenderer.color = WithAlpha(mainColor, alpha * 0.72f); } if(iconRenderer != null) { iconRenderer.color = WithAlpha(Color.white, alpha); } if(labelText != null) { labelText.color = WithAlpha(Color.white, alpha); } if(sparkleRenderers == null) { return; } for(int i = 0; i < sparkleRenderers.Length; i++) { if(sparkleRenderers[i] != null) { sparkleRenderers[i].color = WithAlpha(mainColor, alpha); } } } private static string GetLabel(GameManager.TutorialItemType itemType, bool blockedDamage) { if(blockedDamage) { return itemType == GameManager.TutorialItemType.InvincibleRamen ? "SAFE" : "BLOCK"; } switch(itemType) { case GameManager.TutorialItemType.InvincibleRamen: return "SAFE"; case GameManager.TutorialItemType.Shield: return "SHIELD"; case GameManager.TutorialItemType.SpeedShoes: return "SPEED"; case GameManager.TutorialItemType.MileageCard: return "x2"; case GameManager.TutorialItemType.HealthSushi: default: return "+1"; } } private static Color GetItemColor(GameManager.TutorialItemType itemType, bool blockedDamage) { if(blockedDamage) { return itemType == GameManager.TutorialItemType.InvincibleRamen ? new Color(1f, 0.76f, 0.24f, 1f) : new Color(0.36f, 0.82f, 1f, 1f); } switch(itemType) { case GameManager.TutorialItemType.InvincibleRamen: return new Color(1f, 0.58f, 0.12f, 1f); case GameManager.TutorialItemType.Shield: return new Color(0.32f, 0.78f, 1f, 1f); case GameManager.TutorialItemType.SpeedShoes: return new Color(0.35f, 1f, 0.5f, 1f); case GameManager.TutorialItemType.MileageCard: return new Color(1f, 0.92f, 0.26f, 1f); case GameManager.TutorialItemType.HealthSushi: default: return new Color(1f, 0.34f, 0.42f, 1f); } } private static float EaseOut(float value) { return 1f - Mathf.Pow(1f - Mathf.Clamp01(value), 3f); } private static Color WithAlpha(Color color, float alpha) { return new Color(color.r, color.g, color.b, Mathf.Clamp01(alpha)); } private static Sprite GetRingSprite() { if(ringSprite == null) { ringSprite = CreateRingSprite(64, 21f, 29f); } return ringSprite; } private static Sprite GetDotSprite() { if(dotSprite == null) { dotSprite = CreateDiscSprite(48, 19f); } return dotSprite; } private static Sprite GetSparkleSprite() { if(sparkleSprite == null) { sparkleSprite = CreateSparkleSprite(24); } return sparkleSprite; } private static Sprite CreateRingSprite(int size, float innerRadius, float outerRadius) { Texture2D texture = CreateTexture(size); Color[] pixels = new Color[size * size]; Vector2 center = new Vector2((size - 1) * 0.5f, (size - 1) * 0.5f); for(int y = 0; y < size; y++) { for(int x = 0; x < size; x++) { float distance = Vector2.Distance(new Vector2(x, y), center); float outerAlpha = 1f - Mathf.SmoothStep(outerRadius - 2f, outerRadius, distance); float innerAlpha = Mathf.SmoothStep(innerRadius - 2f, innerRadius, distance); pixels[y * size + x] = new Color(1f, 1f, 1f, outerAlpha * innerAlpha); } } texture.SetPixels(pixels); texture.Apply(); return Sprite.Create(texture, new Rect(0f, 0f, size, size), new Vector2(0.5f, 0.5f), size); } private static Sprite CreateDiscSprite(int size, float radius) { Texture2D texture = CreateTexture(size); Color[] pixels = new Color[size * size]; Vector2 center = new Vector2((size - 1) * 0.5f, (size - 1) * 0.5f); for(int y = 0; y < size; y++) { for(int x = 0; x < size; x++) { float distance = Vector2.Distance(new Vector2(x, y), center); float alpha = 1f - Mathf.SmoothStep(radius - 2f, radius, distance); pixels[y * size + x] = new Color(1f, 1f, 1f, alpha); } } texture.SetPixels(pixels); texture.Apply(); return Sprite.Create(texture, new Rect(0f, 0f, size, size), new Vector2(0.5f, 0.5f), size); } private static Sprite CreateSparkleSprite(int size) { Texture2D texture = CreateTexture(size); Color[] pixels = new Color[size * size]; float center = (size - 1) * 0.5f; for(int y = 0; y < size; y++) { for(int x = 0; x < size; x++) { float horizontal = Mathf.Abs(y - center); float vertical = Mathf.Abs(x - center); float diagonalA = Mathf.Abs((x - center) - (y - center)); float diagonalB = Mathf.Abs((x - center) + (y - center)); float alpha = Mathf.Max( 1f - horizontal * 0.42f - Mathf.Abs(x - center) * 0.08f, 1f - vertical * 0.42f - Mathf.Abs(y - center) * 0.08f); alpha = Mathf.Max(alpha, 0.62f - Mathf.Min(diagonalA, diagonalB) * 0.28f); pixels[y * size + x] = new Color(1f, 1f, 1f, Mathf.Clamp01(alpha)); } } texture.SetPixels(pixels); texture.Apply(); return Sprite.Create(texture, new Rect(0f, 0f, size, size), new Vector2(0.5f, 0.5f), size); } private static Texture2D CreateTexture(int size) { Texture2D texture = new Texture2D(size, size, TextureFormat.RGBA32, false); texture.wrapMode = TextureWrapMode.Clamp; texture.filterMode = FilterMode.Bilinear; texture.hideFlags = HideFlags.HideAndDontSave; return texture; } }