ExampleTrack.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using UnityEngine;
  6. namespace SplineMesh {
  7. /// <summary>
  8. /// Example of component to bend many meshes along a spline. This component can be used as-is but will most likely be a base for your own component.
  9. ///
  10. /// This is a more advanced and real-life SplineMesh component. Use it as a source of inspiration.
  11. ///
  12. /// In this script, you will learn to :
  13. /// - preserve baked lightmap when entering playmode,
  14. /// - better manage the generated content life cycle to avoid useless calculations
  15. /// - create data class to produce richer content along your spline
  16. ///
  17. /// This is the most complete Example provided in the asset. For further help, information and ideas, please visit
  18. /// the officiel thread on Unity forum.
  19. ///
  20. /// And if you like SplineMesh, please review it on the asset store !
  21. ///
  22. /// Now you should be able to bend the world to your will.
  23. ///
  24. /// Have fun with SplineMesh !
  25. ///
  26. /// </summary>
  27. [ExecuteInEditMode]
  28. [SelectionBase]
  29. [DisallowMultipleComponent]
  30. public class ExampleTrack : MonoBehaviour {
  31. private GameObject generated;
  32. private Spline spline = null;
  33. private bool toUpdate = false;
  34. /// <summary>
  35. /// A list of object that are storing data for each segment of the curve.
  36. /// </summary>
  37. public List<TrackSegment> segments = new List<TrackSegment>();
  38. /// <summary>
  39. /// If true, the generated content will be updated in play mode.
  40. /// If false, the content generated and saved to the scene will be used in playmode without modification.
  41. /// Usefull to preserve lightmaps baked for static objects.
  42. /// </summary>
  43. public bool updateInPlayMode;
  44. private void OnEnable() {
  45. string generatedName = "generated by " + GetType().Name;
  46. var generatedTranform = transform.Find(generatedName);
  47. generated = generatedTranform != null ? generatedTranform.gameObject : UOUtility.Create(generatedName, gameObject);
  48. spline = GetComponentInParent<Spline>();
  49. // we listen changes in the spline's node list and we update the list of segment accordingly
  50. // this way, if we insert a node between two others, a segment will be inserted too and the data won't shift
  51. while (segments.Count < spline.nodes.Count) {
  52. segments.Add(new TrackSegment());
  53. }
  54. while (segments.Count > spline.nodes.Count) {
  55. segments.RemoveAt(segments.Count - 1);
  56. }
  57. spline.NodeListChanged += (s, e) => {
  58. switch (e.type) {
  59. case ListChangeType.Add:
  60. segments.Add(new TrackSegment());
  61. break;
  62. case ListChangeType.Remove:
  63. segments.RemoveAt(e.removeIndex);
  64. break;
  65. case ListChangeType.Insert:
  66. segments.Insert(e.insertIndex, new TrackSegment());
  67. break;
  68. }
  69. toUpdate = true;
  70. };
  71. toUpdate = true;
  72. }
  73. private void OnValidate() {
  74. if (spline == null) return;
  75. toUpdate = true;
  76. }
  77. private void Update() {
  78. // we can prevent the generated content to be updated during playmode to preserve baked data saved in the scene
  79. if (!updateInPlayMode && Application.isPlaying) return;
  80. if (toUpdate) {
  81. toUpdate = false;
  82. CreateMeshes();
  83. }
  84. }
  85. public void CreateMeshes() {
  86. List<GameObject> used = new List<GameObject>();
  87. for (int i = 0; i < spline.GetCurves().Count; i++) {
  88. var curve = spline.GetCurves()[i];
  89. foreach (var tm in segments[i].transformedMeshes) {
  90. if (tm.mesh == null) {
  91. // if there is no mesh specified for this segment, we ignore it.
  92. continue;
  93. }
  94. // we try to find a game object previously generated. this avoids destroying/creating
  95. // game objects at each update, wich is faster.
  96. var childName = "segment " + i + " mesh " + segments[i].transformedMeshes.IndexOf(tm);
  97. var childTransform = generated.transform.Find(childName);
  98. GameObject go;
  99. if (childTransform == null) {
  100. go = UOUtility.Create(childName,
  101. generated,
  102. typeof(MeshFilter),
  103. typeof(MeshRenderer),
  104. typeof(MeshBender),
  105. typeof(MeshCollider));
  106. go.isStatic = true;
  107. } else {
  108. go = childTransform.gameObject;
  109. }
  110. go.GetComponent<MeshRenderer>().material = tm.material;
  111. go.GetComponent<MeshCollider>().material = tm.physicMaterial;
  112. // we update the data in the bender. It will decide itself if the bending must be recalculated.
  113. MeshBender mb = go.GetComponent<MeshBender>();
  114. mb.Source = SourceMesh.Build(tm.mesh)
  115. .Translate(tm.translation)
  116. .Rotate(Quaternion.Euler(tm.rotation))
  117. .Scale(tm.scale);
  118. mb.SetInterval(curve);
  119. mb.ComputeIfNeeded();
  120. used.Add(go);
  121. }
  122. }
  123. // finally, we destroy the unused objects
  124. foreach (var go in generated.transform
  125. .Cast<Transform>()
  126. .Select(child => child.gameObject).Except(used)) {
  127. UOUtility.Destroy(go);
  128. }
  129. }
  130. }
  131. /// <summary>
  132. /// This class store any data associated with a spline segment.
  133. /// In this example, a list of meshes.
  134. /// It is intended to be edited in the inspector.
  135. /// </summary>
  136. [Serializable]
  137. public class TrackSegment {
  138. public List<TransformedMesh> transformedMeshes = new List<TransformedMesh>();
  139. }
  140. /// <summary>
  141. /// This class stores all needed data to represent a mesh in situation.
  142. /// It is intended to be edited in the inspector.
  143. /// </summary>
  144. [Serializable]
  145. public class TransformedMesh {
  146. public TransformedMesh() {
  147. scale = Vector3.one;
  148. }
  149. public Mesh mesh;
  150. public Material material;
  151. public PhysicMaterial physicMaterial;
  152. public Vector3 translation;
  153. public Vector3 rotation;
  154. public Vector3 scale = Vector3.one;
  155. }
  156. }