Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Unity插件 - MeshEditor(五) 網格頂點動畫

Unity插件 - MeshEditor(五) 網格頂點動畫

編輯:關於Android編程

這次加入的功能比之前幾次的實用性明顯要高些,像什麼破碎啊,融化啊,其實細想一下會發現......沒什麼卵用,模型的頂點數據還是應該交給GPU繪制才是正道,CPU刷新模型頂點始終是個吃力不討好的事(不過我好像至始至終就是在干吃力不討好的事來著),所以今天要講的網格頂點動畫也還是別用到過於復雜的模型之上,畢竟到頭來吃力的只會是你的項目,不過一些簡單的模型倒不用擔心,像什麼旗幟飄揚什麼的,不用打開3DMAX(前提是得會用這東西K動畫),不用局限於Unity的animator系統(畢竟給你一個做得像旗幟的cube,你能用animator調一個飄動的動畫?),只需簡單的幾步拖拽便可以K出一個動畫,並且可以將動畫信息保存為本地文件,實現多項目間復用,同時,頂點數相同的模型也可以復用動畫(這感覺也沒什麼卵用)。

 

今天所提的網格頂點動畫完全不同於Unity Animator系統的機制,事實上跟它半毛錢關系都沒有,所以這兩種動畫在同一物體上是可以共同存在的,事實上,眾所周知,Animator的關鍵幀只會記錄物體的transform組件的position、rotation以及scale的數值變化,其余的任何屬性改變都不會被它視為有另一關鍵幀產生,而網格頂點動畫只會記錄模型的頂點數據作為關鍵幀,完全不會改動transform組件的屬性,所以這兩種動畫完全可以共存。

\

好了,進入正題,我以給一個cube調一個動畫為例子講解一下整個流程及實現的思路。

 

第一步、為cube添加我們的網格頂點動畫編輯器組件(MeshAnimation)

\

添加動畫幀:以Scene場景中當前物體的狀態信息保存為一個新的關鍵幀,這裡的代碼主要是記錄每個頂點的位置

/// 
    /// 添加動畫幀
    /// 
    public void AddFrame()
    {
        Vector3[] vertices = new Vector3[_Vertices.Length];
        for (int i = 0; i < _Vertices.Length; i++)
        {
            vertices[i] = _Vertices[i].transform.position;
        }
        _VerticesAnimationArray.Add(vertices);
    }


我們最好先在cube的初始狀態就添加一個動畫幀,以便於播放動畫時它會從初始狀態開始

\

 

第二步、現在我們多添加幾個關鍵幀,目前每幀的狀態都是保持在初始形態

\

 

第三步、我們的第一幀就讓他保持初始狀態,現在選中第二幀,同時在場景中調節cube的形態,當你覺得滿意的時候,點擊apply應用就可以將物體的狀態應用到當前的第二幀數據,當然如果這一關鍵幀不想要了,點擊delete刪除即可

 

\

/// 
    /// 應用動畫幀
    /// 
    public void ApplyFrame()
    {
        //如果當前動畫幀數據存在,則應用當前物體的各頂點數據至當前動畫幀
        if (_NowSelectFrame >= 0 && _NowSelectFrame < _VerticesAnimationArray.Count)
        {
            for (int i = 0; i < _Vertices.Length; i++)
            {
                _VerticesAnimationArray[_NowSelectFrame][i] = _Vertices[i].transform.position;
            }
        }
    }

 

/// 
    /// 刪除動畫幀
    /// 
    public void DeleteFrame()
    {
        //如果當前動畫幀數據存在,則刪除當前動畫幀數據
        if (_NowSelectFrame >= 0 && _NowSelectFrame < _VerticesAnimationArray.Count)
        {
            _VerticesAnimationArray.RemoveAt(_NowSelectFrame);
            _NowSelectFrame = -1;
        }
    }

 

我們將cube調節成這個樣子,然後點擊apply應用關鍵幀

\

 

第四步、選中第三個關鍵幀,再調到自己滿意的形態,並再點擊apply應用

\

 

第五步、選中第四個關鍵幀,這裡我們要讓他有個緩沖的效果,也就是說跟第三幀的差距小一點

然後我們的第四幀就調成了這個慫樣~

\

 

第六步、第五幀我們就要讓他發射出去(前幾幀是收縮,蓄勢,然後第五幀猛地彈出~~有沒有一種發射炮彈的感覺~~),當然如果你想復制某一幀的話,只需選中這一幀,點擊添加關鍵幀,最後面就會多出來與此幀相同的一幀,然後在此基礎上調節下一幀更方便

\

 

第七步、之後就是給他K幾個反彈回來的緩沖關鍵幀,注意這裡選中任意一幀場景中的cube就會變化到那一幀的形態(這種方式是仿Animator的),隨意修改之後點擊應用可以保存,不點擊應用默認改動無效

\

 /// 
    /// 選定指定幀
    /// 
    public void SelectFrame(int frameIndex)
    {
        //如果當前動畫幀數據存在,則選定當前動畫幀,所有頂點應用當前動畫幀數據
        if (frameIndex >= 0 && frameIndex < _VerticesAnimationArray.Count)
        {
            _NowSelectFrame = frameIndex;
            for (int i = 0; i < _Vertices.Length; i++)
            {
                _Vertices[i].transform.position = _VerticesAnimationArray[frameIndex][i];
            }
        }
    }

 

完成之後點擊預覽按鈕就可以馬上在Scene界面看到cube的動畫效果,這裡沒截圖,後面用動畫播放器播放的時候再截圖

\

因為腳本就算添加了編輯器執行的標識,它的update函數依然不會逐幀執行,而是在場景物體發生變化的時候才執行,所以這裡的動畫預覽函數不能放在update裡,那麼只有將之加入到Unity編輯器逐幀刷新周期了

/// 
    /// 預覽動畫
    /// 
    public void PlayAnimation()
    {
        //沒有動畫可以預覽
        if (_VerticesAnimationArray.Count <= 0)
        {
            return;
        }
        //預覽從第一幀開始(頂點動畫數組下標0)
        _AnimationIndex = 0;
        //重置記錄動畫播放上一序列的變量
        _AnimationLastIndex = -1;
        //重建新的動畫片段
        _AnimationFragment = new Vector3[_Vertices.Length];
        //重置動畫播放控制器
        _AnimationPlayControl = 0;
        //動畫進入到第一幀
        for (int i = 0; i < _Vertices.Length; i++)
        {
            _Vertices[i].transform.position = _VerticesAnimationArray[0][i];
        }
        _IsPlay = true;
        //將刷新動畫函數注冊到Unity編輯器幀執行模塊
        EditorApplication.update += PlayingAnimation;
    }


動畫刷新函數采用將每個關鍵幀切分為動畫片段的方式,將片段循環累加給cube的網格頂點

/// 
    /// 動畫預覽中
    /// 
    void PlayingAnimation()
    {
        if (_IsPlay)
        {
            //動畫播放至最後一幀,動畫播放完畢
            if (_AnimationIndex + 1 >= _VerticesAnimationArray.Count)
            {
                //動畫播放完畢
                _IsPlay = false;
                //清除刷新動畫函數的注冊
                EditorApplication.update -= PlayingAnimation;
                //動畫回歸到第一幀
                for (int i = 0; i < _Vertices.Length; i++)
                {
                    _Vertices[i].transform.position = _VerticesAnimationArray[0][i];
                }
                return;
            }
            //當前動畫播放序列不等於上一幀序列,則進入下一幀
            if (_AnimationIndex != _AnimationLastIndex)
            {
                _AnimationLastIndex = _AnimationIndex;
                //分割動畫片段
                for (int i = 0; i < _AnimationFragment.Length; i++)
                {
                    _AnimationFragment[i] = (_VerticesAnimationArray[_AnimationIndex + 1][i] - _VerticesAnimationArray[_AnimationIndex][i])/ _AnimationPlaySpeed;
                }
            }
            //動畫進行中
            for (int i = 0; i < _Vertices.Length; i++)
            {
                _Vertices[i].transform.position += _AnimationFragment[i];
            }
            //動畫控制器計數
            _AnimationPlayControl += 1;
            //動畫控制器記錄的一個動畫幀播放完畢
            if (_AnimationPlayControl >= _AnimationPlaySpeed)
            {
                _AnimationPlayControl = 0;
                _AnimationIndex += 1;
            }
            RefishMesh();
        }
    }

 

