当前位置:   article > 正文

【Unity教程】2D水物理模拟_unity 2d水

unity 2d水

Unity教程】2D水物理模拟

 视频地址

介绍

  • 当角色进入水中时,水会根据角色下降的速度大小受力,进而让水面下降

  • 当角色跳出水中时,水会根据角色出水的速度大小受力,水面会有少数上升

  • 角色再水中移动时,会对周围水面产生影响,类似出水时的受力

  • 水面会根据受力,上下波动,并且随着时间波动衰减,类似橡皮筋

  • 水面波动会向周围扩散,并衰减,最终趋于平静

第一部分 绘制水面

绘制组件(unity内置的组件)

水面和绘制

  • 1.绘制水上面部分的顶点和下面部分的顶点

  1.  //顶点,length是水面长度,quality是顶点数量
  2.          float interval_length = length/quality;
  3.          //分开设置数组前一部分是上顶点,后一半是下顶点
  4.          //上面顶点
  5.          for (int i = 0; i < quality; i++) {
  6.              vertices[i] = new Vector3(i * interval_length, width, 0);
  7.         }
  8.          //下面顶点
  9.          for (int i = quality; i < 2*quality; i++) {
  10.              vertices[i] = new Vector3((i-quality) * interval_length, 0, 0);
  11.         }
  • 2.如果看了上面mesh的博客,就知道需要把这些点连接成一个顺时针的三角形,这样才能再正面显示出来

  • 从下顶点遍历,绘制三角形,每个下顶点顺时针绘制两个三角形

  • 3.然后UV采样

  • UV坐标是x范围(0,1)y的范围(0,1)

  • 所以绘制的顶点均进行均匀在uv图上采样

  1.  Vector2[] uvs = new Vector2[vertices.Length];
  2.          for (int i = 0; i < vertices.Length; i++) {
  3.              uvs[i] = new Vector2(vertices[i].x/length,vertices[i].y/width);
  4.         }

img

完整绘制水面代码

  
  1. //绘制水
  2.      public void DrawWater(float length,float width,int quality) {
  3.          //材质
  4.          gameObject.GetComponent<MeshRenderer>().material = mat;
  5.          //顶点
  6.          float interval_length = length/quality;
  7.          //分开设置数组前一部分是上顶点,后一半是下顶点
  8.          //上面顶点
  9.          for (int i = 0; i < quality; i++) {
  10.              vertices[i] = new Vector3(i * interval_length, width, 0);
  11.         }
  12.          //下面顶点
  13.          for (int i = quality; i < 2*quality; i++) {
  14.              vertices[i] = new Vector3((i-quality) * interval_length, 0, 0);
  15.         }
  16.          //设置三角形
  17.          int anglescount = (quality*2-2)*3;
  18.          triangles = new int[anglescount];
  19.  ​
  20.          int current=0;
  21.          for (int i = quality; i < 2 * quality - 1; i++) {
  22.  ​
  23.              triangles[current++] = i;
  24.              triangles[current++] = i-quality;
  25.              triangles[current++] = i-quality+1;
  26.  ​
  27.              triangles[current++] = i;
  28.              triangles[current++] = i - quality + 1;
  29.              triangles[current++] = i + 1;
  30.         }
  31.  ​
  32.          Vector2[] uvs = new Vector2[vertices.Length];
  33.          for (int i = 0; i < vertices.Length; i++) {
  34.              uvs[i] = new Vector2(vertices[i].x/length,vertices[i].y/width);
  35.         }
  36.  ​
  37.          Mesh mesh = GetComponent<MeshFilter>().mesh;
  38.          mesh.Clear();
  39.          mesh.RecalculateNormals();
  40.          mesh.RecalculateTangents();
  41.          mesh.vertices = vertices;
  42.          mesh.triangles = triangles;
  43.          mesh.uv = uvs;
  44.     }

第二部分 物理模拟

物理模拟原理

  • 因为水面是由很多个顶点组成,所以只需要对水面的顶点进行变换,在重新绘制顶点即可模拟水面的变化。

  • 在这里每个顶点移动只在Y轴(上下)进行偏移,不进行左右移动

  • 所以只需要模拟每个顶点的运动规律就可以实现水面动画

实现思路

  • 1.因为只对水面部分的顶点进行模拟,设置一个水面顶点变化速度的数组

  • 2.当物体落入水中,跳出水中,在水中移动时,根据距离和力的大小赋予数组初速度。

  • 3.然后每帧让顶点位置+对应变化速度,进行顶点偏移

  • 4.速度数组会时刻收到与速度相反的力,可以理解为水面张力,目的就是让水面恢复平静,所以在设置每个位置对应的加速度,与速度方向相反

  • 5.速度受加速度和阻力的影响持续衰减,最终减少为0

  • 6.扩散,从左遍历,每一个顶点变换速度+上相邻右边顶点的差值,再从右边遍历,每一个顶点变换速度+上相邻左边顶点的差值

