Bài viết này sẽ hướng dẫn cách làm biến dạng mesh trong khi chạy runtime và sử dụng được trên các low-end device. Cụ thể, trong bài viết này mình sẽ tạo hiệu ứng lõm Terrain (có mesh như một model 3D - không phải built-in Terrain của Unity) khi xúc đất.

Tổng quan

Để tạo được hiệu ứng này, chúng ta cần phải xử lý 3 bước:

  • Bước 1: Thực hiện làm lõm mesh khi xúc đất.
  • Bước 2: Tô màu xung quanh vị trí làm lõm.
    Khi xúc đất, nền đất bên dưới không thể giống như bề mặt đất trước khi xúc được.
  • Bước 3: Sau khi làm lõm, chúng ta cần phải update lại collider cho mesh.
    Khi chúng ta di chuyển tới khu vực bị làm lõm, không thể đứng như trên đất bằng như trước khi xúc.

Thực hiện làm lõm mesh

Tạo script DeformableMesh.cs và gắn vào Terrain.

public float maximumDepression;
private MeshFilter meshFilter;
private List<Vector3> originalVertices;
private List<Vector3> modifiedVertices;

void Start()
{
    MeshRegenerated();  
}

void MeshRegenerated()
{
    meshFilter = GetComponent<MeshFilter>();
    meshFilter.mesh.MarkDynamic();
    originalVertices = meshFilter.mesh.vertices.ToList();
    modifiedVertices = meshFilter.mesh.vertices.ToList();
}

public void AddDepression(Vector3 depressionPoint, float radius)
{
    var worldPos4 = this.transform.worldToLocalMatrix * depressionPoint;
    var worldPos = new Vector3(worldPos4.x, worldPos4.y, worldPos4.z);
    for (int i = 0; i < modifiedVertices.Count; ++i)
    {
        var distance = (worldPos - (modifiedVertices[i] + Vector3.down * maximumDepression)).magnitude;
        if (distance < radius)
        {
            var newVert = originalVertices[i] + Vector3.down * maximumDepression;
            modifiedVertices.RemoveAt(i);
            modifiedVertices.Insert(i, newVert);
        }
    }
    meshFilter.mesh.SetVertices(modifiedVertices);
}

Trong đó, maximumDepression là độ sâu lớn nhất mà mesh bị biến dạng.
Bản chất của hàm Addpression() là duyệt qua list tất cả các đỉnh của mesh, sau đó tính toán các đỉnh nào sẽ bị biến dạng dựa trên điểm va chạm depressionPoint, radius và cập nhật lại dữ liệu đỉnh của mesh. Do vậy, ảnh hưởng của script này đến hiệu năng của ứng dụng sẽ phụ thuộc vào tần suất nó được gọi và số đỉnh của Terrain. Terrain càng phức tạp thì ảnh hưởng của nó đến hiệu năng càng lớn. Vì vậy, bạn sẽ cần phải chú ý đến số verts của Terrain và tìm các cách để giảm thiểu nó.

Tiếp theo, tạo script PhysicsDeformer.cs và gắn vào object va chạm với Terrain mà cần tạo hiệu ứng này.

public float collisionRadius = 0.1f;
public DeformableMesh deformableMesh;
private void OnCollisionEnter(Collision collision)
{
    if (collision.gameObject.CompareTag("Terrain"))
    {
        ContactPoint[] contacts = collision.contacts;
        foreach(ContactPoint contact in contacts)
        {
            deformableMesh.AddDepression(contact.point, collisionRadius);
        }
    }
}

Script này đơn giản là tìm điểm va chạm giữa object gắn nó và Terrain. collisionRadius sẽ quyết định bán kính làm lõm tại điểm va chạm ( số lượng các đỉnh bị làm lõm xung quanh điểm va chạm). Với số lượng contact càng lớn, tần suất gọi hàm AddDepression() càng nhiều. Do vậy, tuỳ theo target device của bạn mà tuỳ chỉnh tần suất gọi hàm. Đối với các low-end device, bạn có thể chỉ gọi hàm 1 lần bằng thay contact.point = collision.contacts[0].point ( điểm va chạm đầu tiên).