第八步、這裡是重點了,記得點擊導出動畫,如果你直接點擊編輯完成或是突然有了什麼好想法跑去VS裡隨意改了下腳本導致Unity編輯器重新編譯的話,很遺憾你的動畫數據都會丟失,記得導出完畢了之後再點擊編輯完成

\

使用scriptableobject序列化動畫數據至asset文件中,這裡的坑是真坑,路徑必須還得是Asset開頭,後綴必須還得是asset,剛開始坑了我不少無辜的時間

/// 
    /// 導出動畫
    /// 
    public void ExportAnimation()
    {
        //動畫幀數小於等於1不允許導出
        if (_VerticesAnimationArray.Count <= 1)
            return;
        //創建動畫數據文件
        MeshAnimationAsset meshAnimationAsset = ScriptableObject.CreateInstance();
        //記錄動畫頂點數
        meshAnimationAsset._VertexNumber = _RecordAllVerticesList.Count;
        //記錄動畫幀數
        meshAnimationAsset._FrameNumber = _VerticesAnimationArray.Count;
        //記錄動畫幀數據
        meshAnimationAsset._VerticesAnimationArray = new Vector3[_VerticesAnimationArray.Count * _RecordAllVerticesList.Count];
        for (int n = 0; n < _VerticesAnimationArray.Count; n++)
        {
            for (int i = 0; i < _VerticesAnimationArray[n].Length; i++)
            {
                for (int j = 0; j < _AllVerticesGroupList[i].Count; j++)
                {
                    int number = n * _RecordAllVerticesList.Count + _AllVerticesGroupList[i][j];
                    EditorUtility.DisplayProgressBar("導出動畫", "正在導出頂點數據(" + number + "/" + meshAnimationAsset._VerticesAnimationArray.Length + ")......", 1.0f / meshAnimationAsset._VerticesAnimationArray.Length * number);
                    meshAnimationAsset._VerticesAnimationArray[number] = transform.worldToLocalMatrix.MultiplyPoint3x4(_VerticesAnimationArray[n][i]);
                }
            }
        }
        //創建本地文件
        string path = "Assets/" + GetComponent().sharedMesh.name + "AnimationData.asset";
        AssetDatabase.CreateAsset(meshAnimationAsset, path);

        EditorUtility.ClearProgressBar();
    }

 

如下就是我們導出來的動畫數據,可以看到裡面包含了10個關鍵幀,適用於一切有24個網格頂點的模型(網格頂點是可操控頂點的3倍),當然他的原主是cube

\

 

第九步、然後,為cube添加網格頂點動畫播放器組件(MeshAnimationPlayer)並為其添加我們的CubeAnimationData,每一個MeshAnimationPlayer對應一個AnimationData文件,暫不支持代碼中動態變更

\

MeshAnimationAsset:動畫播放器的目標asset文件,頂點數量需與當前掛載物體一致

AnimationPlaySpeed:動畫播放速度,注意,這裡是值越小播放越快

另外兩個參數是開啟循環播放和啟動時即播放,我們勾選啟動播放,然後運行程序,下面是動態效果圖

\

我們可以看到動畫有點丑~~當然這只是隨便調調,畢竟我不是美術

MeshAnimationPlayer的播放有外部可控開關

/// 
    /// 播放動畫
    /// 
    public void Play()
    {
        //從第一幀開始播放(頂點動畫數組下標0)
        _AnimationIndex = 0;
        //重置記錄動畫播放上一序列的變量
        _AnimationLastIndex = -1;
        //重置動畫播放控制器
        _AnimationPlayControl = 0;
        //動畫跳轉到第一幀
        SelectFrame(_AnimationIndex);
        _IsPlaying = true;
    }
    /// 
    /// 停止播放
    /// 
    public void Stop()
    {
        _IsPlaying = false;
        //動畫回歸到第一幀
        SelectFrame(0);
    }


以及要獲取當前動畫是否播放中,可以直接讀取_IsPlaying屬性。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved