using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace SplineMesh {
///
/// 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 !
///
///
[ExecuteInEditMode]
[SelectionBase]
[DisallowMultipleComponent]
public class ExampleTrack : MonoBehaviour {
private GameObject generated;
private Spline spline = null;
private bool toUpdate = false;
///
/// A list of object that are storing data for each segment of the curve.
///
public List segments = new List();
///
/// 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.
///
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();
// 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 used = new List();
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().material = tm.material;
go.GetComponent().material = tm.physicMaterial;
// we update the data in the bender. It will decide itself if the bending must be recalculated.
MeshBender mb = go.GetComponent();
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()
.Select(child => child.gameObject).Except(used)) {
UOUtility.Destroy(go);
}
}
}
///
/// 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.
///
[Serializable]
public class TrackSegment {
public List transformedMeshes = new List();
}
///
/// This class stores all needed data to represent a mesh in situation.
/// It is intended to be edited in the inspector.
///
[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;
}
}