Image for post
Image for post

[Tutorial] Particle Sea in Unity3D

When I saw it, I was totally shocked. Firstly — it looked amazing, Secondly — it was playing smoothly in my browser on a non-high-end PC. That was my reaction to this: http://i-remember.fr/en. I immediately fell in love with that and the three.js library. I knew I had to recreate the same effect in Unity3d.

Hi,

Today I decided to share with you my spare time project — Particle Sea and also show you step by step how I managed to create such an effect.

In short, what we need to do:

  1. Create Particle System and array of particles
  2. Assign particles positions & create Perlin Noise
  3. Make everything move nice and smoothly — put in loop
  4. Color!

Let’s Start!

I assume that you have already created a new, empty project and scene. If not, do so right now, I’ll wait for you. As soon as you’ve done that, create a new empty game object and place it at the origin. This will be the base of our “sea”. Then create a new C# script named ParticleSea and add it to the object. We’ll also a need ParticleSystem component so go ahead and add one.

Image for post
Image for post
That’s how it should look like

Project Setup

As I said before, at first we need to create a particle system (we’ve done that already) and create an array of particles. Simply open the ParticleSea class you’ve just created and paste following code. Don’t worry, I’ll explain it all thoroughly.

using UnityEngine; 
using System.Collections;
public class ParticleSea : MonoBehaviour {
public ParticleSystem particleSystem;
private ParticleSystem.Particle[] particlesArray;
public int seaResolution = 25;
void Start() {
particlesArray = new ParticleSystem.Particle[seaResolution * seaResolution];
particleSystem.maxParticles = seaResolution * seaResolution;
particleSystem.Emit(seaResolution * seaResolution);
particleSystem.GetParticles(particlesArray);
}
}
Image for post
Image for post
Don’t forget to assign ParticleSystem

Assigning particles’ position

Right now we have our system creating particles. We also have them assigned in particlesArray, so we can start playing with them. What we would like to do is iterate through each one of them and assign a unique position. First we would like to create a simple grid/plane made of these. Let’s start with a new variable:

public float spacing = 0.25f;
for(int i = 0; i < seaResolution; i++) { 
for(int j = 0; j < seaResolution; j++) {
particlesArray[i * seaResolution + j].position = new Vector3(i * spacing, j * spacing, 0);
}
}
particleSystem.SetParticles(particlesArray, particlesArray.Length);
Image for post
Image for post
Looping: False, Prewarm: False, Start Lifetime: 100, Play on Awake: False, Emission Rate: 0
Image for post
Image for post

Perlin Noise

I can honestly say it’s great tool/algorithm and I can guarantee you’ll use it many times in your Unity projects. In this tutorial I won’t cover what precisely Perlin Noise is but if you would like to broaden your knowledge in that topic check out Wikipedia or Unity Docs.
In our case the only thing you need to know about this noise is that it will produce nice Z-axis output basing on X-Axis and Y-Axis. In order to apply Perlin Noise we’ll need to introduce two new public variables:

public float noiseScale = 0.2f; 
public float heightScale = 3f;
for(int i = 0; i < seaResolution; i++) { 
for(int j = 0; j < seaResolution; j++) {
float zPos = Mathf.PerlinNoise(i * noiseScale, j * noiseScale) * heightScale;
particlesArray[i * seaResolution + j].position = new Vector3(i * spacing, zPos, j * spacing);
}
}
Image for post
Image for post
Static Particle Sea — so far so good

Animating Sea

The most exciting part I guess. And it’s all pretty simple. We only need to move some code to Update() function so it is be executed in every frame and leave some in start function and add two or three new variables which will be changing with time. The code that is executed every frame should be something like this:

void Update() { 
for(int i = 0; i < seaResolution; i++) {
for(int j = 0; j < seaResolution; j++) {
float zPos = Mathf.PerlinNoise(i * noiseScale + perlinNoiseAnimX, j * noiseScale + perlinNoiseAnimY) * heightScale;
particlesArray[i * seaResolution + j].position = new Vector3(i * spacing, zPos, j * spacing);
}
}
perlinNoiseAnimX += 0.01f; perlinNoiseAnimY += 0.01f;
particleSystem.SetParticles(particlesArray, particlesArray.Length);
}
private float perlinNoiseAnimX = 0.01f; 
private float perlinNoiseAnimY = 0.01f;
void Start() { 
particlesArray = new ParticleSystem.Particle[seaResolution * seaResolution];
particleSystem.maxParticles = seaResolution * seaResolution;
particleSystem.Emit(seaResolution * seaResolution);
particleSystem.GetParticles(particlesArray);
}
Image for post
Image for post
Feel free to play with values in inspector

Animating particles color

We can make the whole thing even more awesome just by making valleys transparent and changing the colour of the hills. here, the Gradient class is very handy. Simply add another variable:

public Gradient colorGradient;
Image for post
Image for post
float zPos = Mathf.PerlinNoise(i * noiseScale + perlinNoiseAnimX, j * noiseScale + perlinNoiseAnimY); particlesArray[i * seaResolution + j].color = colorGradient.Evaluate(zPos); particlesArray[i * seaResolution + j].position = new Vector3(i * spacing, zPos * heightScale, j * spacing);
Image for post
Image for post
Black camera background, Resolution: 100, Spacing: 0.3, Noise Scale: 0.05, Height Scale: 3

Written by

Founder of https://dynobase.dev, AWS Certified Architect, love minimalism and procedural content generation.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store