void OnEnable() { Foliage2D foliage = (Foliage2D)target; if (foliage.GetComponent <MeshFilter>().sharedMesh == null) { foliage.RebuildMesh(); } }
static void AddGrassPatch() { GameObject obj = new GameObject("New Foliage2D_Object"); Foliage2D path = obj.AddComponent <Foliage2D>(); obj.AddComponent <Foliage2D_Animation>(); path.SetDefaultMaterial(); path.RebuildMesh(); obj.transform.position = GetSpawnPos(); Selection.activeGameObject = obj; EditorGUIUtility.PingObject(obj); }
void Start() { animator = GetComponent <Animator>(); foliage2D = GetComponent <Foliage2D>(); meshFilter = GetComponent <MeshFilter>().sharedMesh; initialVertexPos = meshFilter.vertices; horizontalVerts = foliage2D.widthSegments + 1; finalVertexPos = meshFilter.vertices; collider2DObject = new List <Collider2D>(); collider3DObject = new List <Collider>(); enterOffset = new List <float>(); anglesInDeg = new float[foliage2D.heightSegments + 1]; posOffset = new Vector3[foliage2D.heightSegments + 1]; centerLinePoints = new Vector2[foliage2D.heightSegments + 1]; // We change the default speed of the animation and then reset it // back so that the motion of different foliage objects isn't synchronized. // This will produce a more realistic behaviour. if (animator != null && changeAnimationSpeed) { StartCoroutine(SetAnimationSpeed()); } // This is a safeguard to eliminate the "Index out of Range Exception" errors // that may be generated when the object in the scene is a prefab instance // and the user forgot to save the changes he made to it. if (offsetFactor.Count != foliage2D.heightSegments + 1) { offsetFactor.Clear(); int len = foliage2D.heightSegments + 1; float offset = 1f / foliage2D.heightSegments; for (int i = 0; i < len; i++) { offsetFactor.Add(offset * i); } } }
private void CustomInspector(Foliage2D_Animation anim) { Undo.RecordObject(target, "Modified Inspector"); showProperties = EditorGUILayout.Foldout(showProperties, "Animation"); if (showProperties) { EditorGUI.indentLevel = 1; InspectorBox(10, () => { anim.foliageBending = (Foliage2D_MeshBending)EditorGUILayout.EnumPopup(new GUIContent("Mesh Bending ", "Describes the type of bending that should be applied to the foliage mesh."), anim.foliageBending); anim.changeAnimationSpeed = EditorGUILayout.Toggle(new GUIContent("Asynchronous Anim Start", "When set to true, the speed of the animation is changed for a short period of time. " + "This change takes place only once at the start of the game. This is useful if you don't want the foliage objects to have synchronous animations."), anim.changeAnimationSpeed); if (anim.changeAnimationSpeed) { anim.minSpeed = EditorGUILayout.FloatField(new GUIContent("Min Speed", "The min value for a random number used to change the playback speed of an animation."), anim.minSpeed); anim.maxSpeed = EditorGUILayout.FloatField(new GUIContent("Max Speed", "The max value for a random number used to change the playback speed of an animation."), anim.maxSpeed); anim.minSeconds = EditorGUILayout.FloatField(new GUIContent("Min Seconds", "The min value for a random number. The value of this " + "number determines how many seconds should the animation speed offset last."), anim.minSeconds); anim.maxSeconds = EditorGUILayout.FloatField(new GUIContent("Max Seconds", "The max value for a random number. The value of this " + " number determines how many seconds should the animation speed be offset."), anim.maxSeconds); } }); } EditorGUI.indentLevel = 0; showOffsetFactors = EditorGUILayout.Foldout(showOffsetFactors, "Offset Factors"); if (showOffsetFactors) { EditorGUI.indentLevel = 1; InspectorBox(10, () => { Foliage2D foliage2D = anim.GetComponent <Foliage2D>(); if (anim.offsetFactor.Count != foliage2D.heightSegments + 1) { anim.offsetFactor.Clear(); int len = foliage2D.heightSegments + 1; float offset = 1f / foliage2D.heightSegments; for (int i = 0; i < len; i++) { anim.offsetFactor.Add(offset * i); } } for (int i = anim.offsetFactor.Count - 1; i >= 0; i--) { anim.offsetFactor[i] = EditorGUILayout.Slider(new GUIContent("Offset " + i, "Determines which part of the animated variable " + " 'offset' will be used to calculate the new vertex position."), anim.offsetFactor[i], 0, 1); } }); } EditorGUI.indentLevel = 0; }
/// <summary> /// Custom inspector. /// </summary> private void CustomInspector(Foliage2D foliage2D) { Undo.RecordObject(target, "Modified Inspector"); showVisuals = EditorGUILayout.Foldout(showVisuals, "Visual Properties"); if (showVisuals) { EditorGUI.indentLevel = 1; InspectorBox(10, () => { foliage2D.pixelsPerUnit = Mathf.Clamp(EditorGUILayout.FloatField(new GUIContent("Pixels To Units", "The number of pixels in 1 Unity unit."), foliage2D.pixelsPerUnit), 1, 768); foliage2D.widthSegments = Mathf.Clamp(EditorGUILayout.IntField(new GUIContent("With Segments", "The number of columns the mesh has."), foliage2D.widthSegments), 1, 100); foliage2D.heightSegments = Mathf.Clamp(EditorGUILayout.IntField(new GUIContent("Height Segments", "The number of rows the mesh has."), foliage2D.heightSegments), 1, 100); Type utility = Type.GetType("UnityEditorInternal.InternalEditorUtility, UnityEditor"); if (utility != null) { PropertyInfo sortingLayerNames = utility.GetProperty("sortingLayerNames", BindingFlags.Static | BindingFlags.NonPublic); if (sortingLayerNames != null) { string[] layerNames = sortingLayerNames.GetValue(null, null) as string[]; string currName = foliage2D.GetComponent <Renderer>().sortingLayerName == "" ? "Default" : foliage2D.GetComponent <Renderer>().sortingLayerName; int nameID = EditorGUILayout.Popup("Sorting Layer", Array.IndexOf(layerNames, currName), layerNames); foliage2D.GetComponent <Renderer>().sortingLayerName = layerNames[nameID]; } else { foliage2D.GetComponent <Renderer>().sortingLayerID = EditorGUILayout.IntField("Sorting Layer", foliage2D.GetComponent <Renderer>().sortingLayerID); } } else { foliage2D.GetComponent <Renderer>().sortingLayerID = EditorGUILayout.IntField("Sorting Layer", foliage2D.GetComponent <Renderer>().sortingLayerID); } foliage2D.GetComponent <Renderer>().sortingOrder = EditorGUILayout.IntField("Order in Layer", foliage2D.GetComponent <Renderer>().sortingOrder); }); } EditorGUI.indentLevel = 0; if (GUILayout.Button("Rebuild Mesh")) { foliage2D.RebuildMesh(); } if (GUI.changed) { EditorUtility.SetDirty(target); foliage2D.RebuildMesh(); } if (Event.current.type == EventType.ValidateCommand) { switch (Event.current.commandName) { case "UndoRedoPerformed": foliage2D.RebuildMesh(); break; } } }
// Inspector Fields public override void OnInspectorGUI() { Foliage2D foliage2D = (Foliage2D)target; CustomInspector(foliage2D); }
/// <summary> /// Fills the foliage path with objects. /// </summary> public void RecreateFoliage() { foliageCount = foliageOnPath.Count; distanceFromStart = 0; prevDistanceFromStart = 0; maxCount = 0; float ZAxisOffset = zOffset; List <GameObject> foliageP = new List <GameObject>(); int len = foliagePrefabs.Count; foliageP.Clear(); // The foliage prefabs are placed in a new list to make sure that there are no null fields. for (int k = 0; k < len; k++) { if (foliagePrefabs[k] != null) { foliageP.Add(foliagePrefabs[k]); } } maxCount = foliageP.Count; // If the prefab list is empty return. if (maxCount == 0) { return; } objectIndex = 1; // Foliage objects are placed on the line described by the path nodes. for (int i = 0; i < handlesPosition.Count - 1; i++) { line = handlesPosition[i + 1] - handlesPosition[i]; angleInRad = Mathf.Atan2(line.y, line.x); angleInDeg = angleInRad * Mathf.Rad2Deg; lineLength = line.magnitude; // The vector is normalized so that we can calculate its normal correctly. line.Normalize(); lineNormal = new Vector2(-line.y, line.x); distanceFromStart = 0; prevDistanceFromStart = 0; // This loop is executed until the distance between the start of the line and a // foliage object is smaller than the length of the line. while (true) { updatedPrefabIndex = false; // If a new object must be instantiated, updates the "prefabIndex" value. // The value of "prefabIndex" determines which prefab will be instantiated // in the current iteration. if (foliageCount < objectIndex) { if (foliagePattern == Foliage2D_Pattern.Random) { // Get a random index. prefabIndex = Random.Range(0, maxCount); } else { // The value of "prefabIndex" is incremented by 1 so that new objects are instantiated consecutively. prefabIndex++; // If the value of "prefab Index" is bigger than "maxCount" or smaller than 0, we reset it to 0. if (prefabIndex >= maxCount || prefabIndex < 0) { prefabIndex = 0; } } updatedPrefabIndex = true; // We get the Foliage2D component of the prefab with the index of "prefabIndex" // so that we have access to the width of the correct foliage object when we // calculate the distance from the start of the line where this object will be placed. foliage2D = foliageP[prefabIndex].GetComponent <Foliage2D>(); DistanceFromStart(); } else { foliage2D = foliageOnPath[objectIndex - 1].GetComponent <Foliage2D>(); DistanceFromStart(); } // If the distance from the start of the line where the last object was placed is // bigger than the line length, the position of the object is reset and the execution // of the while loop is terminated. if (distanceFromStart > lineLength) { foliage2D = foliageOnPath[prevIndex].GetComponent <Foliage2D>(); distanceFromStart = lineLength - foliage2D.width / 2f + lastObjectOffset; if (foliagePathType == Foliage2D_PathType.Smooth) { SmoothPath(i); } else { pointOnTheLine = Vector2.Lerp(handlesPosition[i], handlesPosition[i + 1], distanceFromStart / lineLength); } posOffset = lineNormal * (foliage2D.height / 2f); foliageOnPath[prevIndex].transform.position = transform.TransformPoint(new Vector3(pointOnTheLine.x + posOffset.x, pointOnTheLine.y + posOffset.y, ZAxisOffset)); if (updatedPrefabIndex && foliagePattern == Foliage2D_Pattern.Consecutive) { RestorePrefabIndex(); } break; } if (foliagePathType == Foliage2D_PathType.Smooth) { SmoothPath(i); } else { pointOnTheLine = Vector2.Lerp(handlesPosition[i], handlesPosition[i + 1], distanceFromStart / lineLength); } if (foliageCount < objectIndex) { // If the index of the current object is bigger than the total number of // instantiated objects, a new object is instantiated. ZAxisOffset *= -1; Vector3 pos = pointOnTheLine + posOffset; pos.z = zOffset; GameObject obj = Instantiate(foliageP[prefabIndex], transform.TransformPoint(pos), Quaternion.Euler(new Vector3(0, 0, angleInDeg))) as GameObject; obj.transform.parent = transform; foliageOnPath.Add(obj); prevIndex = foliageOnPath.Count - 1; foliageCount = foliageOnPath.Count; Foliage2D objFoliage = obj.GetComponent <Foliage2D>(); // The object mesh is recreated so that we don't have 2 objects with the same mesh instance. objFoliage.RebuildMesh(); objectIndex++; } else { ZAxisOffset *= -1; prevIndex = objectIndex - 1; foliageOnPath[objectIndex - 1].transform.position = transform.TransformPoint(new Vector3(pointOnTheLine.x + posOffset.x, pointOnTheLine.y + posOffset.y, ZAxisOffset)); foliageOnPath[objectIndex - 1].transform.rotation = Quaternion.Euler(new Vector3(0, 0, angleInDeg)); objectIndex++; } } } // If the path shrinked and there is an excess of foliage objects, delete them. if (foliageCount + 1 > objectIndex) { int lenD = foliageCount + 1 - objectIndex; for (int i = 0; i < lenD; i++) { int last = foliageOnPath.Count - 1; DestroyImmediate(foliageOnPath[last]); foliageOnPath.RemoveAt(last); if (foliagePattern == Foliage2D_Pattern.Consecutive) { RestorePrefabIndex(); } } } }