AnimateTiledTexture.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. public class AnimateTiledTexture : MonoBehaviour
  5. {
  6. public int _columns = 2; // The number of columns of the texture
  7. public int _rows = 2; // The number of rows of the texture
  8. public Vector2 _scale = new Vector3(1f, 1f); // Scale the texture. This must be a non-zero number. negative scale flips the image
  9. public Vector2 _offset = Vector2.zero; // You can use this if you do not want the texture centered. (These are very small numbers .001)
  10. public Vector2 _buffer = Vector2.zero; // You can use this to buffer frames to hide unwanted grid lines or artifacts
  11. public float _framesPerSecond = 10f; // Frames per second that you want to texture to play at
  12. public bool _playOnce = false; // Enable this if you want the animation to only play one time
  13. public bool _disableUponCompletion = false; // Enable this if you want the texture to disable the renderer when it is finished playing
  14. public bool _enableEvents = false; // Enable this if you want to register an event that fires when the animation is finished playing
  15. public bool _playOnEnable = true; // The animation will play when the object is enabled
  16. public bool _newMaterialInstance = false; // Set this to true if you want to create a new material instance
  17. private int _index = 0; // Keeps track of the current frame
  18. private Vector2 _textureSize = Vector2.zero; // Keeps track of the texture scale
  19. private Material _materialInstance = null; // Material instance of the material we create (if needed)
  20. private bool _hasMaterialInstance = false; // A flag so we know if we have a material instance we need to clean up (better than a null check i think)
  21. private bool _isPlaying = false; // A flag to determine if the animation is currently playing
  22. public delegate void VoidEvent(); // The Event delegate
  23. private List<VoidEvent> _voidEventCallbackList; // A list of functions we need to call if events are enabled
  24. // Use this function to register your callback function with this script
  25. public void RegisterCallback(VoidEvent cbFunction)
  26. {
  27. // If events are enabled, add the callback function to the event list
  28. if (_enableEvents)
  29. _voidEventCallbackList.Add(cbFunction);
  30. else
  31. Debug.LogWarning("AnimateTiledTexture: You are attempting to register a callback but the events of this object are not enabled!");
  32. }
  33. // Use this function to unregister a callback function with this script
  34. public void UnRegisterCallback(VoidEvent cbFunction)
  35. {
  36. // If events are enabled, unregister the callback function from the event list
  37. if (_enableEvents)
  38. _voidEventCallbackList.Remove(cbFunction);
  39. else
  40. Debug.LogWarning("AnimateTiledTexture: You are attempting to un-register a callback but the events of this object are not enabled!");
  41. }
  42. public void Play()
  43. {
  44. // If the animation is playing, stop it
  45. if (_isPlaying)
  46. {
  47. StopCoroutine("updateTiling");
  48. _isPlaying = false;
  49. }
  50. // Make sure the renderer is enabled
  51. GetComponent<Renderer>().enabled = true;
  52. //Because of the way textures calculate the y value, we need to start at the max y value
  53. _index = _columns;
  54. // Start the update tiling coroutine
  55. StartCoroutine(updateTiling());
  56. }
  57. public void ChangeMaterial(Material newMaterial, bool newInstance = false)
  58. {
  59. if (newInstance)
  60. {
  61. // First check our material instance, if we already have a material instance
  62. // and we want to create a new one, we need to clean up the old one
  63. if (_hasMaterialInstance)
  64. Object.Destroy(GetComponent<Renderer>().sharedMaterial);
  65. // create the new material
  66. _materialInstance = new Material(newMaterial);
  67. // Assign it to the renderer
  68. GetComponent<Renderer>().sharedMaterial = _materialInstance;
  69. // Set the flag
  70. _hasMaterialInstance = true;
  71. }
  72. else // if we dont have create a new instance, just assign the texture
  73. GetComponent<Renderer>().sharedMaterial = newMaterial;
  74. // We need to recalc the texture size (since different material = possible different texture)
  75. CalcTextureSize();
  76. // Assign the new texture size
  77. GetComponent<Renderer>().sharedMaterial.SetTextureScale("_MainTex", _textureSize);
  78. }
  79. private void Awake()
  80. {
  81. // Allocate memory for the events, if needed
  82. if (_enableEvents)
  83. _voidEventCallbackList = new List<VoidEvent>();
  84. //Create the material instance, if needed. else, just use this function to recalc the texture size
  85. ChangeMaterial(GetComponent<Renderer>().sharedMaterial, _newMaterialInstance);
  86. }
  87. private void OnDestroy()
  88. {
  89. // If we wanted new material instances, we need to destroy the material
  90. if (_hasMaterialInstance)
  91. {
  92. Object.Destroy(GetComponent<Renderer>().sharedMaterial);
  93. _hasMaterialInstance = false;
  94. }
  95. }
  96. // Handles all event triggers to callback functions
  97. private void HandleCallbacks(List<VoidEvent> cbList)
  98. {
  99. // For now simply loop through them all and call yet.
  100. for (int i = 0; i < cbList.Count; ++i)
  101. cbList[i]();
  102. }
  103. private void OnEnable()
  104. {
  105. CalcTextureSize();
  106. if (_playOnEnable)
  107. Play();
  108. }
  109. private void CalcTextureSize()
  110. {
  111. //set the tile size of the texture (in UV units), based on the rows and columns
  112. _textureSize = new Vector2(1f / _columns, 1f / _rows);
  113. // Add in the scale
  114. _textureSize.x = _textureSize.x / _scale.x;
  115. _textureSize.y = _textureSize.y / _scale.y;
  116. // Buffer some of the image out (removes gridlines and stufF)
  117. _textureSize -= _buffer;
  118. }
  119. // The main update function of this script
  120. private IEnumerator updateTiling()
  121. {
  122. _isPlaying = true;
  123. // This is the max number of frames
  124. int checkAgainst = (_rows * _columns);
  125. while (true)
  126. {
  127. // If we are at the last frame, we need to either loop or break out of the loop
  128. if (_index >= checkAgainst)
  129. {
  130. _index = 0; // Reset the index
  131. // If we only want to play the texture one time
  132. if (_playOnce)
  133. {
  134. if (checkAgainst == _columns)
  135. {
  136. // We are done with the coroutine. Fire the event, if needed
  137. if(_enableEvents)
  138. HandleCallbacks(_voidEventCallbackList);
  139. if (_disableUponCompletion)
  140. gameObject.GetComponent<Renderer>().enabled = false;
  141. // turn off the isplaying flag
  142. _isPlaying = false;
  143. // Break out of the loop, we are finished
  144. yield break;
  145. }
  146. else
  147. checkAgainst = _columns; // We need to loop through one more row
  148. }
  149. }
  150. // Apply the offset in order to move to the next frame
  151. ApplyOffset();
  152. //Increment the index
  153. _index++;
  154. // Wait a time before we move to the next frame. Note, this gives unexpected results on mobile devices
  155. yield return new WaitForSeconds(1f / _framesPerSecond);
  156. }
  157. }
  158. private void ApplyOffset()
  159. {
  160. //split into x and y indexes. calculate the new offsets
  161. Vector2 offset = new Vector2((float)_index / _columns - (_index / _columns), //x index
  162. 1 - ((_index / _columns) / (float)_rows)); //y index
  163. // Reset the y offset, if needed
  164. if (offset.y == 1)
  165. offset.y = 0.0f;
  166. // If we have scaled the texture, we need to reposition the texture to the center of the object
  167. offset.x += ((1f / _columns) - _textureSize.x) / 2.0f;
  168. offset.y += ((1f / _rows) - _textureSize.y) / 2.0f;
  169. // Add an additional offset if the user does not want the texture centered
  170. offset.x += _offset.x;
  171. offset.y += _offset.y;
  172. // Update the material
  173. GetComponent<Renderer>().sharedMaterial.SetTextureOffset("_MainTex", offset);
  174. }
  175. }