DinoFracture Engine, актив для добавления в Unity эффекта разлома, облегчающего создание любых сеток, является очень хорошим продуктом.
https://assetstore.unity.com/packages/tools/physics/dinofracture-a-dynamic-fracture-library-26599
Он может генерировать изломы во время выполнения и в редакторе. Но если вы пытаетесь сделать излом в редакторе (не в режиме игры), вам нужно сделать определенную сцену и прикрепить компонент PreFracturedGeometory, и нажать кнопку «Create Fractures».
В моем случае есть много сеток, на которых нужно сделать изломы, поэтому я сделал скрипт для Dino Fracture, чтобы генерировать изломы без кликов в сцене.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEditor;
using DinoFracture;
using DinoFracture.Editor;
public class PreFractureInAssetFolder: PreFracturedGeometryEditor
{
private const string PrefabFolderPath = "Assets/{your prefab folder path}";
private const string MeshFolderPath = "Assets/{your mesh folder path}";
private const string ExportFolderName = "PreFractured";
private const string InsideMaterialPath = "Assets/{your inside material path}.mat";
private const string StandardFractureTemplatePath = "Assets/DinoFracture/Plugin/Prefabs/StandardFracturePiece.prefab";
[MenuItem("Tools/DinoFracture/PreFractureInAssetFolder")]
public static void PreFracture()
{
//filter paths you want generate fractures
string[] allPath = AssetDatabase.GetAllAssetPaths();
var prefabPathList = allPath.Where(p => p.Contains(PrefabFolderPath))
.Where(p2 => !p2.Contains(ExportFolderName)).ToList();
var objectCount = prefabPathList.Count;
for (var index = 0; index < objectCount; index++)
{
var prefabPath = prefabPathList[index];
var contentsRoot = PrefabUtility.LoadPrefabContents(prefabPath);
if (contentsRoot.GetComponent<MeshFilter>() == false)
{
Debug.Log("contentsRoot " + contentsRoot.name +"doesnt has meshfilter");
objectCount--;
continue;
}
MakeFractureMeshes(contentsRoot, prefabPath, objectCount);
}
}
private static void MakeFractureMeshes(GameObject contentsRoot, string prefabPath, int objectCount)
{
List<Tuple<Mesh, string>> meshAssetList = new List<Tuple<Mesh, string>>();
List<Tuple<GameObject, string, string>> prefabAssetList = new List<Tuple<GameObject, string, string>>();
var standardFractureTemplate = AssetDatabase.LoadAssetAtPath<GameObject>(StandardFractureTemplatePath);
var insideMaterial = AssetDatabase.LoadAssetAtPath<Material>(InsideMaterialPath);
var geom = contentsRoot.AddComponent<PreFracturedGeometry>();
geom.FractureTemplate = standardFractureTemplate;
geom.InsideMaterial = insideMaterial;
//set parametors as you want
geom.NumFracturePieces = 4;
geom.NumIterations = 2;
geom.NumGenerations = 2;
geom.UVScale = FractureUVScale.EntireMesh;
geom.GenerateFractureMeshes(
(g) =>
{
g.GeneratedPieces.SetActive(true);
//generate meshes
string meshSaveFolderPath = Path.Combine(MeshFolderPath, ExportFolderName, geom.gameObject.name);
for (int i = 0; i < geom.GeneratedPieces.transform.childCount; i++)
{
var pieceTransform = geom.GeneratedPieces.transform.GetChild(i);
//in my case mesh collider is much CPU usage so replase to BoxCollider
var meshCollider = pieceTransform.GetComponent<MeshCollider>();
GameObject.DestroyImmediate(meshCollider);
var boxCollider = pieceTransform.gameObject.AddComponent<BoxCollider>();
boxCollider.enabled = false;
var rigidbody = pieceTransform.GetComponent<Rigidbody>();
rigidbody.isKinematic = true;
MeshFilter mf = geom.GeneratedPieces.transform.GetChild(i).GetComponent<MeshFilter>();
if (mf != null && mf.sharedMesh != null)
{
meshAssetList.Add(new Tuple<Mesh, string>(mf.sharedMesh, meshSaveFolderPath));
}
}
//save prefabs
var prefabSaveFolderPath = Path.Combine(Path.GetDirectoryName(prefabPath), ExportFolderName);
prefabAssetList.Add(new Tuple<GameObject, string, string>(g.GeneratedPieces, prefabSaveFolderPath,
contentsRoot.name + ".prefab"));
if (prefabAssetList.Count >= objectCount)
{
SaveProcess(meshAssetList, prefabAssetList);
}
});
}
private static void SaveProcess(List<Tuple<Mesh, string>> meshAssetList, List<Tuple<GameObject, string, string>> prefabAssetList)
{
Debug.Log("save process");
//stop Asset import Postprocess
AssetDatabase.StartAssetEditing();
//save mesh
foreach (var meshAsset in meshAssetList)
{
DirectoryInfo dir = new DirectoryInfo(meshAsset.Item2);
if (!dir.Exists)
{
dir.Create();
}
else
{
// delete old meshes
foreach (FileInfo file in dir.GetFiles())
{
file.Delete();
}
}
string assetPath = Path.Combine(meshAsset.Item2, String.Format("{0}.asset", Guid.NewGuid().ToString("B")));
AssetDatabase.CreateAsset(meshAsset.Item1, assetPath);
}
//save prefab
foreach (var prefabAsset in prefabAssetList)
{
DirectoryInfo dir = new DirectoryInfo(prefabAsset.Item2);
if (!dir.Exists)
{
dir.Create();
}
PrefabUtility.SaveAsPrefabAsset(prefabAsset.Item1, Path.Combine(prefabAsset.Item2, prefabAsset.Item3 ));
GameObject.DestroyImmediate(prefabAsset.Item1);
}
meshAssetList.Clear();
prefabAssetList.Clear();
AssetDatabase.StopAssetEditing();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}