Unity如何製做小球擠捏變形效果並回彈

如何製做一個小球,能擠壓變形,並有彈力恢復原形?php

 

經過修改mesh的頂點位置來作,應該算是頂點動畫的範圍了吧(*/ω\*)。html

 

代碼不會很複雜,主要是理解原理。算法

 

我這個實現是參考:spring

https://catlikecoding.com/unity/tutorials/mesh-deformation/數組

 

先上效果,網格圖是側視:函數

 

建議往下閱讀前,先看一下文檔中關於mesh和頂點的相關概念(Procedural Mesh Geometry下面三個子主題),還有理解向量的概念:工具

https://docs.unity3d.com/Manual/GeneratingMeshGeometryProcedurally.html動畫

 

擠壓小球的時候,主要受力區域會凹陷,而後迫使其餘部位順着力的方向變形。spa

 

 

 

首先定義一下要變形mesh:3d

    public MeshFilter targetMeshFilter;
    private Mesh targetMesh;

並在start中獲取mesh:

void Start()
{
        targetMesh = targetMeshFilter.mesh;
}

觸摸操做用射線來實現,先定義從哪一個相機射出射線:

public Camera mainCamera;

再在update寫下射線代碼:

void Update()
{
  if (Input.GetMouseButton(0))
  {
    if (Physics.Raycast(mainCamera.ScreenPointToRay(Input.mousePosition), out RaycastHit hitInfo))
    {
               
    }
   }
}

定義一些用到的數組:

private Vector3[] originalVertices, displacedVertices, vertexVelocities;

private int verticesCount;

在start中初始化,從上往下,分別含義是這個mesh頂點數量,初始的頂點位置,頂點下一步的位置,頂點移動速度:

 void Start()
    {
      ... verticesCount
= targetMesh.vertices.Length; originalVertices = targetMesh.vertices; displacedVertices = targetMesh.vertices; vertexVelocities = new Vector3[verticesCount]; }

在觸摸到小球時,先定義一下要用到的觸摸力度,發力點偏移量:

    public float force = 10;
    public float forceOffset = 0.1f;

擠壓小球並回彈,因此小球須要有觸摸力度表示變形程度,發力點偏移量表示做用點的位置,0偏移就是在球體表面,值越高越偏離球表面。

 

在射線觸碰成功的代碼裏面添加如下:

Vector3 actingForcePoint = targetMeshFilter.transform.InverseTransformPoint(hitInfo.point + hitInfo.normal * forceOffset);//發力點指向球的本地座標向量

for (int i = 0; i < verticesCount; i++)
{
   Vector3 pointToVertex = displacedVertices[i] - actingForcePoint;//做用力點指向當前頂點位置的向量

   float actingForce = force / (1f + pointToVertex.sqrMagnitude);//做用力大小
   vertexVelocities[i] += pointToVertex.normalized * actingForce * Time.deltaTime;//頂點速度向量
}

解釋一下,就是頂點的座標位置都是相對於這個模型的座標不是世界座標,因此觸摸的時候,發力點座標要轉換成相對座標。

hitInfo.normal也就是觸摸點的法線,是垂直於觸摸點並指向外面的,forceOffset值表示做用點離表面有多高,1表示法線長度那麼高,0表示在表面,以下圖,棕色表示觸摸點,紅色表示法線。

 

還有做用力actingForce,球的各個頂點受力,若是是一致的,那麼球就是平行飛出去,而不是變形了, 觸摸點受力最大,而後輻射出去逐漸衰減。這裏用了一條函數來計算,y=force/(1+x^2),當force值爲10,爲5,爲1時函數圖像以下,能夠根據本身的狀況調整衰減力度,這裏用的函數圖像繪製工具地址是:https://zh.numberempire.com/graphingcalculator.php ,百度隨便找的。

 

 

 

有了做用力,還要有回彈以恢復形狀,和阻力來消除做用力和彈力,還有從新把頂底從新賦值,並從新計算法線(影響光照):

定義一下:

    public float springForce = 20f;
    public float damping = 5f;

而後在update中:

 for (int i = 0; i < verticesCount; i++)
 {
   vertexVelocities[i] += (originalVertices[i] - displacedVertices[i]) * springForce * Time.deltaTime;//加上+頂點當前位置指向頂點初始位置的速度向量==回彈力
   vertexVelocities[i] *= 1f - damping * Time.deltaTime;//乘上阻力
   displacedVertices[i] += vertexVelocities[i] * Time.deltaTime;//算出頂點的下一個位置
 }

 targetMesh.vertices = displacedVertices;
 targetMesh.RecalculateNormals();

到此,就完成了,下面是完整代碼:

 1 using UnityEngine;
 2 
 3 public class DeformationToucher : MonoBehaviour
 4 {
 5     public MeshFilter targetMeshFilter;
 6     private Mesh targetMesh;
 7 
 8     public Camera mainCamera;
 9 
10     private Vector3[] originalVertices, displacedVertices, vertexVelocities;
11 
12     private int verticesCount;
13 
14     public float force = 10;
15     public float forceOffset = 0.1f;
16     public float springForce = 20f;
17     public float damping = 5f;
18 
19     void Start()
20     {
21         targetMesh = targetMeshFilter.mesh;
22 
23         verticesCount = targetMesh.vertices.Length;
24 
25         originalVertices = targetMesh.vertices;
26         displacedVertices = targetMesh.vertices;
27         vertexVelocities = new Vector3[verticesCount];
28     }
29 
30     void Update()
31     {
32         if (Input.GetMouseButton(0))
33         {
34             if (Physics.Raycast(mainCamera.ScreenPointToRay(Input.mousePosition), out RaycastHit hitInfo))
35             {
36                 Vector3 actingForcePoint = targetMeshFilter.transform.InverseTransformPoint(hitInfo.point + hitInfo.normal * forceOffset);//發力點指向球的本地座標向量
37 
38                 for (int i = 0; i < verticesCount; i++)
39                 {
40                     Vector3 pointToVertex = displacedVertices[i] - actingForcePoint;//做用力點指向當前頂點位置的向量
41 
42                     float actingForce = force / (1f + pointToVertex.sqrMagnitude);//做用力大小
43                     vertexVelocities[i] += pointToVertex.normalized * actingForce * Time.deltaTime;//頂點速度向量
44                 }
45             }
46         }
47 
48         for (int i = 0; i < verticesCount; i++)
49         {
50             vertexVelocities[i] += (originalVertices[i] - displacedVertices[i]) * springForce * Time.deltaTime;//加上+頂點當前位置指向頂點初始位置的速度向量==回彈力
51             vertexVelocities[i] *= 1f - damping * Time.deltaTime;//乘上阻力
52             displacedVertices[i] += vertexVelocities[i] * Time.deltaTime;//算出頂點的下一個位置
53         }
54 
55         targetMesh.vertices = displacedVertices;
56         targetMesh.RecalculateNormals();
57     }
58 }

 

這是我第一次接觸mesh和頂點方面的計算,若是發現有什麼錯漏,請指出。

歡迎交流。

 

轉載註明出處。

相關文章
相關標籤/搜索