# Make a realistic boat in Unity with C#

## 3. Add buoyancy so the boat can float

### Introduction

Our cube from the last part is sinking to the bottom of the ocean like a stone. To make it float realistically we have to add a force to it called buoyancy. The main reason I decided to make a boat in Unity was that I read an article in Gamasutra called "Water interaction model for boats in video games." It will explain the math and physics behind what we are going to do in this part, so read it before you begin creating the code. I have also used the same names on the variables as in that article to make it a little easier to follow the code because things will get complicated.

The basic summary of the Gamasutra article is that you have to find out which parts of the boat is below the water. Then you should add a buoyancy force to those parts.

What you need to know is that if you create a 3d object it will consist of triangles. Even the most complicated 3d object, like the ones you see in movies like Shrek, consist of a lot of triangles, but you can't see them because they are so small. So each side in our cube consists of 2 triangles.

In Unity you have to deal with at least 2 arrays to control the triangles. One triangle consists of 3 so-called vertices that each has a coordinate in 3d-space like x,y,z. A vertice is a corner in the triangle. One array will store these. The other array will store in which order the vertices form a triangle. When you build a triangle in Unity you have to store the position of the vertices in the array so they form a clockwise loop through all the corners. If you happen to store them counter-clockwise the triangle will be inside out and you will not see it (you will see it if you move to the other side of the triangle).

So to sum up: The boat (cube) consists of triangles. We need to find out if one of the triangles is submerged. If the entire triangle is below the water then we can just store it and add buoyancy to the entire triangle. But if only a part of the triangle is below the water, then we have to cut it in pieces and store the pieces that are below the water and add buoyancy to those pieces. It will look like this:

To make this work we need the following scripts:

• BoatPhysics

• ModifyBoatMesh

• TriangleData

• WaterController

### BoatPhysics

This script will act as the parent to all other scripts we need. This is the only script we have to add to the cube to make it float.

```using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace BoatTutorial
{
public class BoatPhysics : MonoBehaviour
{
//Drags
public GameObject underWaterObj;

//Script that's doing everything needed with the boat mesh, such as finding out which part is above the water
private ModifyBoatMesh modifyBoatMesh;

//Mesh for debugging
private Mesh underWaterMesh;

//The boats rigidbody
private Rigidbody boatRB;

//The density of the water the boat is traveling in
private float rhoWater = 1027f;

void Start()
{
//Get the boat's rigidbody
boatRB = gameObject.GetComponent<Rigidbody>();

//Init the script that will modify the boat mesh
modifyBoatMesh = new ModifyBoatMesh(gameObject);

//Meshes that are below and above the water
underWaterMesh = underWaterObj.GetComponent<MeshFilter>().mesh;
}

void Update()
{
//Generate the under water mesh
modifyBoatMesh.GenerateUnderwaterMesh();

//Display the under water mesh
modifyBoatMesh.DisplayMesh(underWaterMesh, "UnderWater Mesh", modifyBoatMesh.underWaterTriangleData);
}

void FixedUpdate()
{
//Add forces to the part of the boat that's below the water
if (modifyBoatMesh.underWaterTriangleData.Count > 0)
{
}
}

//Add all forces that act on the squares below the water
{
//Get all triangles
List<TriangleData> underWaterTriangleData = modifyBoatMesh.underWaterTriangleData;

for (int i = 0; i < underWaterTriangleData.Count; i++)
{
//This triangle
TriangleData triangleData = underWaterTriangleData[i];

//Calculate the buoyancy force
Vector3 buoyancyForce = BuoyancyForce(rhoWater, triangleData);

//Add the force to the boat

//Debug

//Normal
Debug.DrawRay(triangleData.center, triangleData.normal * 3f, Color.white);

//Buoyancy
Debug.DrawRay(triangleData.center, buoyancyForce.normalized * -3f, Color.blue);
}
}

//The buoyancy force so the boat can float
private Vector3 BuoyancyForce(float rho, TriangleData triangleData)
{
//Buoyancy is a hydrostatic force - it's there even if the water isn't flowing or if the boat stays still

// F_buoyancy = rho * g * V
// rho - density of the mediaum you are in
// g - gravity
// V - volume of fluid directly above the curved surface

// V = z * S * n
// z - distance to surface
// S - surface area
// n - normal to the surface
Vector3 buoyancyForce = rho * Physics.gravity.y * triangleData.distanceToSurface * triangleData.area * triangleData.normal;

//The vertical component of the hydrostatic forces don't cancel out but the horizontal do
buoyancyForce.x = 0f;
buoyancyForce.z = 0f;

return buoyancyForce;
}
}
}
```

