using System; using System.Collections; using UnityEngine; namespace Assets.Scripts.Utils { [RequireComponent(typeof(Projector))] internal sealed class ProjectorTextureAnimation : MonoBehaviour { public int TilesX = 4; public int TilesY = 4; public float Fps = 30; public AnimationCurve FrameOverTime = AnimationCurve.Linear(0, 1, 1, 1); private Projector _projector; private Material _projectorMaterial; private Material _originalMaterial; private float _animationLifeTime; private int _index; private int _countOfTextures, _currentNumOfTexture; private bool _isStarted; private bool _isCanceled; private float _animationStartTime; private void Start() { _projector = GetComponent<Projector>(); if (_projector == null) throw new InvalidOperationException("Could not get projector"); _animationLifeTime = TilesX * TilesY / Fps; _index = TilesX - 1; _countOfTextures = TilesY * TilesX; Play(); } private void Play() { if (_isStarted) return; if (_projector == null || _projector.material == null) return; _originalMaterial = _projector.material; _projectorMaterial = Instantiate(_projector.material); _projector.material = _projectorMaterial; if (_originalMaterial == null) return; var offset = Vector3.zero; var size = new Vector2(1f / TilesX, 1f / TilesY); _projectorMaterial.SetTextureScale("_MainTex", size); _projectorMaterial.SetTextureOffset("_MainTex", offset); _isStarted = true; StartCoroutine(UpdateCorutine()); } private IEnumerator UpdateCorutine() { _animationStartTime = Time.time; while (!_isCanceled) { UpdateFrame(); var frameTime = (Time.time - _animationStartTime) / _animationLifeTime; var currentSpeedFps = FrameOverTime.Evaluate(Mathf.Clamp01(frameTime)); yield return new WaitForSeconds(1f / (Fps * currentSpeedFps)); } } private void UpdateFrame() { ++_currentNumOfTexture; ++_index; if (_index >= _countOfTextures) _index = 0; if (_currentNumOfTexture == _countOfTextures) { _animationStartTime = Time.time; _currentNumOfTexture = 0; } var offset = new Vector2((float)_index / TilesX - (int)(_index / TilesX), 1 - (int)(_index / TilesX) / (float)TilesY); if (_projector != null) _projectorMaterial.SetTextureOffset("_MainTex", offset); } private void Stop() { _isStarted = false; _isCanceled = true; StopAllCoroutines(); _projector.material = _originalMaterial; //_projectorMaterial.SetTextureScale("_MainTex", new Vector2(1, 1)); //_projectorMaterial.SetTextureOffset("_MainTex", new Vector2(0, 0)); } private void OnEnable() { Play(); } private void OnDisable() { Stop(); } private void OnDestroy() { Stop(); } } }