# Dynamic 2D Water in Unity

In this tutorial we are going to simulate dynamic 2D water in Unity with simple physics calculation. This post is actually inspired by this article from tutsplus. The problem with that method is the result is not quite optimized (around 200 drawcalls). In this post, we will try to make the more optimized version.

#### Steps:

• Create an empty Gameobject and attach a new script called DynamicWater2D.cs (or whatever name you want. really.)
• Edit DynamicWater2D.cs  and declare all variables we need
``````[System.Serializable]
public struct Bound {
public float top;
public float right;
public float bottom;
public float left;
}

public Bound bound;
public int quality;

public Material waterMaterial;
public GameObject splash;

private Vector3[] vertices;

private Mesh mesh;

public float springconstant = 0.02f;
public float damping = 0.1f;
public float collisionVelocityFactor = 0.04f;

float[] velocities;
float[] accelerations;
float[] leftDeltas;
float[] rightDeltas;

private float timer;``````
• This part of code is totally different from tutsplus tutorial. We want to make it only 1 mesh rather than so many meshes joined together
``````private void GenerateMesh () {
float range = (bound.right - bound.left) / (quality - 1);
vertices = new Vector3[quality * 2];

// generate vertices
// top vertices
for (int i = 0; i < quality; i++) {
vertices[i] = new Vector3 (bound.left + (i * range), bound.top, 0);
}
// bottom vertices
for (int i = 0; i < quality; i++) {
vertices[i + quality] = new Vector2 (bound.left + (i * range), bound.bottom);
}

// generate tris. the algorithm is messed up but works. lol.
int[] template = new int;
template = quality;
template = 0;
template = quality + 1;
template = 0;
template = 1;
template = quality + 1;

int marker = 0;
int[] tris = new int[((quality - 1) * 2) * 3];
for (int i = 0; i < tris.Length; i++) {
tris[i] = template[marker++]++;
if (marker >= 6) marker = 0;
}

// generate mesh
if (waterMaterial) meshRenderer.sharedMaterial = waterMaterial;

mesh = new Mesh ();
mesh.vertices = vertices;
mesh.triangles = tris;
mesh.RecalculateNormals ();
mesh.RecalculateBounds ();

// set up mesh
meshFilter.mesh = mesh;
}``````
• Don’t forget to add a Collider. This part is also different. We only need 1 big Collider rather than so many colliders placed side by side
``````private void SetBoxCollider2D () {
col.isTrigger = true;
}``````
• Now the update function. Pretty much the same with the tutorial from tutsplus. Except, I added a timer so it won’t update the physics all the time. It’s just for optimization sake.
``````private void Update () {
// optimization. we don't want to calculate all of this on every update.
if(timer <= 0) return;
timer -= Time.deltaTime;

// updating physics
for (int i = 0; i < quality; i++) {
float force = springconstant * (vertices[i].y - bound.top) + velocities[i] * damping;
accelerations[i] = -force;
vertices[i].y += velocities[i];
velocities[i] += accelerations[i];
}

for (int i = 0; i < quality; i++) {
if (i > 0) {
leftDeltas[i] = spread * (vertices[i].y - vertices[i - 1].y);
velocities[i - 1] += leftDeltas[i];
}
if (i < quality - 1) {
rightDeltas[i] = spread * (vertices[i].y - vertices[i + 1].y);
velocities[i + 1] += rightDeltas[i];
}
}

// updating mesh
mesh.vertices = vertices;
}``````
• The last piece of code is adding functions to detect collisions with other objects.
``````private void OnTriggerEnter2D(Collider2D col) {
Rigidbody2D rb = col.GetComponent<Rigidbody2D>();
Splash(col, rb.velocity.y * collisionVelocityFactor);
}

public void Splash (Collider2D col, float force) {
timer = 3f;
float radius = col.bounds.max.x - col.bounds.min.x;
Vector2 center = new Vector2(col.bounds.center.x, bound.top) ;

// instantiate splash particle
GameObject splashGO = Instantiate(splash, new Vector3(center.x, center.y, 0), Quaternion.Euler(0,0,60));
Destroy(splashGO, 2f);

// applying physics
for (int i = 0; i < quality; i++) {
if (PointInsideCircle (vertices[i], center, radius)) {
velocities[i] = force;
}
}
}

bool PointInsideCircle (Vector2 point, Vector2 center, float radius) {
return Vector2.Distance (point, center) < radius;
}``````
• That’s all for our DynamicWater2D.cs. Now, create a new script to test the simulation. We want to randomly spawn boxes that fall into the water.
``````using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace ilhamhe {

public class BoxSpawner : MonoBehaviour {
public GameObject box;

private void Update () {
if (Input.GetKeyDown (KeyCode.Space)) {
GameObject go = Instantiate (box, new Vector3 (Random.Range (-7f, 7f), Random.Range (3f, 5f), 0), Quaternion.identity) as GameObject;
go.transform.localScale = new Vector2(Random.Range(.5f, 1.5f), Random.Range(.5f, 1.5f));
Destroy(go, 3f);
}
}
}

}``````
• To test it out, attach BoxSpawner.cs to empty Gameobject and set up some prefabs for our boxes. The result will look like this:

That’s all for this tutorial. If you have any question, don’t hesitate to write a comment. And also, you can download the whole project here from Github: Unity 2D Dynamic Water