### ModifyBoatMesh

This script will cut the cube's triangles into the smaller pieces.

```using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace BoatTutorial
{
//Generates the mesh that's below the water
public class ModifyBoatMesh
{
//The boat transform needed to get the global position of a vertice
private Transform boatTrans;
//Coordinates of all vertices in the original boat
Vector3[] boatVertices;
//Positions in allVerticesArray, such as 0, 3, 5, to build triangles
int[] boatTriangles;

//So we only need to make the transformation from local to global once
public Vector3[] boatVerticesGlobal;
//Find all the distances to water once because some triangles share vertices, so reuse
float[] allDistancesToWater;

//The triangles belonging to the part of the boat that's under water
public List<TriangleData> underWaterTriangleData = new List<TriangleData>();

public ModifyBoatMesh(GameObject boatObj)
{
//Get the transform
boatTrans = boatObj.transform;

//Init the arrays and lists
boatVertices = boatObj.GetComponent<MeshFilter>().mesh.vertices;
boatTriangles = boatObj.GetComponent<MeshFilter>().mesh.triangles;

//The boat vertices in global position
boatVerticesGlobal = new Vector3[boatVertices.Length];
//Find all the distances to water once because some triangles share vertices, so reuse
allDistancesToWater = new float[boatVertices.Length];
}

//Generate the underwater mesh
public void GenerateUnderwaterMesh()
{
//Reset
underWaterTriangleData.Clear();

//Find all the distances to water once because some triangles share vertices, so reuse
for (int j = 0; j < boatVertices.Length; j++)
{
//The coordinate should be in global position
Vector3 globalPos = boatTrans.TransformPoint(boatVertices[j]);

//Save the global position so we only need to calculate it once here
//And if we want to debug we can convert it back to local
boatVerticesGlobal[j] = globalPos;

allDistancesToWater[j] = WaterController.current.DistanceToWater(globalPos, Time.time);
}

//Add the triangles that are below the water
}

//Add all the triangles that's part of the underwater mesh
{
//List that will store the data we need to sort the vertices based on distance to water
List<VertexData> vertexData = new List<VertexData>();

//Add init data that will be replaced

//Loop through all the triangles (3 vertices at a time = 1 triangle)
int i = 0;
while (i < boatTriangles.Length)
{
//Loop through the 3 vertices
for (int x = 0; x < 3; x++)
{
//Save the data we need
vertexData[x].distance = allDistancesToWater[boatTriangles[i]];

vertexData[x].index = x;

vertexData[x].globalVertexPos = boatVerticesGlobal[boatTriangles[i]];

i++;
}

//All vertices are above the water
if (vertexData[0].distance > 0f && vertexData[1].distance > 0f && vertexData[2].distance > 0f)
{
continue;
}

//Create the triangles that are below the waterline

//All vertices are underwater
if (vertexData[0].distance < 0f && vertexData[1].distance < 0f && vertexData[2].distance < 0f)
{
Vector3 p1 = vertexData[0].globalVertexPos;
Vector3 p2 = vertexData[1].globalVertexPos;
Vector3 p3 = vertexData[2].globalVertexPos;

//Save the triangle
}
//1 or 2 vertices are below the water
else
{
//Sort the vertices
vertexData.Sort((x, y) => x.distance.CompareTo(y.distance));

vertexData.Reverse();

//One vertice is above the water, the rest is below
if (vertexData[0].distance > 0f && vertexData[1].distance < 0f && vertexData[2].distance < 0f)
{
}
//Two vertices are above the water, the other is below
else if (vertexData[0].distance > 0f && vertexData[1].distance > 0f && vertexData[2].distance < 0f)
{
}
}
}
}

//Build the new triangles where one of the old vertices is above the water
{
//H is always at position 0
Vector3 H = vertexData[0].globalVertexPos;

//Left of H is M
//Right of H is L

//Find the index of M
int M_index = vertexData[0].index - 1;
if (M_index < 0)
{
M_index = 2;
}

//We also need the heights to water
float h_H = vertexData[0].distance;
float h_M = 0f;
float h_L = 0f;

Vector3 M = Vector3.zero;
Vector3 L = Vector3.zero;

//This means M is at position 1 in the List
if (vertexData[1].index == M_index)
{
M = vertexData[1].globalVertexPos;
L = vertexData[2].globalVertexPos;

h_M = vertexData[1].distance;
h_L = vertexData[2].distance;
}
else
{
M = vertexData[2].globalVertexPos;
L = vertexData[1].globalVertexPos;

h_M = vertexData[2].distance;
h_L = vertexData[1].distance;
}

//Now we can calculate where we should cut the triangle to form 2 new triangles
//because the resulting area will always form a square

//Point I_M
Vector3 MH = H - M;

float t_M = -h_M / (h_H - h_M);

Vector3 MI_M = t_M * MH;

Vector3 I_M = MI_M + M;

//Point I_L
Vector3 LH = H - L;

float t_L = -h_L / (h_H - h_L);

Vector3 LI_L = t_L * LH;

Vector3 I_L = LI_L + L;

//Save the data, such as normal, area, etc
//2 triangles below the water
}

//Build the new triangles where two of the old vertices are above the water
{
//H and M are above the water
//H is after the vertice that's below water, which is L
//So we know which one is L because it is last in the sorted list
Vector3 L = vertexData[2].globalVertexPos;

//Find the index of H
int H_index = vertexData[2].index + 1;
if (H_index > 2)
{
H_index = 0;
}

//We also need the heights to water
float h_L = vertexData[2].distance;
float h_H = 0f;
float h_M = 0f;

Vector3 H = Vector3.zero;
Vector3 M = Vector3.zero;

//This means that H is at position 1 in the list
if (vertexData[1].index == H_index)
{
H = vertexData[1].globalVertexPos;
M = vertexData[0].globalVertexPos;

h_H = vertexData[1].distance;
h_M = vertexData[0].distance;
}
else
{
H = vertexData[0].globalVertexPos;
M = vertexData[1].globalVertexPos;

h_H = vertexData[0].distance;
h_M = vertexData[1].distance;
}

//Now we can find where to cut the triangle

//Point J_M
Vector3 LM = M - L;

float t_M = -h_L / (h_M - h_L);

Vector3 LJ_M = t_M * LM;

Vector3 J_M = LJ_M + L;

//Point J_H
Vector3 LH = H - L;

float t_H = -h_L / (h_H - h_L);

Vector3 LJ_H = t_H * LH;

Vector3 J_H = LJ_H + L;

//Save the data, such as normal, area, etc
//1 triangle below the water
}

//Help class to store triangle data so we can sort the distances
private class VertexData
{
//The distance to water from this vertex
public float distance;
//An index so we can form clockwise triangles
public int index;
//The global Vector3 position of the vertex
public Vector3 globalVertexPos;
}

//Display the underwater mesh
public void DisplayMesh(Mesh mesh, string name, List<TriangleData> triangesData)
{
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();

//Build the mesh
for (int i = 0; i < triangesData.Count; i++)
{
//From global coordinates to local coordinates
Vector3 p1 = boatTrans.InverseTransformPoint(triangesData[i].p1);
Vector3 p2 = boatTrans.InverseTransformPoint(triangesData[i].p2);
Vector3 p3 = boatTrans.InverseTransformPoint(triangesData[i].p3);

}

//Remove the old mesh
mesh.Clear();

//Give it a name
mesh.name = name;

//Add the new vertices and triangles
mesh.vertices = vertices.ToArray();

mesh.triangles = triangles.ToArray();

mesh.RecalculateBounds();
}
}
}
```

