-
-
Save thsbrown/cbc8f429dc01c06bae417295e53ebfbe to your computer and use it in GitHub Desktop.
Display a emanating ring from a given position in Unity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using Cysharp.Threading.Tasks; | |
using DG.Tweening; | |
using Lean.Touch; | |
using Shapes; | |
using Sirenix.OdinInspector; | |
using StudioName.Runtime.ExtensionAndHelpers; | |
using StudioName.Runtime.Utility; | |
using UnityEngine; | |
using UnityEngine.Rendering; | |
public class InteractionRing : ImmediateModeShapeDrawer | |
{ | |
/// <summary> | |
/// The color of our ring | |
/// </summary> | |
[Tooltip("The color of our ring")] | |
[Title("Configure Ring")] | |
public Color color; | |
/// <summary> | |
/// The thickness of our ring | |
/// </summary> | |
[Tooltip("The thickness of our ring")] | |
public float thickness; | |
/// <summary> | |
/// The starting and ending range we will use for our radius when animating | |
/// </summary> | |
[Title("Configure Animation")] | |
[Tooltip("The starting and ending range we will use for our radius when animating")] | |
[MinMaxSlider(0, 20,true)] | |
public Vector2 radiusRange; | |
/// <summary> | |
/// The speed of our ring animation | |
/// </summary> | |
[Tooltip("The speed of our ring animation")] | |
public float animationSpeed; | |
/// <summary> | |
/// The percent completion time that the new ring will be created and begin animating at | |
/// </summary> | |
[Tooltip("The percent completion time that the new ring will be created and begin animating at")] | |
[Range(0,1)] | |
public float secondStartsAt; | |
/// <summary> | |
/// The easing curve we will use when animating our ring | |
/// </summary> | |
[Tooltip("The easing curve we will use when animating our ring")] | |
public Ease ease; | |
/// <summary> | |
/// The rings we are currently animating | |
/// </summary> | |
private List<AnimatedRing> animatedRings; | |
private void Start() | |
{ | |
StartRingAnimation(); | |
} | |
private void OnDestroy() | |
{ | |
StopRingAnimation(); | |
} | |
public override void DrawShapes(Camera camera) | |
{ | |
//only render if we are either main camera or scene view camera (otherwise we won't see shape in scene view) | |
if (camera != Camera.main && camera.cameraType != CameraType.SceneView) | |
{ | |
return; | |
} | |
using(Draw.Command(camera,CameraEvent.BeforeForwardAlpha)) | |
{ | |
foreach (var shapesRing in animatedRings) | |
{ | |
shapesRing.Draw(); | |
} | |
} | |
} | |
/// <summary> | |
/// Restarts our ring animation | |
/// </summary> | |
[TitleGroup("Utility")] | |
[ButtonGroup("Utility/Methods")] | |
public async UniTask StartRingAnimation() | |
{ | |
IsRingsAnimating = true; | |
animatedRings = new List<AnimatedRing>(); | |
while (IsRingsAnimating) | |
{ | |
var shapeRing = new AnimatedRing(); | |
shapeRing.ConfigureRing(transform,thickness,color); | |
shapeRing.ConfigureAnimation(radiusRange,animationSpeed,ease); | |
animatedRings.Add(shapeRing); | |
var tween = shapeRing.Play(); | |
tween.OnComplete(() => | |
{ | |
animatedRings.Remove(shapeRing); | |
}); | |
var distance = 0f; | |
while (tween.IsActive() && distance < secondStartsAt) | |
{ | |
await UniTask.Yield(); | |
distance = GenUtil.MapRange(shapeRing.Radius, radiusRange, new Vector2(0, 1)); | |
} | |
//uncomment this for second to spawn utilizing animation duration instead of distance | |
//await tween.AsyncWaitForPosition(tween.Duration() * secondStartsAt).AsUniTask(); | |
} | |
} | |
/// <summary> | |
/// Stops our ring animation | |
/// </summary> | |
[ButtonGroup("Utility/Methods")] | |
public void StopRingAnimation() | |
{ | |
IsRingsAnimating = false; | |
var tweens = animatedRings.Select(x => x.LastAnimation).ToList(); | |
foreach (var tween in tweens) | |
{ | |
tween.Kill(true); | |
} | |
} | |
/// <summary> | |
/// Stop our ring animation and restarts it | |
/// </summary> | |
[ButtonGroup("Utility/Methods")] | |
public async void RestartRingAnimation() | |
{ | |
StopRingAnimation(); | |
//wait period of time for our AsyncWaitForPosition in PlayRingAnimation to complete so while loop will exit | |
await Task.Delay(TimeSpan.FromSeconds(0.01f)); | |
StartRingAnimation(); | |
} | |
/// <summary> | |
/// Is our ring animation currently playing | |
/// </summary> | |
public bool IsRingsAnimating { get; private set; } | |
private void OnDrawGizmos() | |
{ | |
Draw.Ring(transform.position,Quaternion.Euler(new Vector3(-90,0,0)),radiusRange.x,thickness,DiscColors.Flat(Color.green)); | |
Draw.Ring(transform.position,Quaternion.Euler(new Vector3(-90,0,0)),radiusRange.y,thickness,DiscColors.Flat(Color.red)); | |
var secondStartRadius = GenUtil.MapRange(secondStartsAt,new Vector2(0, 1),radiusRange); | |
Draw.Ring(transform.position,Quaternion.Euler(new Vector3(-90,0,0)),secondStartRadius,thickness,DiscColors.Flat(Color.blue)); | |
} | |
/// <summary> | |
/// The ring that we will animate outward using <see cref="DOTween"/> and <see cref="Shapes"/> | |
/// </summary> | |
private class AnimatedRing | |
{ | |
private Transform transform; | |
private float radius; | |
private float endingRadius; | |
private float thickness; | |
private Color color; | |
private float animationSpeed; | |
private Ease ease; | |
/// <summary> | |
/// Configures the properties of our ring that we will be animating | |
/// </summary> | |
/// <param name="transform">The transform position we will start our ring from</param> | |
/// <param name="thickness">The thickness of our ring</param> | |
/// <param name="color">The color of our ring</param> | |
public void ConfigureRing(Transform transform, float thickness, Color color) | |
{ | |
this.transform = transform; | |
this.thickness = thickness; | |
this.color = color; | |
this.animationSpeed = 1; | |
} | |
/// <summary> | |
/// Configures the properties of our ring animation | |
/// </summary> | |
/// <param name="radiusRange">The starting and ending radius for our animation (x = starting, y = ending)</param> | |
/// <param name="animationSpeed">The speed our animation</param> | |
/// <param name="ease">The type of ease we will use for animation</param> | |
public void ConfigureAnimation(Vector2 radiusRange, float animationSpeed, Ease ease) | |
{ | |
this.radius = radiusRange.x; | |
this.endingRadius = radiusRange.y; | |
this.animationSpeed = animationSpeed; | |
this.ease = ease; | |
} | |
/// <summary> | |
/// Draws our ring utilizing <see cref="Shapes.Draw.Ring(UnityEngine.Vector3)"/> | |
/// </summary> | |
public void Draw() | |
{ | |
Shapes.Draw.Ring(transform.position,Quaternion.Euler(new Vector3(-90,0,0)),radius,thickness,DiscColors.Flat(color)); | |
} | |
/// <summary> | |
/// Starts the animation sequence | |
/// </summary> | |
/// <returns>The tween we are using to play our animation</returns> | |
public Tween Play() | |
{ | |
var sequence = DOTween.Sequence(); | |
var radiusTween = DOTween.To( | |
() => radius, | |
(x) => radius = x, | |
endingRadius, | |
animationSpeed); | |
var colorTween = DOTween.ToAlpha( | |
() => color, | |
(x) => color = x, | |
0, | |
animationSpeed); | |
sequence.Append(radiusTween); | |
sequence.Join(colorTween); | |
sequence.SetEase(ease); | |
LastAnimation = sequence; | |
return sequence; | |
} | |
/// <summary> | |
/// The last animation / tween that was played using <see cref="Play"/> | |
/// </summary> | |
public Tween LastAnimation { get; private set; } | |
/// <summary> | |
/// The current radius of the animated ring (this will vary based on the animation elapsed time) | |
/// <remarks>Equal to starting radius when animation begins, and ending radius when animation completes</remarks> | |
/// </summary> | |
public float Radius => radius; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment