using Cysharp.Threading.Tasks; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.VFX; namespace DanieleMarotta.RiversongCodeShowcase { public class BuildingDeleteAnimation : MonoBehaviour { private static readonly int IntensityPropertyID = Shader.PropertyToID("Intensity"); private static readonly int SizePropertyID = Shader.PropertyToID("Size"); [SerializeField] [ReadOnly] private float _sinkingHeight; [SerializeField] private float _sinkingSpeed = 1; [SerializeField] private AnimationCurve _sinkingSpeedCurve = AnimationCurve.Constant(0, 1, 1); [SerializeField] private float _shakeFrequency = 10; [SerializeField] private float _shakeAmplitude = 0.1f; [SerializeField] private VisualEffect _vfxPrefab; [SerializeField] private int _vfxCount = 1; [SerializeField] private float _vfxInterval = 1; [SerializeField] private float _vfxIntensityDecay = 0.5f; private void ComputeSinkingHeight() { var bounds = default(Bounds); var renderers = GetComponentsInChildren(); foreach (var renderer in renderers) bounds.Encapsulate(renderer.bounds); _sinkingHeight = bounds.size.y; } private void Awake() { if (_sinkingHeight == 0) ComputeSinkingHeight(); } public async UniTask PlayAsync(Building building) { var initialPosition = transform.position; var animatedPosition = transform.position; var angularFrequency = 2 * Mathf.PI * _shakeFrequency; var shakeWeights = new Vector3(Random.Range(-1, 1), 0, Random.Range(-1, 1)); for (var i = 0; i < _vfxCount; i++) { var delay = i * _vfxInterval; var intensity = Mathf.Pow(_vfxIntensityDecay, i); _ = PlayVfxAsync(building, initialPosition, delay, intensity); } var t = 0f; while (animatedPosition.y > initialPosition.y - _sinkingHeight) { var dt = Time.deltaTime; t += dt; var sinkingSpeed = _sinkingSpeed * _sinkingSpeedCurve.Evaluate(t); animatedPosition += Vector3.down * (sinkingSpeed * dt); var shake = Mathf.Sin(t * angularFrequency) * _shakeAmplitude * _sinkingSpeed; transform.position = animatedPosition + shake * shakeWeights; await UniTask.NextFrame(); } } private async UniTask PlayVfxAsync(Building building, Vector3 position, float delay, float intensity) { if (delay > 0) await UniTask.WaitForSeconds(delay); var vfx = Instantiate(_vfxPrefab, position, Quaternion.identity); vfx.SetFloat(IntensityPropertyID, intensity); vfx.SetVector2(SizePropertyID, new Vector2(building.Rect.Width, building.Rect.Height)); } private void OnValidate() { ComputeSinkingHeight(); } } }