Unity动画中获取动态顶点坐标生成动态点云的问题咨询
解决Unity动画模型实时顶点坐标获取问题
问题根源
你当前代码中通过skin.sharedMesh获取的是模型原始绑定状态的网格数据,该数据不会随骨骼动画的播放而更新,因此导出的顶点坐标始终是静态姿态,无法反映动画中的肢体变形。
核心解决方案
使用SkinnedMeshRenderer.BakeMesh()方法,将当前帧动画驱动下的实时变形网格烘焙到临时Mesh对象中,再从这个临时Mesh里读取顶点坐标,就能获取到对应动画姿态的顶点数据。
修改后的完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerControl : MonoBehaviour { // 移動スピード public float speed; // 前進or後退 bool FronFlag = true; // 保存ファイル名 public string filename = "sample"; // 原点にあるオブジェクトの頂点を数えるか? bool OriginSaveFlag = false; // Start is called before the first frame update void Start() { if (OriginSaveFlag) { SaveCurrentMeshVertices(); } } // Update is called once per frame void Update() { // プレイヤの位置情報 Transform PlayerTransform = this.transform; // 座標を取得 Vector3 pos = PlayerTransform.position; if (FronFlag) { // 前に移動する transform.position += transform.forward * speed * Time.deltaTime; if (pos.z <= -10.0){ FronFlag = false; } } else { // 後ろに移動する transform.position -= transform.forward * speed * Time.deltaTime; if (pos.z >= 0){ FronFlag = true; } } if (Input.GetKeyDown(KeyCode.S)) { Debug.Log("Sキーが押されたので、頂点座標を保存します。"); SaveCurrentMeshVertices(); } } /// <summary> /// 現在のアニメーション姿勢の頂点座標を保存するメソッド /// </summary> void SaveCurrentMeshVertices() { SkinnedMeshRenderer skin = GetComponentInChildren<SkinnedMeshRenderer>(); if(skin == null) { Debug.LogError("SkinnedMeshRendererが見つかりません"); return; } // 現在のアニメーション姿勢のメッシュをベイクする Mesh bakedMesh = new Mesh(); skin.BakeMesh(bakedMesh); // 頂点の数を数える int vtx_num = bakedMesh.vertices.Length; Debug.Log("Number of vertexes: " + vtx_num); // 頂点座標を取得(必要に応じてワールド座標に変換) Vector3[] vtx_posi_array = new Vector3[vtx_num]; for(int i = 0; i < vtx_num; i++) { // ローカル座標のまま保存する場合 vtx_posi_array[i] = bakedMesh.vertices[i]; // ワールド座標に変換して保存する場合は下記を使用 // vtx_posi_array[i] = skin.transform.TransformPoint(bakedMesh.vertices[i]); Debug.Log("No." + (i+1) + ", Vertex Coordinates: " + vtx_posi_array[i].ToString("F6")); } // csvファイルに書き込む try { string savePath = filename + ".csv"; bool append = false; Debug.Log("vtx_posi_array.Length: " + vtx_posi_array.Length); using(var sw = new System.IO.StreamWriter(savePath, append)) { for(int i = 0; i < vtx_posi_array.Length; ++i) { sw.WriteLine("{0},{1},{2}", vtx_posi_array[i].x, vtx_posi_array[i].y, vtx_posi_array[i].z); } } Debug.Log("頂点座標を" + savePath + "に保存しました"); } catch(System.Exception e) { Debug.LogError("ファイル保存エラー: " + e.Message); } // ベイクしたメッシュを破棄してメモリリークを防ぐ Destroy(bakedMesh); } }
关键修改点说明
- 提取通用方法:将顶点保存逻辑封装为
SaveCurrentMeshVertices(),避免代码冗余,提高可维护性。 - 烘焙实时网格:通过
skin.BakeMesh(bakedMesh)获取当前动画帧的变形网格,替代原来的sharedMesh。 - 资源释放:使用完临时
bakedMesh后调用Destroy(bakedMesh),防止内存泄漏。 - 可选坐标转换:添加了世界坐标转换的注释代码,若需要导出世界空间下的点云,可取消注释使用。
- 错误处理:增加了
SkinnedMeshRenderer的空值检查,以及更明确的错误日志。
额外注意事项
- 确保模型的
SkinnedMeshRenderer组件已正确配置,且动画控制器正常播放目标动画。 BakeMesh()操作会带来一定性能开销,建议仅在需要保存顶点时调用(如按下S键时),不要每帧执行。- 若需要批量导出多帧动画的点云,可以在
Update()中按时间间隔或动画帧触发保存逻辑。
内容的提问来源于stack exchange,提问作者Uchiy1320
相关产品推荐
相关产品推荐

