You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
}

关键修改点说明

  1. 提取通用方法:将顶点保存逻辑封装为SaveCurrentMeshVertices(),避免代码冗余,提高可维护性。
  2. 烘焙实时网格:通过skin.BakeMesh(bakedMesh)获取当前动画帧的变形网格,替代原来的sharedMesh。
  3. 资源释放:使用完临时bakedMesh后调用Destroy(bakedMesh),防止内存泄漏。
  4. 可选坐标转换:添加了世界坐标转换的注释代码,若需要导出世界空间下的点云,可取消注释使用。
  5. 错误处理:增加了SkinnedMeshRenderer的空值检查,以及更明确的错误日志。

额外注意事项

  • 确保模型的SkinnedMeshRenderer组件已正确配置,且动画控制器正常播放目标动画。
  • BakeMesh()操作会带来一定性能开销,建议仅在需要保存顶点时调用(如按下S键时),不要每帧执行。
  • 若需要批量导出多帧动画的点云,可以在Update()中按时间间隔或动画帧触发保存逻辑。

内容的提问来源于stack exchange,提问作者Uchiy1320

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 20:01:06