### TriangleData

To be able to calculate the buoyancy force we have to calculate a few things for each triangle that's below the water, such as area of the triangle, and this script will store all those things. What you can see in the image below is the center of the triangle (where the while lines start) and the normal to the triangle (the white line).

```using UnityEngine;
using System.Collections;

namespace BoatTutorial
{
//To save space so we don't have to send millions of parameters to each method
public struct TriangleData
{
//The corners of this triangle in global coordinates
public Vector3 p1;
public Vector3 p2;
public Vector3 p3;

//The center of the triangle
public Vector3 center;

//The distance to the surface from the center of the triangle
public float distanceToSurface;

//The normal to the triangle
public Vector3 normal;

//The area of the triangle
public float area;

public TriangleData(Vector3 p1, Vector3 p2, Vector3 p3)
{
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;

//Center of the triangle
this.center = (p1 + p2 + p3) / 3f;

//Distance to the surface from the center of the triangle
this.distanceToSurface = Mathf.Abs(WaterController.current.DistanceToWater(this.center, Time.time));

//Normal to the triangle
this.normal = Vector3.Cross(p2 - p1, p3 - p1).normalized;

//Area of the triangle
float a = Vector3.Distance(p1, p2);

float c = Vector3.Distance(p3, p1);

this.area = (a * c * Mathf.Sin(Vector3.Angle(p2 - p1, p3 - p1) * Mathf.Deg2Rad)) / 2f;
}
}
}
```

