Search Unity

  1. Are you interested in providing feedback directly to Unity teams? Sign up to become a member of Unity Pulse, our new product feedback and research community.
    Dismiss Notice

Buoyancy script

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

  1. idurvesh

    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?
     
  2. idurvesh

    idurvesh

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

    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

    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

    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

    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

    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

    dienat

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

    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

    dienat

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

    alexzzzz

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

    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

    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

    dienat

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

    Wolfshadow

    Joined:
    Jul 7, 2015
    Posts:
    2
    Brilliant!
     
  16. Wolfshadow

    Wolfshadow

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

    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

    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. //
    6. // https://forum.unity3d.com/threads/buoyancy-script.72974/
    7. //
    8. // Terms of use: do whatever you like
    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.                             {
    116.                                 points.Add(p);
    117.                             }
    118.                         }
    119.                     }
    120.                 }
    121.                 if (points.Count == 0)
    122.                 {
    123.                     points.Add(bounds.center);
    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.  
    144.                             points.Add(p);
    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);
    226.                 list.Add(mixed);
    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;
    269.                     rigidBody.AddForceAtPosition(force, wp);
    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

    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

    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

    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

    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
     
  23. Deleted User

    Deleted User

    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

    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

    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

    warman88

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

    McGravity

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