123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using UnityEngine;
- namespace SplineMesh {
- /// <summary>
- /// 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.
- ///
- /// This is a more advanced and real-life SplineMesh component. Use it as a source of inspiration.
- ///
- /// In this script, you will learn to :
- /// - preserve baked lightmap when entering playmode,
- /// - better manage the generated content life cycle to avoid useless calculations
- /// - create data class to produce richer content along your spline
- ///
- /// This is the most complete Example provided in the asset. For further help, information and ideas, please visit
- /// the officiel thread on Unity forum.
- ///
- /// And if you like SplineMesh, please review it on the asset store !
- ///
- /// Now you should be able to bend the world to your will.
- ///
- /// Have fun with SplineMesh !
- ///
- /// </summary>
- [ExecuteInEditMode]
- [SelectionBase]
- [DisallowMultipleComponent]
- public class ExampleTrack : MonoBehaviour {
- private GameObject generated;
- private Spline spline = null;
- private bool toUpdate = false;
- /// <summary>
- /// A list of object that are storing data for each segment of the curve.
- /// </summary>
- public List<TrackSegment> segments = new List<TrackSegment>();
- /// <summary>
- /// If true, the generated content will be updated in play mode.
- /// If false, the content generated and saved to the scene will be used in playmode without modification.
- /// Usefull to preserve lightmaps baked for static objects.
- /// </summary>
- public bool updateInPlayMode;
- private void OnEnable() {
- string generatedName = "generated by " + GetType().Name;
- var generatedTranform = transform.Find(generatedName);
- generated = generatedTranform != null ? generatedTranform.gameObject : UOUtility.Create(generatedName, gameObject);
- spline = GetComponentInParent<Spline>();
- // we listen changes in the spline's node list and we update the list of segment accordingly
- // this way, if we insert a node between two others, a segment will be inserted too and the data won't shift
- while (segments.Count < spline.nodes.Count) {
- segments.Add(new TrackSegment());
- }
- while (segments.Count > spline.nodes.Count) {
- segments.RemoveAt(segments.Count - 1);
- }
- spline.NodeListChanged += (s, e) => {
- switch (e.type) {
- case ListChangeType.Add:
- segments.Add(new TrackSegment());
- break;
- case ListChangeType.Remove:
- segments.RemoveAt(e.removeIndex);
- break;
- case ListChangeType.Insert:
- segments.Insert(e.insertIndex, new TrackSegment());
- break;
- }
- toUpdate = true;
- };
- toUpdate = true;
- }
- private void OnValidate() {
- if (spline == null) return;
- toUpdate = true;
- }
- private void Update() {
- // we can prevent the generated content to be updated during playmode to preserve baked data saved in the scene
- if (!updateInPlayMode && Application.isPlaying) return;
- if (toUpdate) {
- toUpdate = false;
- CreateMeshes();
- }
- }
- public void CreateMeshes() {
- List<GameObject> used = new List<GameObject>();
- for (int i = 0; i < spline.GetCurves().Count; i++) {
- var curve = spline.GetCurves()[i];
- foreach (var tm in segments[i].transformedMeshes) {
- if (tm.mesh == null) {
- // if there is no mesh specified for this segment, we ignore it.
- continue;
- }
- // we try to find a game object previously generated. this avoids destroying/creating
- // game objects at each update, wich is faster.
- var childName = "segment " + i + " mesh " + segments[i].transformedMeshes.IndexOf(tm);
- var childTransform = generated.transform.Find(childName);
- GameObject go;
- if (childTransform == null) {
- go = UOUtility.Create(childName,
- generated,
- typeof(MeshFilter),
- typeof(MeshRenderer),
- typeof(MeshBender),
- typeof(MeshCollider));
- go.isStatic = true;
- } else {
- go = childTransform.gameObject;
- }
- go.GetComponent<MeshRenderer>().material = tm.material;
- go.GetComponent<MeshCollider>().material = tm.physicMaterial;
- // we update the data in the bender. It will decide itself if the bending must be recalculated.
- MeshBender mb = go.GetComponent<MeshBender>();
- mb.Source = SourceMesh.Build(tm.mesh)
- .Translate(tm.translation)
- .Rotate(Quaternion.Euler(tm.rotation))
- .Scale(tm.scale);
- mb.SetInterval(curve);
- mb.ComputeIfNeeded();
- used.Add(go);
- }
- }
- // finally, we destroy the unused objects
- foreach (var go in generated.transform
- .Cast<Transform>()
- .Select(child => child.gameObject).Except(used)) {
- UOUtility.Destroy(go);
- }
- }
- }
- /// <summary>
- /// This class store any data associated with a spline segment.
- /// In this example, a list of meshes.
- /// It is intended to be edited in the inspector.
- /// </summary>
- [Serializable]
- public class TrackSegment {
- public List<TransformedMesh> transformedMeshes = new List<TransformedMesh>();
- }
- /// <summary>
- /// This class stores all needed data to represent a mesh in situation.
- /// It is intended to be edited in the inspector.
- /// </summary>
- [Serializable]
- public class TransformedMesh {
- public TransformedMesh() {
- scale = Vector3.one;
- }
- public Mesh mesh;
- public Material material;
- public PhysicMaterial physicMaterial;
- public Vector3 translation;
- public Vector3 rotation;
- public Vector3 scale = Vector3.one;
- }
- }
|