# Buoyancy script

Discussion in 'Made With Unity' started by alexzzzz, Jan 6, 2011.

1. ### idurvesh

Joined:
Jun 9, 2014
Posts:
495
How to make it work on non flat planes? For example on river where downward slopes and y position will not be same?

Joined:
Jun 9, 2014
Posts:
495
3. ### alexzzzz

Joined:
Nov 20, 2010
Posts:
1,444
Change the method GetWaterLevel(float x, float z) to return the real water level if on river and, say, -1000 if on land.

4. ### idurvesh

Joined:
Jun 9, 2014
Posts:
495
Yes I tried it, but my river is modeler i.e., different modules joint to make single river so how can I get different position of water dynamically? when I up-down my existing water, its now working but after changing water module dynamically its not working....

Here is code to how I accomplished dynamic height of water
Code (CSharp):
1.
2.         RaycastHit hit;
3.         if (Physics.Raycast(transform.position, Vector3.down * 500, out hit,waterMask))
4.         {
5.
6.
7.
8.                 Vector3 temPos= Vector3.zero;
9.                 temPos.x = x;
10.                 temPos.z = z;
11.
12.                 float h = hit.transform.worldToLocalMatrix.MultiplyPoint(temPos).y;
13.
14.
15.
16.                 return h + hit.point.y ;
17.
18.
19.
20.
21.
22.
23.         }
24.
25.

5. ### alexzzzz

Joined:
Nov 20, 2010
Posts:
1,444
Code (CSharp):
1.     float GetWaterLevel(float x, float z)
2.     {
3.         RaycastHit hit;
4.         if (Physics.Raycast(new Vector3(x, 250, z), Vector3.down * 500, out hit, waterMask))
5.         {
6.             return hit.point.y;
7.         }
8.         return -1000;
9.     }
10.
However, the performance will suffer, I guess, from the excessive amount of raycasts.

6. ### idurvesh

Joined:
Jun 9, 2014
Posts:
495
Thanks for code,I tried it,however it doesn't seem to work properly, Player falls through plane and raycast is spawning at different place than expected.Here is screenshot of it.

http://imgur.com/oRJKRK2

7. ### chushuxiang

Joined:
Jan 27, 2015
Posts:
1
HI alexzzzz and guys
,thanks for share.It is very great work.
I confused in the script why you used"
1. var originalRotation = transform.rotation;
2. var originalPosition = transform.position;
3. transform.rotation = Quaternion.identity;
4. transform.position = Vector3.zero;
"and then restore the transform.Why did u that in script and what is mean of it ? I am a student from Asia. Look forward to your reply. Very very thanks! Forgive my English ....

8. ### dienat

Joined:
May 27, 2016
Posts:
417
Is this script public domain, free to use for commercial games?

9. ### alexzzzz

Joined:
Nov 20, 2010
Posts:
1,444
Sorry, I didn't think about the license. Let it officially be WTFPL version 2.

dienat likes this.
10. ### dienat

Joined:
May 27, 2016
Posts:
417
Thanks and good job with this script

11. ### alexzzzz

Joined:
Nov 20, 2010
Posts:
1,444
I hope it still works.

12. ### dienat

Joined:
May 27, 2016
Posts:
417
I notice that when i have the cam attached to the ship with buoyancy the cam jitters with big waves ( i am using Ceto ), do you know why this happens?

13. ### alexzzzz

Joined:
Nov 20, 2010
Posts:
1,444
Most likely it's because the physics frequency and framerate are different. Two possible solution:
1. Enable interpolation/extrapolation for the ship's rigidbody.
2. Detach the camera from the ship and control it with some script that will make the camera smoothly follow a target.

14. ### dienat

Joined:
May 27, 2016
Posts:
417
I found the solution, it was a Ceto problem, thanks anyway.

Joined:
Jul 7, 2015
Posts:
2
Brilliant!

Joined:
Jul 7, 2015
Posts:
2
How does this scale of 1000 as middle relate to grams per cubic centimeters?

17. ### jmarcos007

Joined:
Mar 2, 2017
Posts:
11
Hi! First, excuse my english. I´m new on Unity and working on a script for buoyancy. I'd like a function or script to return the vertical distance (depth) from an object to water surface. I´m using 'Water4Advanced' (from Unity) that have waves with dynamic mesh. I spent lots of hours looking for, but a can´t implement any found idea. Thats because I´m new on C# and Unity. Somebody can help me? Thanks.

18. ### rockbyte

Joined:
May 1, 2017
Posts:
6
Below are the changes I performed to the v2.1 script to work well with my Unity 5.5. What I changed:
- Added requirement for RigidBody/Collider (does not need to perform this check in the code now)
- Use RigidBody and Collider Components, instead of the legacy properties