完整代码

 
  1. /****************************************************
  2.      文件:WaterMeshRender.cs
  3.   作者:别离或雪
  4.      邮箱: 2946952974@qq.com
  5.      日期:#CreateTime#
  6.   功能:水面模拟
  7.  *****************************************************/
  8.  ​
  9.  using UnityEngine;
  10.  ​
  11.  [RequireComponent(typeof(MeshFilter),typeof(MeshRenderer))]
  12.  public class WaterMeshRender : MonoBehaviour
  13.  {
  14.  ​
  15.     [Header("水")]
  16.      public float length;//水面长度
  17.      public float width;//水深
  18.      public int quality;//顶点数量
  19.  ​
  20.      public Material mat;//材质
  21.     [Header("组件")]
  22.      private BoxCollider2D boxCollider;
  23.      private Bounds bounds;
  24.      Vector3[] vertices;//顶点
  25.      int[] triangles;//三角形
  26.  ​
  27.      float[] leftdetails;//向左扩散速度
  28.      float[] rightdetails;//向右扩散速度
  29.      float[] velocitys;//velocityY方向的速度
  30.      float[] accelerated;//加速度
  31.  ​
  32.      public float damping=0.2f;//速度对恢复波动的影响力
  33.      public float heighspeed=2f;//高度对恢复波动的影响力
  34.      public float spread;//传播衰减值
  35.  ​
  36.      //public float MaxOffsetTop=1;//水面最大高度
  37.      //public float MaxOffsetBottom=1;//水面最低深度
  38.  ​
  39.      public float collForce=0.1f;//物体落水力影响值
  40.      private void Start() {
  41.          vertices = new Vector3[quality*2];
  42.          velocitys = new float[quality];
  43.          accelerated = new float[quality * 2];
  44.          leftdetails = new float[quality];
  45.          rightdetails = new float[quality];
  46.          DrawWater(length, width, quality);
  47.          boxCollider = GetComponent<BoxCollider2D>();
  48.          boxCollider.offset = new Vector2(4.8f, 0.74f);
  49.          boxCollider.size = new Vector2(length, width);
  50.          bounds = boxCollider.bounds;
  51.  ​
  52.          //可以自己波动范围限制
  53.          //MaxOffsetTop = width+2;
  54.          //MaxOffsetBottom = 0.5f;
  55.  ​
  56.          MeshRenderer meshRenderer = transform.GetComponent<MeshRenderer>();
  57.          meshRenderer.sortingOrder = 1;
  58.     }
  59.      private void Update() {
  60.          //持续绘制
  61.          //物理
  62.          for (int i = 0; i < quality; i++) {
  63.              //加速度的力
  64.              float force =heighspeed*(vertices[i].y-width)+velocitys[i]*damping;
  65.              //顶点变换
  66.              vertices[i].y +=velocitys[i]
  67.              //这里可以设置力的最大最小范围
  68.                 ;
  69.              accelerated[i] = -force;
  70.              //速度收到加速度和阻力衰减(这个数值自行设置,这里我设置速度大小的1/5)
  71.              velocitys[i] += accelerated[i]-velocitys[i]/5;
  72.         }
  73.          //水扩散和衰减
  74.          for (int i = 0; i < quality; i++) {
  75.              if (i > 0) {
  76.                  leftdetails[i] = spread * (vertices[i].y - vertices[i - 1].y);
  77.                  velocitys[i - 1] += leftdetails[i];
  78.             }
  79.              if (i < quality - 1) {
  80.                  rightdetails[i] = spread * (vertices[i].y - vertices[i + 1].y);
  81.                  velocitys[i + 1] += rightdetails[i];
  82.             }
  83.         }
  84.          //重新绘制顶点
  85.          ReDraw(vertices);
  86.     }
  87.      //绘制水
  88.      public void DrawWater(float length,float width,int quality) {
  89.          //材质
  90.          gameObject.GetComponent<MeshRenderer>().material = mat;
  91.          //顶点
  92.          float interval_length = length/quality;
  93.          //分开设置数组前一部分是上顶点,后一半是下顶点
  94.          //上面顶点
  95.          for (int i = 0; i < quality; i++) {
  96.              vertices[i] = new Vector3(i * interval_length, width, 0);
  97.         }
  98.          //下面顶点
  99.          for (int i = quality; i < 2*quality; i++) {
  100.              vertices[i] = new Vector3((i-quality) * interval_length, 0, 0);
  101.         }
  102.          //设置三角形
  103.          int anglescount = (quality*2-2)*3;
  104.          triangles = new int[anglescount];
  105.  ​
  106.          int current=0;
  107.          for (int i = quality; i < 2 * quality - 1; i++) {
  108.  ​
  109.              triangles[current++] = i;
  110.              triangles[current++] = i-quality;
  111.              triangles[current++] = i-quality+1;
  112.  ​
  113.              triangles[current++] = i;
  114.              triangles[current++] = i - quality + 1;
  115.              triangles[current++] = i + 1;
  116.         }
  117.          //对应坐标UV设置
  118.          Vector2[] uvs = new Vector2[vertices.Length];
  119.          for (int i = 0; i < vertices.Length; i++) {
  120.              uvs[i] = new Vector2(vertices[i].x/length,vertices[i].y/width);
  121.         }
  122.  ​
  123.          Mesh mesh = GetComponent<MeshFilter>().mesh;
  124.          mesh.Clear();
  125.          mesh.RecalculateNormals();
  126.          mesh.RecalculateTangents();
  127.          mesh.vertices = vertices;
  128.          mesh.triangles = triangles;
  129.          mesh.uv = uvs;
  130.     }
  131.      public void ReDraw(Vector3[] currentvertices) {
  132.          //设置三角形
  133.          int anglescount = (quality * 2 - 2) * 3;
  134.          triangles = new int[anglescount];
  135.  ​
  136.          int current = 0;
  137.          for (int i = quality; i < 2 * quality - 1; i++) {
  138.  ​
  139.              triangles[current++] = i;
  140.              triangles[current++] = i - quality;
  141.              triangles[current++] = i - quality + 1;
  142.  ​
  143.              triangles[current++] = i;
  144.              triangles[current++] = i - quality + 1;
  145.              triangles[current++] = i + 1;
  146.         }
  147.          Vector2[] uvs = new Vector2[vertices.Length];
  148.          for (int i = 0; i < vertices.Length; i++) {
  149.              uvs[i] = new Vector2(vertices[i].x / length, Mathf.Max(vertices[i].y / width,1));
  150.         }
  151.  ​
  152.          Mesh mesh = GetComponent<MeshFilter>().mesh;
  153.          mesh.Clear();
  154.          mesh.RecalculateNormals();
  155.          mesh.RecalculateTangents();
  156.          mesh.vertices = currentvertices;
  157.          mesh.triangles = triangles;
  158.          mesh.uv = uvs;
  159.     }
  160.  ​
  161.      private void OnTriggerEnter2D(Collider2D col) {
  162.          Vector2 velocity = col.GetComponent<Rigidbody2D>().velocity;
  163.          FallIn(col, velocity.y*collForce);
  164.          Debug.Log("Enter Water");
  165.     }
  166.      private void OnTriggerStay2D(Collider2D col) {
  167.          Vector2 velocity = col.GetComponent<Rigidbody2D>().velocity;
  168.          //持续呆在水中力为上方向,这里设置速度的绝对值
  169.          StayWater(col, Mathf.Abs(velocity.x) * collForce);
  170.     }
  171.      private void OnTriggerExit2D(Collider2D col) {
  172.          Vector2 velocity = col.GetComponent<Rigidbody2D>().velocity;
  173.          JumpOut(col,velocity.y*collForce);
  174.          Debug.Log("Out Water");
  175.     }
  176.      //落入水中
  177.      private void FallIn(Collider2D col,float force) {
  178.          //原点,因为绘制的顶点并不是世界坐标,需要加上原点的位置才是世界坐标
  179.          Vector2 originalpos = transform.position;
  180.          //计算物体包围盒宽度
  181.          float radius = col.bounds.max.x - col.bounds.min.x;
  182.          //计算中心点
  183.          Vector2 center = new Vector2(col.bounds.center.x,width);
  184.          //计算每个受力的大小,赋予初始速度
  185.          for (int i = 0; i < quality; i++) {
  186.              //只计算X距离
  187.              float dis = Vector2.Distance(new Vector2(originalpos.x+vertices[i].x,width), center);
  188.              if (dis < radius) {
  189.                  velocitys[i] = force * (radius - dis) / radius;
  190.             }
  191.         }
  192.     }
  193.      //跃出水面
  194.      private void JumpOut(Collider2D col,float force) {
  195.          Vector2 originalpos = transform.position;
  196.          float radius = col.bounds.max.x - col.bounds.min.x;
  197.          Vector2 center = new Vector2(col.bounds.center.x, width);
  198.          for (int i = 0; i < quality; i++) {
  199.              float dis = Vector2.Distance(new Vector2(originalpos.x + vertices[i].x, width), center);
  200.              if (dis < radius) {
  201.                  velocitys[i] = force * (radius - dis) / radius;
  202.             }
  203.         }
  204.     }
  205.      //水中移动
  206.      private void StayWater(Collider2D col,float force) {
  207.          //水中移动影响当前速度,不是赋予初始速度
  208.          Vector2 originalpos = transform.position;
  209.          float radius = col.bounds.max.x - col.bounds.min.x;
  210.          Vector2 center = new Vector2(col.bounds.center.x, width);
  211.          for (int i = 0; i < quality; i++) {
  212.              float dis = Vector2.Distance(new Vector2(originalpos.x + vertices[i].x, width), center);
  213.              if (dis < radius) {
  214.                  velocitys[i] += (force) * dis /5;
  215.             }
  216.         }
  217.     }
  218.  }

项目地址

2946952974/WaterMeshRender: 水面模拟 (github.com)

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号