Unity中如何实现Mesh旗帜飘动动画?技术求助
旗帜飘动动画实现问题求助
我尝试实现旗帜飘动的动画,目标是通过正弦函数移动存储顶点的二维数组VerticeArray[,]的每一列,让Mesh呈现飘动效果。目前已创建初始Mesh:

相关说明:
GenerateVertices():将二维顶点数组转换为一维数组mg.nRows:Mesh的行数(当前为5)mg.nColumns:Mesh的列数(当前为3)
以下是我的代码,始终无法得到正确效果,恳请帮忙修正:
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UIElements; public class PlaneMeshDeformComponent : MonoBehaviour { [SerializeField] public float amplitude; [SerializeField] private bool activate; private Mesh m; private Vector3[] verticesCopy; private Vector3[,] verticeArray; private PlaneMeshGeneratorComponent mg; private float step = 0; private float previousStep = 0; private void Start() { mg = GetComponent<PlaneMeshGeneratorComponent>(); m = GetComponent<MeshFilter>().mesh; verticesCopy = m.vertices; for (int i = 0; i < verticesCopy.Length; ++i) { verticesCopy[i] += new Vector3(0, 0, amplitude * Mathf.Sin((float)(verticesCopy[i].x * (2*Math.PI/verticesCopy[^1].x)))); } //4. m.vertices = verticesCopy; //5. m.RecalculateNormals(); verticeArray = GenerateArray(m.vertices); /*for (int i = 0; i <= mg.nColumns; i++) { for (int k = 0; k <= mg.nRows; k++) { Debug.Log(verticeArray[k,i]); } }*/ } private void Update() { step += Time.deltaTime; for (int i = 0; i <= mg.nColumns; i++) { for (int k = 0; k <= mg.nRows; k++) { verticeArray[k, i] = new Vector3(verticeArray[k,i].x, verticeArray[k,i].y, verticeArray[k,i].z+amplitude*(Mathf.Sin(step)-Mathf.Sin(previousStep))); m.vertices = mg.GenerateVertices(verticeArray); Debug.Log($"step is {step}, previous is {previousStep}"); } } previousStep = step; } private Vector3[,] GenerateArray(Vector3[] verticesList) { Vector3[,] vArray= new Vector3[mg.nRows+1, mg.nColumns+1]; for (int i = 0; i <= mg.nRows; i++) { for (int k = 0; k <= mg.nColumns; k++) { vArray[i,k] = verticesList[i * (mg.nColumns+1) + k]; } } return vArray; } }
问题分析与修正代码
你的核心问题在于动画逻辑错误:顶点偏移是累加差值,导致顶点会持续向Z轴偏移,且每帧多次更新Mesh效率极低。以下是修正后的代码:
using System; using UnityEngine; public class PlaneMeshDeformComponent : MonoBehaviour { [SerializeField] public float amplitude = 0.5f; [SerializeField] private bool activate; [SerializeField] private float waveSpeed = 2f; // 飘动速度 [SerializeField] private float waveLength = 1f; // 波浪波长 private Mesh m; private Vector3[] originalVertices; // 存储原始顶点作为基准 private Vector3[,] verticeArray; private PlaneMeshGeneratorComponent mg; private float timeElapsed = 0; private void Start() { mg = GetComponent<PlaneMeshGeneratorComponent>(); m = GetComponent<MeshFilter>().mesh; originalVertices = m.vertices; // 保存原始顶点,不修改基准值 verticeArray = GenerateArray(originalVertices); } private void Update() { if (!activate) return; timeElapsed += Time.deltaTime; // 遍历每一列计算偏移 for (int col = 0; col <= mg.nColumns; col++) { // 取当前列的X位置作为波浪相位基准 float xPos = verticeArray[0, col].x; // 计算当前列的Z轴偏移:结合时间和X位置,模拟波浪传递 float zOffset = amplitude * Mathf.Sin(timeElapsed * waveSpeed + xPos / waveLength); // 更新该列所有顶点的Z值 for (int row = 0; row <= mg.nRows; row++) { int vertexIndex = row * (mg.nColumns + 1) + col; verticeArray[row, col] = new Vector3( originalVertices[vertexIndex].x, originalVertices[vertexIndex].y, originalVertices[vertexIndex].z + zOffset ); } } // 一次性更新Mesh并重新计算法线 m.vertices = mg.GenerateVertices(verticeArray); m.RecalculateNormals(); } private Vector3[,] GenerateArray(Vector3[] verticesList) { Vector3[,] vArray = new Vector3[mg.nRows + 1, mg.nColumns + 1]; for (int i = 0; i <= mg.nRows; i++) { for (int k = 0; k <= mg.nColumns; k++) { vArray[i, k] = verticesList[i * (mg.nColumns + 1) + k]; } } return vArray; } }
关键修正点
- 保留原始顶点:用
originalVertices存储初始位置,每次偏移都基于原始值计算,避免顶点位置失控。 - 修正动画逻辑:通过
时间+X位置的正弦函数模拟波浪传递,符合旗帜飘动的物理效果。 - 优化性能:将Mesh更新移到循环外,避免每帧多次修改Mesh数据。
- 添加可调参数:新增
waveSpeed和waveLength,方便调整飘动的速度和波浪疏密。 - 启用开关控制:利用
activate变量控制动画启停,便于调试。
内容的提问来源于stack exchange,提问作者Nicholas Spektor
相关产品推荐
相关产品推荐