Code (csharp):
1.
2. // Buoyancy.cs
3. // by Alex Zhdankin
4. // Version 2.1.1?
5. //
7. //
9.
10.     using System.Collections.Generic;
11.     using UnityEngine;
12.
13.     [RequireComponent(typeof(Rigidbody))]
14.     [RequireComponent(typeof(Collider))]
15.     public class Buoyancy : MonoBehaviour
16.     {
17.         public float density = 500;
18.         public int slicesPerAxis = 2;
19.         public bool isConcave = false;
20.         public int voxelsLimit = 16;
21.
22.         private const float DAMPFER = 0.1f;
23.         private const float WATER_DENSITY = 1000;
24.
25.         private float voxelHalfHeight;
26.         private Vector3 localArchimedesForce;
27.         private List<Vector3> voxels;
28.         private bool isMeshCollider;
29.         private List<Vector3[]> forces; // For drawing force gizmos
30.
31.         private Rigidbody rigidBody;
32.         private Collider collider;
33.
34.         /// <summary>
35.         /// Provides initialization.
36.         /// </summary>
37.         private void Start()
38.         {
39.             forces = new List<Vector3[]>(); // For drawing force gizmos
40.             rigidBody = GetComponent<Rigidbody>();
41.             collider = GetComponent<Collider>();
42.
43.             // Store original rotation and position
44.             var originalRotation = transform.rotation;
45.             var originalPosition = transform.position;
46.             transform.rotation = Quaternion.identity;
47.             transform.position = Vector3.zero;
48.
49.             isMeshCollider = GetComponent<MeshCollider>() != null;
50.
51.             var bounds = collider.bounds;
52.             if (bounds.size.x < bounds.size.y)
53.             {
54.                 voxelHalfHeight = bounds.size.x;
55.             }
56.             else
57.             {
58.                 voxelHalfHeight = bounds.size.y;
59.             }
60.             if (bounds.size.z < voxelHalfHeight)
61.             {
62.                 voxelHalfHeight = bounds.size.z;
63.             }
64.             voxelHalfHeight /= 2 * slicesPerAxis;
65.
66.             rigidBody.centerOfMass = new Vector3(0, -bounds.extents.y * 0f, 0) + transform.InverseTransformPoint(bounds.center);
67.
68.             voxels = SliceIntoVoxels(isMeshCollider && isConcave);
69.
70.             // Restore original rotation and position
71.             transform.rotation = originalRotation;
72.             transform.position = originalPosition;
73.
74.             float volume = rigidBody.mass / density;
75.
76.             WeldPoints(voxels, voxelsLimit);
77.
78.             float archimedesForceMagnitude = WATER_DENSITY * Mathf.Abs(Physics.gravity.y) * volume;
79.             localArchimedesForce = new Vector3(0, archimedesForceMagnitude, 0) / voxels.Count;
80.
81.             Debug.Log(string.Format("[Buoyancy.cs] Name=\"{0}\" volume={1:0.0}, mass={2:0.0}, density={3:0.0}", name, volume, rigidBody.mass, density));
82.         }
83.
84.         /// <summary>
85.         /// Slices the object into number of voxels represented by their center points.
86.         /// <param name="concave">Whether the object have a concave shape.</param>
87.         /// <returns>List of voxels represented by their center points.</returns>
88.         /// </summary>
89.         private List<Vector3> SliceIntoVoxels(bool concave)
90.         {
91.             var points = new List<Vector3>(slicesPerAxis * slicesPerAxis * slicesPerAxis);
92.
93.             if (concave)
94.             {
95.                 var meshCol = GetComponent<MeshCollider>();
96.
97.                 var convexValue = meshCol.convex;
98.                 meshCol.convex = false;
99.
100.                 // Concave slicing
101.                 var bounds = collider.bounds;
102.                 for (int ix = 0; ix < slicesPerAxis; ix++)
103.                 {
104.                     for (int iy = 0; iy < slicesPerAxis; iy++)
105.                     {
106.                         for (int iz = 0; iz < slicesPerAxis; iz++)
107.                         {
108.                             float x = bounds.min.x + bounds.size.x / slicesPerAxis * (0.5f + ix);
109.                             float y = bounds.min.y + bounds.size.y / slicesPerAxis * (0.5f + iy);
110.                             float z = bounds.min.z + bounds.size.z / slicesPerAxis * (0.5f + iz);
111.
112.                             var p = transform.InverseTransformPoint(new Vector3(x, y, z));
113.
114.                             if (PointIsInsideMeshCollider(meshCol, p))
115.                             {
117.                             }
118.                         }
119.                     }
120.                 }
121.                 if (points.Count == 0)
122.                 {
124.                 }
125.
126.                 meshCol.convex = convexValue;
127.             }
128.             else
129.             {
130.                 // Convex slicing
131.                 var bounds = GetComponent<Collider>().bounds;
132.                 for (int ix = 0; ix < slicesPerAxis; ix++)
133.                 {
134.                     for (int iy = 0; iy < slicesPerAxis; iy++)
135.                     {
136.                         for (int iz = 0; iz < slicesPerAxis; iz++)
137.                         {
138.                             float x = bounds.min.x + bounds.size.x / slicesPerAxis * (0.5f + ix);
139.                             float y = bounds.min.y + bounds.size.y / slicesPerAxis * (0.5f + iy);
140.                             float z = bounds.min.z + bounds.size.z / slicesPerAxis * (0.5f + iz);
141.
142.                             var p = transform.InverseTransformPoint(new Vector3(x, y, z));
143.
145.                         }
146.                     }
147.                 }
148.             }
149.
150.             return points;
151.         }
152.
153.         /// <summary>
154.         /// Returns whether the point is inside the mesh collider.
155.         /// </summary>
156.         /// <param name="c">Mesh collider.</param>
157.         /// <param name="p">Point.</param>
158.         /// <returns>True - the point is inside the mesh collider. False - the point is outside of the mesh collider. </returns>
159.         private static bool PointIsInsideMeshCollider(Collider c, Vector3 p)
160.         {
161.             Vector3[] directions = { Vector3.up, Vector3.down, Vector3.left, Vector3.right, Vector3.forward, Vector3.back };
162.
163.             foreach (var ray in directions)
164.             {
165.                 RaycastHit hit;
166.                 if (c.Raycast(new Ray(p - ray * 1000, ray), out hit, 1000f) == false)
167.                 {
168.                     return false;
169.                 }
170.             }
171.
172.             return true;
173.         }
174.
175.         /// <summary>
176.         /// Returns two closest points in the list.
177.         /// </summary>
178.         /// <param name="list">List of points.</param>
179.         /// <param name="firstIndex">Index of the first point in the list. It's always less than the second index.</param>
180.         /// <param name="secondIndex">Index of the second point in the list. It's always greater than the first index.</param>
181.         private static void FindClosestPoints(IList<Vector3> list, out int firstIndex, out int secondIndex)
182.         {
183.             float minDistance = float.MaxValue, maxDistance = float.MinValue;
184.             firstIndex = 0;
185.             secondIndex = 1;
186.
187.             for (int i = 0; i < list.Count - 1; i++)
188.             {
189.                 for (int j = i + 1; j < list.Count; j++)
190.                 {
191.                     float distance = Vector3.Distance(list[i], list[j]);
192.                     if (distance < minDistance)
193.                     {
194.                         minDistance = distance;
195.                         firstIndex = i;
196.                         secondIndex = j;
197.                     }
198.                     if (distance > maxDistance)
199.                     {
200.                         maxDistance = distance;
201.                     }
202.                 }
203.             }
204.         }
205.
206.         /// <summary>
207.         /// Welds closest points.
208.         /// </summary>
209.         /// <param name="list">List of points.</param>
210.         /// <param name="targetCount">Target number of points in the list.</param>
211.         private static void WeldPoints(IList<Vector3> list, int targetCount)
212.         {
213.             if (list.Count <= 2 || targetCount < 2)
214.             {
215.                 return;
216.             }
217.
218.             while (list.Count > targetCount)
219.             {
220.                 int first, second;
221.                 FindClosestPoints(list, out first, out second);
222.
223.                 var mixed = (list[first] + list[second]) * 0.5f;
224.                 list.RemoveAt(second); // the second index is always greater that the first => removing the second item first
225.                 list.RemoveAt(first);
227.             }
228.         }
229.
230.         /// <summary>
231.         /// Returns the water level at given location.
232.         /// </summary>
233.         /// <param name="x">x-coordinate</param>
234.         /// <param name="z">z-coordinate</param>
235.         /// <returns>Water level</returns>
236.         private float GetWaterLevel(float x, float z)
237.         {
238. //    return ocean == null ? 0.0f : ocean.GetWaterHeightAtLocation(x, z);
239.             return 5.0f;
240.         }
241.
242.         /// <summary>
243.         /// Calculates physics.
244.         /// </summary>
245.         private void FixedUpdate()
246.         {
247.             forces.Clear(); // For drawing force gizmos
248.
249.             foreach (var point in voxels)
250.             {
251.                 var wp = transform.TransformPoint(point);
252.                 float waterLevel = GetWaterLevel(wp.x, wp.z);
253.
254.                 if (wp.y - voxelHalfHeight < waterLevel)
255.                 {
256.                     float k = (waterLevel - wp.y) / (2 * voxelHalfHeight) + 0.5f;
257.                     if (k > 1)
258.                     {
259.                         k = 1f;
260.                     }
261.                     else if (k < 0)
262.                     {
263.                         k = 0f;
264.                     }
265.
266.                     var velocity = rigidBody.GetPointVelocity(wp);
267.                     var localDampingForce = -velocity * DAMPFER * rigidBody.mass;
268.                     var force = localDampingForce + Mathf.Sqrt(k) * localArchimedesForce;
270.
271.                     forces.Add(new[] { wp, force }); // For drawing force gizmos
272.                 }
273.             }
274.         }
275.
276.         /// <summary>
277.         /// Draws gizmos.
278.         /// </summary>
279.         private void OnDrawGizmos()
280.         {
281.             if (voxels == null || forces == null)
282.             {
283.                 return;
284.             }
285.
286.             const float gizmoSize = 0.05f;
287.             Gizmos.color = Color.yellow;
288.
289.             foreach (var p in voxels)
290.             {
291.                 Gizmos.DrawCube(transform.TransformPoint(p), new Vector3(gizmoSize, gizmoSize, gizmoSize));
292.             }
293.
294.             Gizmos.color = Color.cyan;
295.
296.             foreach (var force in forces)
297.             {
298.                 Gizmos.DrawCube(force[0], new Vector3(gizmoSize, gizmoSize, gizmoSize));
299.                 Gizmos.DrawLine(force[0], force[0] + force[1] / rigidBody.mass);
300.             }
301.         }
302.     }
303.

