📜 ⬆️ ⬇️

Unity3d We play with the mesh. Part 3 - Collision Based Mesh Warp

Many were waiting for the continuation of the series, and certainly missed the new article. Therefore I will not begin to weary more, and today we will continue our games with a mesh in Unity3D, and also we will expand the luggage of knowledge and skills.


Today we will deal with deformation based on collisions. Well and, of course, everyone who is interested, welcome under cat.



A frame from the old Soviet cartoon "Brack"


Content


  1. Unity3d We play with the mesh. Part 1 - Generating a mesh using an elevation map
  2. Unity3d We play with the mesh. Part 2 - Deformation of the mesh with a height map
  3. Unity3d We play with the mesh. Part 3 - Collision Based Mesh Warp

Warning: there will be few pictures.


Collision theory


To deform the mesh based on collisions, we need to understand what a collision is. And in fact there is nothing complicated about it. A collision is a collision of bodies that have the ability to collide. That is, in our case, have colliders and Rigidbody, at a minimum.


For handling collisions, Unity has 3 methods with a required argument of Collision :


OnCollisionEnter (Collision) , OnCollisionStay (Collision) , OnCollisionExit (Collision)


We will use one of them. What do you think?


OnCollisionEnter

Nearly! :) This method works on the first collision. But there is a possibility that the colliders will not stop contact, and the positions of the objects will change.


OnCollisionStay

Exactly! This is what we need. This method is triggered at each collision of colliders.


OnCollisionExit

Not exactly what we need. This method will work after the contact of the colliders is terminated.


With the method sorted out, let's understand what is useful conflict and what attributes it owns.


We will not go into details, and we will describe things that are useful for our implementation. Collision has an array of points of contact - ContacPoint , which in turn have very useful attributes: point and normal (yes, and there is a normal here). point - is responsible for the coordinates of the collision, and normal - for the vector under which the collision occurs.


!!! In order for a collision to be fixed, at least one body must have a Rigidbody component !!!


Implementation theory


With a collision like everything is clear. It remains to think about how to make the deformation.


All we need to do is find out the collision points and shift the vertices of the mesh at these points along the collision normal vector multiplied by some constant. We will need only 2 parameters - the radius for finding the vertices and the constant for the displacement of the vertex along the collision normal vector. We will need the radius in order to find the necessary vertices, because in my opinion the best way to do this is to check for distance.


What could be the difficulty? The mesh is always locally, that is, in zero coordinates. But this complexity is solved by the built-in Unity methods.


Start writing a solution


Let's create the DeformableGO class and add the attributes we need, as well as several private components in order to save references to them.


DeformableGO
 using UnityEngine; public class DeformableGO : MonoBehaviour { public float maxDeformDelta = 1f; //   public float radius = 0.5f; //   MeshFilter mf; Vector3[] vertices; Transform trans; } 

It is important for us to keep the original mesh, so that before the start of the scene we will get all the necessary links, and also copy our mesh


DeformableGO
 using UnityEngine; public class DeformableGO : MonoBehaviour { public float maxDeformDelta = 1f; //   public float radius = 0.5f; //   MeshFilter mf; Vector3[] vertices; Transform trans; void Awake() { trans = GetComponent<Transform>(); mf = gameObject.GetComponent<MeshFilter>(); Mesh mesh = CopyMesh(mf.sharedMesh); mf.sharedMesh = mesh; vertices = mesh.vertices; } Mesh CopyMesh(Mesh oldmesh) { Mesh newmesh = new Mesh(); newmesh.vertices = oldmesh.vertices; newmesh.triangles = oldmesh.triangles; newmesh.uv = oldmesh.uv; newmesh.normals = oldmesh.normals; newmesh.tangents = oldmesh.tangents; newmesh.colors = oldmesh.colors; newmesh.bounds = oldmesh.bounds; newmesh.MarkDynamic(); //   return newmesh; } } 

You could see the comment "new method" opposite the method that we have not yet used. Unity Techologies strongly advised to use it at one of the conferences, if we work with a dynamically changeable mesh, because this method optimizes the mesh for frequent changes. Documentation can be found here .


Let's add the OnCollisionStay(Collision) processing and create a stub for the mesh deforming method.


OnCollisionStay + PressMesh ()
 //   ,     , //   ,      //   .      //  `Vector3 dir`,    - `Vector3 point` void PressMesh(Vector3 point, Vector3 dir) { } void OnCollisionStay(Collision collision) { for (int i = 0; i < collision.contacts.Length; i++) { PressMesh(collision.contacts[i].point, collision.contacts[i].normal); } mf.sharedMesh.vertices = vertices; mf.sharedMesh.RecalculateNormals(); mf.sharedMesh.RecalculateBounds(); } 

Let's implement the logic of the method PressMesh


PressMesh
  void PressMesh(Vector3 point, Vector3 dir) { //      //       var localPos = trans.InverseTransformPoint(point); for (int i = 0; i < vertices.Length; i++) { float distance = (localPos - vertices[i]).magnitude; if (distance <= radius) { vertices[i] += dir * maxDeformDelta; } } } 

The full script should look like this.


DeformableGO.cs
 using UnityEngine; public class DeformableGO : MonoBehaviour { public float maxDeformDelta = 1f; public float radius = 0.5f; MeshFilter mf; Vector3[] vertices; Transform trans; void Awake() { trans = GetComponent<Transform>(); mf = gameObject.GetComponent<MeshFilter>(); Mesh mesh = CopyMesh(mf.sharedMesh); mf.sharedMesh = mesh; vertices = mesh.vertices; } void PressMesh(Vector3 point, Vector3 dir) { var localPos = trans.InverseTransformPoint(point); for (int i = 0; i < vertices.Length; i++) { float distance = (localPos - vertices[i]).magnitude; if (distance <= radius) { vertices[i] += dir * maxDeformDelta; } } } void OnCollisionStay(Collision collision) { for (int i = 0; i < collision.contacts.Length; i++) { PressMesh(collision.contacts[i].point, collision.contacts[i].normal); } mf.sharedMesh.vertices = vertices; mf.sharedMesh.RecalculateNormals(); mf.sharedMesh.RecalculateBounds(); } Mesh CopyMesh(Mesh oldmesh) { Mesh newmesh = new Mesh(); newmesh.vertices = oldmesh.vertices; newmesh.triangles = oldmesh.triangles; newmesh.uv = oldmesh.uv; newmesh.normals = oldmesh.normals; newmesh.tangents = oldmesh.tangents; newmesh.colors = oldmesh.colors; newmesh.bounds = oldmesh.bounds; newmesh.MarkDynamic(); return newmesh; } } 

We are testing


I hung the script on the sphere from the last part, placing it in the coordinates (0, 7, -10), and threw on it several ordinary spheres with Rigidbody , possessing gravity:



Advantages and disadvantages



')

Source: https://habr.com/ru/post/329146/


All Articles