### WaterController

Will help us find the distance to water from a coordinate. Add this script to an empty gameobject in your scene. We will need the strange water parameters in the next part of the tutorial where we will create a moving sea, so keep them.

```using UnityEngine;
using System.Collections;

//Controlls the water
public class WaterController : MonoBehaviour
{
public static WaterController current;

public bool isMoving;

//Wave height and speed
public float scale = 0.1f;
public float speed = 1.0f;
//The width between the waves
public float waveDistance = 1f;
//Noise parameters
public float noiseStrength = 1f;
public float noiseWalk = 1f;

void Start()
{
current = this;
}

//Get the y coordinate from whatever wavetype we are using
public float GetWaveYPos(Vector3 position, float timeSinceStart)
{
//if (isMoving)
//{
//return WaveTypes.SinXWave(position, speed, scale, waveDistance, noiseStrength, noiseWalk, timeSinceStart);
//}
//else
//{
//return 0f;
//}

return 0f;
}

//Find the distance from a vertice to water
//Make sure the position is in global coordinates
//Positive if above water
//Negative if below water
public float DistanceToWater(Vector3 position, float timeSinceStart)
{
float waterHeight = GetWaveYPos(position, timeSinceStart);

float distanceToWater = position.y - waterHeight;

return distanceToWater;
}
}
```

### Will it float?

Now we can finally answer the question: "Will the cube float?" My cube, which weighs 800 kg, is floating. But is it also realistic? To find out we can ask our old friend Archimedes:

Any object, wholly or partially immersed in a fluid, is buoyed up by a force equal to the weight of the fluid displaced by the object.

What Archimedes is saying is that an object floats on water if it can displace a volume of water whose weight is greater than that of the object. So if our cube weighs more than 1000 kg, which is larger than the weight of the volume of displaced water if the density of water is 1000kg/m^3 and the cube's side is 1 m, then it should sink. If you change you cube's mass above/below 1000 kg then you should notice that it will float up/down, so this model is actually good.

This model will work for any mesh you have, so it doesn't have to be a cube. Test to change your object to a cylinder, a sphere, or a more complicated boat. But don't forget to change the mass!