I haven’t really finished optimizing it, there is a lot more performance you could squeeze out of this with micro optimizations. And its probably hammering the GC. I also cheated on dealing with the edge case.
The script provides verticies that behave like water. To get it to look like water you are probably want to attach textures and shader magic.
Simply attach both scripts to a GameObject with a MeshFilter and a MeshRenderer and hit play.
public class WaterMaker : MonoBehaviour {
[SerializeField] int waterLength = 100;
[SerializeField] int waterWidth = 100;
Vector4 [,] water;
Mesh waterMesh;
Vector3[] vertices;
[SerializeField, Range(0,1)] float damping = 0.1f;
[SerializeField, Range(0,100)] float waveSpeed = 10;
void Start () {
MakeWater();
MakeWaterMesh();
}
void Update () {
UpdateWater ();
UpdateMesh ();
}
void MakeWaterMesh(){
List<Vector3> verticies = new List<Vector3>();
foreach(Vector4 point in water){
verticies.Add ((Vector3)point);
}
waterMesh = new Mesh();
waterMesh.MarkDynamic ();
waterMesh.name = "Water Mesh";
List<int> triangles = new List<int>();
for (int i = 0; i < waterLength - 1; i++){
for (int j = 0; j < waterWidth - 1; j++){
triangles.Add (i * waterWidth + j);
triangles.Add (i * waterWidth + j + 1);
triangles.Add ((i+1) * waterWidth + j);
}
}
for (int i = 1; i < waterLength; i++){
for (int j = 1; j < waterWidth; j++){
triangles.Add (i * waterWidth + j);
triangles.Add (i * waterWidth + j - 1);
triangles.Add ((i-1) * waterWidth + j);
}
}
waterMesh.vertices = verticies.ToArray ();
vertices = verticies.ToArray ();
waterMesh.triangles = triangles.ToArray ();
waterMesh.RecalculateNormals();
waterMesh.RecalculateBounds();
GetComponent<MeshFilter>().mesh = waterMesh;
}
void MakeWater(){
water = new Vector4[waterLength,waterWidth];
for (int i = 0; i < waterLength; i++){
for (int j = 0; j < waterWidth; j++){
water [i,j] = new Vector4(i,0,j,0);
}
}
}
void UpdateWater (){
for (int i = 1; i < waterLength - 1; i++){
for (int j = 1; j < waterWidth - 1; j++){
water[i,j].w -= ((water[i,j].y - water[i-1,j].y)
+ (water[i,j].y - water[i+1,j].y)
+ (water[i,j].y - water[i,j-1].y)
+ (water[i,j].y - water[i,j+1].y)) * Time.deltaTime * waveSpeed;
}
}
for (int i = 0; i < waterLength; i++){
for (int j = 0; j < waterWidth; j++){
water [i,j].w *= Mathf.Pow((1-damping),Time.deltaTime);
water[i,j].y += water[i,j].w * Time.deltaTime;
}
}
}
void UpdateMesh (){
for (int i = 0; i < waterLength; i++){
for (int j = 0; j < waterWidth; j++){
vertices[i * waterWidth + j] = (Vector3)water[i,j];
}
}
waterMesh.vertices = vertices;
}
public void MoveVertix (int x, int z, float amount){
water[x,z].y += amount;
}
}
And a little bit of randomization to get things started:
public class FakeWave : MonoBehaviour {
[SerializeField] float period = 1f;
[SerializeField] float magnitude = 5f;
void Start () {
StartCoroutine (Wave());
}
IEnumerator Wave (){
WaterMaker water = GetComponent<WaterMaker>();
while (true){
yield return new WaitForSeconds(period);
int x = Random.Range(2,98);
int z = Random.Range(2,98);
water.MoveVertix (x,z,magnitude);
water.MoveVertix (x-1,z,magnitude);
water.MoveVertix (x+1,z,magnitude);
water.MoveVertix (x,z-1,magnitude);
water.MoveVertix (x,z+1,magnitude);
}
}
}