Unity如何制作小球擠捏變形效果並回彈


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

 

通過修改mesh的頂點位置來做,應該算是頂點動畫的范圍了吧(*/ω\*)。

 

代碼不會很復雜,主要是理解原理。

 

我這個實現是參考:

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

 

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

 

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

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

 

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

 

 

 

首先定義一下要變形mesh:

    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和頂點方面的計算,如果發現有什么錯漏,請指出。

歡迎交流。

 

轉載注明出處。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM