Code
对一个sharedMesh修改后,另一个没修改的Cube (1)也自动修改了,因为Cube与Cube (1)的sharedMesh是同一个实例引用
using UnityEngine;
using UnityEngine.Rendering;
public class ModifySharedMesh : MonoBehaviour
{
public Transform t1;
public Transform t2;
public Transform t3;
public bool usingSharedMesh;
private bool initUsing;
public MeshInfo srcMeshInfo;
public Mesh srcMesh;
public Mesh srcSharedMesh;
// Start is called before the first frame update
void Start()
{
initUsing = usingSharedMesh;
PrintSame("On Start");
}
// Update is called once per frame
void Update()
{
// modify mesh/sharedmesh
if (Input.GetKeyDown(KeyCode.Space))
{
var meshFilter = GetComponent();
// 再下面的CopyMesh使用MeshInfo的方式,发现MeshFilter很可疑,所以这里就测试了一下
// 经过测试果然发现.mesh getter执行一次后, .sharedMesh就不同和其他的 .sharedMesh共享了
// 我就懒得去Spy或是reflector看源代码了,反正只能看到表面层,很多底层逻辑都封装再c++层
PrintSame("before backup");
if (usingSharedMesh)
{
srcSharedMesh = CopyMesh(meshFilter.sharedMesh); // sharedMesh 的数据copy不会导致与其他实例的sharedMesh实例的引用不同
}
else
{
PrintSame("before getter");
var tempMesh = meshFilter.mesh;
PrintSame("after getter");
PrintSame("before CopyMesh(meshFilter.mesh)");
// 第一种
//srcMesh = CopyMesh(meshFilter.mesh); // 一旦Copy的是.mesh,而不是.sharedMesh的话,就会导致原本所有.sharedMesh与其他cube的 .sharedMesh相同引用的,就变成不一样了
// 第二种
srcMeshInfo = CopyMesh(meshFilter.mesh, ref srcMeshInfo); // 甚至copy到MeshInfo也不行,不知道是不是.mesh的getter被执行后,sharedMesh就会与其他的不一样了
PrintSame("after CopyMesh(meshFilter.mesh)");
}
var mesh = usingSharedMesh ? meshFilter.sharedMesh : meshFilter.mesh;
PrintSame("before modify");
mesh.Clear();
mesh.vertices = new Vector3[] { t1.position, t2.position, t3.position };
mesh.triangles = new int[] { 0, 1, 2 };
PrintSame("after modify");
}
// recovery mesh/sharemesh
if (Input.GetKeyDown(KeyCode.S))
{
if (usingSharedMesh)
{
if (srcSharedMesh != null)
{
PrintSame("before sharedMesh recovery");
var mesh = GetComponent().sharedMesh;
CopyMesh(srcSharedMesh, ref mesh);
PrintSame("after sharedMesh recovery");
srcSharedMesh = null;
}
}
else
{
//if (srcMesh != null)
//{
// PrintSame("before mesh recovery");
// var mesh = GetComponent().mesh;
// CopyMesh(srcMesh, ref mesh);
// srcMesh = null;
// PrintSame("after mesh recovery");
//}
if (srcMeshInfo != null)
{
PrintSame("before meshInfo recovery");
CopyMesh(srcMeshInfo, GetComponent().mesh);
srcMeshInfo = null;
PrintSame("after meshInfo recovery");
}
}
}
}
private void PrintSame(string title = null)
{
var sm1 = GameObject.Find("Cube").GetComponent().sharedMesh;
var sm2 = GameObject.Find("Cube (1)").GetComponent().sharedMesh;
if (string.IsNullOrEmpty(title)) Debug.Log($"sharedMesh same:{sm1 == sm2}");
else Debug.Log($"title : {title}, sharedMesh same:{sm1 == sm2}");
}
private Mesh CopyMesh(Mesh mesh)
{
Mesh result = null;
if (mesh)
{
result = new Mesh();
var t = typeof(Mesh);
var ps = t.GetProperties();
foreach (var item in ps)
{
if (item.CanWrite == false) continue;
item.SetValue(result, t.GetProperty(item.Name).GetValue(mesh));
}
}
return result;
}
private Mesh CopyMesh(Mesh mesh, ref Mesh result)
{
if (mesh)
{
if (result == null) result = new Mesh();
var t = typeof(Mesh);
var ps = t.GetProperties();
foreach (var item in ps)
{
if (item.CanWrite == false) continue;
item.SetValue(result, t.GetProperty(item.Name).GetValue(mesh));
}
}
return result;
}
private MeshInfo CopyMesh(Mesh mesh, ref MeshInfo result)
{
if (mesh)
{
if (result == null) result = new MeshInfo();
var meshType = typeof(Mesh);
var meshInfoType = typeof(MeshInfo);
var ps = meshInfoType.GetProperties();
foreach (var item in ps)
{
if (item.CanWrite == false) continue;
item.SetValue(result, meshType.GetProperty(item.Name).GetValue(mesh));
}
}
return result;
}
private void CopyMesh(MeshInfo info, Mesh result)
{
if (info == null || result == null) return;
var meshType = typeof(Mesh);
var meshInfoType = typeof(MeshInfo);
var ps = meshType.GetProperties();
foreach (var item in ps)
{
if (item.CanWrite == false) continue;
var p = meshInfoType.GetProperty(item.Name);
if (p == null) continue;
item.SetValue(result, p.GetValue(info));
}
}
}
public class MeshInfo
{
public IndexFormat indexFormat { get; set; }
public BoneWeight[] boneWeights { get; set; }
public Matrix4x4[] bindposes { get; set; }
public int subMeshCount { get; set; }
public Bounds bounds { get; set; }
public Vector3[] vertices { get; set; }
public Vector3[] normals { get; set; }
public Vector4[] tangents { get; set; }
public Vector2[] uv { get; set; }
public Vector2[] uv2 { get; set; }
public Vector2[] uv3 { get; set; }
public Vector2[] uv4 { get; set; }
public Vector2[] uv5 { get; set; }
public Vector2[] uv6 { get; set; }
public Vector2[] uv7 { get; set; }
public Vector2[] uv8 { get; set; }
public Color[] colors { get; set; }
public Vector2[] uv1 { get; set; }
public Color32[] colors32 { get; set; }
public int[] triangles { get; set; }
}
Runtime
操作:
- 选中场景中的名称为:Cube对象,调整ModifySharedMesh脚本的usingSharedMesh来决定修改sharedMesh还是mesh的方式
- 激活Game视图,按space键:修改
- 激活Game视图,按s键:还原
- MeshFilter.mesh只要执行过getter就会导致该meshFilter的.sharedMesh与其他本身同一实例引用的,变成了不同的
- 对MeshFilter.sharedMesh getter执行就没有这个问题丢失统一引用的问题
- 对MeshFilter.sharedMesh修改,其他相同实例的sharedMesh都会同时修改
- 对MeshFilter.mesh修改,再退出unity play模式后,就会还原数据
- 对MeshFilter.sharedMesh修改,退出了unity play模式一样会保留模型外观,应该是再哪里的文件cache了持久化
- 对MeshFilter.sharedMesh修改后,如果想还原被修改后的mesh,只要关掉(退出unity进程),再重新打开unity的对应项目就可以看到模型还原了
上面的总结,就第一条和倒数第一、第二条的潜规则代码需要注意就好
MeshFilter.mesh大概做了类似的以下处理:
public class MeshFilter ...
{
...
private Mesh _mesh;
public Mesh mesh
{
get
{
if (_mesh == null)
{
_mesh = new Mesh();
Copy(sharedMehs, _mesh);
}
return _mesh;
}
}
...
}
References
- mesh和sharedMesh的区别
- MeshFilter.mesh和MeshFilter.sharedMesh什么区别