Last edited: May 28, 2017
Vytek and cartridgegamestudio like this.
19. ### kotaleks2990

Joined:
May 28, 2017
Posts:
1

I have a mistake: Assets/_Script/Buoyancy.cs(190,30): error CS1502: The best overloaded method match for `UnityEngine.Vector3.Distance(UnityEngine.Vector3, UnityEngine.Vector3)' has some invalid arguments

20. ### rockbyte

Joined:
May 1, 2017
Posts:
6
You're right, somehow when I placed the script here I must have taken out the array selection by accident. the problem is in line 191:
Code (csharp):
1. float distance = Vector3.Distance(list, list[j]);
which should be:

Code (csharp):
1. float distance = Vector3.Distance(list[i], list[j]);
I have corrected the original post now.

21. ### JC_LEON

Joined:
May 20, 2014
Posts:
487
thanks for the script..but how cna i do to make it to work if i have water on different heights??
and make items bouyancy only when collide with water??

22. ### JemoYT

Joined:
Feb 13, 2017
Posts:
7
hey, im having a little problem, you commented out the ocean and others, but can you tell me how to set it up? because as far as i know, ocean is in unity

### Guest

Thank you for making this one free to use. It's a really cool piece of works man, and it produces really realistic-looking results. I'm glad I tried it, it's gonna end up be the first thing I use that's %100 borrowed other than Unity itself. (which is ALOT^2) I know this thread's mega old but it's still high on the search results.

So like, good job, thanks.

For anyone following, at least the first C# file in this thread, you have a static 0-level for the water in there. There's an "ocean" commented out. It's suggesting you could use arrays to get a height-per-(x,y position) I think. If it's just "steady froth/churn/waves" but on a plane, a fixed height in the middle should look OK. If it's a river going down a hill, there's a place where you need to make an "= 0" equal something other than 0 for the height based on a position. Personally, I would just use another raycast to test the height.

valentingurkov likes this.
24. ### McGravity

Joined:
Nov 2, 2013
Posts:
49
Hi,

thanks for the awesome asset. I have a problem though.

There is an offset between the collider and the voxels which are used to compute the up-force. When I place the GameObject at (0,0,0) it works fine, but when I move it in any direction from the origin the offset gets bigger. Am I doing something wrong?

25. ### McGravity

Joined:
Nov 2, 2013
Posts:
49
Figured it out, the bounds are not up to date after the transform is moved to the origin in the Start method. (Line 46) They can be updated via calling
``Physics.SyncTransforms();``
right after moving it.

deekpyro likes this.
26. ### warman88

Joined:
Dec 27, 2020
Posts:
6
It says the first line public Ocean ocean; is a error

27. ### McGravity

Joined:
Nov 2, 2013
Posts:
49
What's the error message?

unityunity