Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question My rigidbody starts to jitter and go crazy the more I build my raft

Discussion in 'Physics' started by Splatacat, Aug 9, 2020.

  1. Splatacat

    Splatacat

    Joined:
    Jan 10, 2013
    Posts:
    13
    Hi there,

    I an building a ocean survival game where you build up your raft plank by plank and as I add more pieces it begins to jitter and wobble slightly, this gets worse if I add more until it eventually flies off and goes out of unity's range.

    Unity Help 1.jpg

    Unity Help 2.jpg

    the raft is the main gameobject and has the rigidbody, the planks and all future items goes on as child objects in the position the player has chosen, with box colliders and the raft then recalculates the points used for the buoyancy calculations.

    I can not for the life of me figure out why it starts to go wonky. no child objects have rigidbodies attached so it should be treating the whole raft as one object with a compound collider.
     
  2. VestedGamr

    VestedGamr

    Joined:
    Dec 19, 2013
    Posts:
    12
    Do the other objects have Rigidbodies before adding them to the raft? Check to make sure those rigidbodies are being deactivated properly at runtime.
     
  3. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    466
    Check if the rotational inertia tensor is recalculated when you add colliders. As your compound object grows, so should the inertia tensor. If it stays the same, buoyancy forces added far away from the center of mass lead to large angular accelerations. I assume your also adding mass to the rigidbody as your raft grows?

    next issue could be the fluid dynamics drag. I assume you’re adding some dampening force. Realistically, they should be proportional to v^2, but this can introduce numeric instability. I also had that and switched to linear drag, which is not physically correct, but looks believable too and doesn’t make your physics explode. You can also use v^2 and clamp it to some reasonable value. I haven’t tested that but I strongly assume it might work too.
     
  4. Splatacat

    Splatacat

    Joined:
    Jan 10, 2013
    Posts:
    13
    Thanks for the replies

    None of the parts have rigidbodies, I have different prefabs for each part, and the built prefabs don't have any rigidbodies attached.

    Thanks I never knew about inertia tensor, I have now added ResetInertiaTensor(); each time.
    This seems to help a little but it seems to still do it now and then, I think its doing it when the floating script increases the drag and angular drag. This works to give the individual pieces a more realistic buoyancy. I'm not sure why this would make it freak out but i'll not increase those on the raft rigidbody and see.
    I will add my buoyancy script here, in case there is something funky i'm doing in it. If you have the time to read through it and see what you think it would be appreciated.

    From my building script:

    Code (CSharp):
    1. private void BuildIt()//Place the piece
    2.     {
    3.         GameObject builtObject = Instantiate(hitItemBuilding.builtPiecePrefab, previewGameObjectChild.position, previewGameObjectChild.rotation, raft.transform);
    4.         //previewScript.Place(hitGameObject);
    5.         Destroy(previewGameObject);
    6.         previewGameObject = null;
    7.         //previewScript = null;
    8.         previewGameObjectChild = null;
    9.         //isBuilding = false;
    10.         canSnap = false;
    11.         andRotating = false;
    12.         selected = false;
    13.         selectable = false;
    14.         previewing = false;
    15.         shifting = false;
    16.  
    17.         if (carryingAmount == 1)
    18.         {
    19.             print("remove " + carryingAmount);
    20.             Destroy(carryingGO[0]);
    21.             carryingGO.RemoveAt(0);
    22.             carryingAmount--;
    23.             amCarrying = false;
    24.             carryingItem = null;
    25.             carryingGO.Clear();
    26.             amCarryingName = null;
    27.             canBuild = false;
    28.         }
    29.  
    30.         if (carryingAmount > 1)
    31.         {
    32.             print("remove " + carryingAmount);
    33.             //int tmporary = carryingAmount - 1;
    34.             Destroy(carryingGO[carryingAmount - 1]);
    35.             carryingGO.RemoveAt(carryingAmount - 1);
    36.             carryingAmount--;
    37.         }
    38.  
    39.         //isRotZ = false;
    40.         //isRotY = false;
    41.         //translateOffset = 0f;
    42.         //AddMeshCollider(raft);
    43.         raft.GetComponent<Rigidbody>().ResetInertiaTensor();
    44.         raft.GetComponent<FloatStuff>().Recalculate();
    45.         //raft.GetComponent<FloatStuff>().enabled = true;
    46.     }
    My buoyancy script:

    Code (CSharp):
    1. // Buoyancy.cs
    2. // by Alex Zhdankin, modified for my purpouse.
    3. // Version 2.1.1?
    4. //
    5. // https://forum.unity3d.com/threads/buoyancy-script.72974/
    6. //
    7. // Terms of use: do whatever you like
    8.  
    9. using System.Collections.Generic;
    10. using UnityEngine;
    11. using System.Linq;
    12.  
    13. [RequireComponent(typeof(Rigidbody))]
    14. [RequireComponent(typeof(Collider))]
    15. public class FloatStuff : 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 Bounds bounds;
    30.     private List<Vector3[]> forces; // For drawing force gizmos
    31.  
    32.     private Rigidbody rigidBody;
    33.     private Collider thecollider;
    34.     public List<Collider> allColliders = new List<Collider>();
    35.     private List<Bounds> allBounds = new List<Bounds>();
    36.     private List<float> allVoxelHalfHeight = new List<float>();
    37.     [SerializeField]
    38.     public List<List<Vector3>> allVoxels = new List<List<Vector3>>();
    39.  
    40.     private Ocean ocean;
    41.     public float waterLevel;
    42.     public bool children;
    43.     private float volume;
    44.     private float allMass;
    45.     public int allVoxelsCount = 0;
    46.     public List<Vector3> allCofM;
    47.     private int subVoxelsCount;
    48.  
    49.     //private int weights;
    50.  
    51.     /// <summary>
    52.     /// Provides initialization.
    53.     /// </summary>
    54.     private void Start()
    55.     {
    56.         forces = new List<Vector3[]>(); // For drawing force gizmos
    57.         rigidBody = GetComponent<Rigidbody>();
    58.         thecollider = GetComponent<Collider>();
    59.         allColliders.Clear();
    60.  
    61.         foreach (Transform child in transform)
    62.         {
    63.             if (child.CompareTag("BuildingMaterial"))
    64.             {
    65.                 allColliders.Add(child.GetComponent<Collider>());
    66.                 allMass += child.GetComponent<MyWeight>().myWeight;
    67.             }
    68.         }
    69.  
    70.         // Store original rotation and position
    71.         var originalRotation = transform.rotation;
    72.         var originalPosition = transform.position;
    73.         transform.rotation = Quaternion.identity;
    74.         transform.position = Vector3.zero;
    75.  
    76.         ocean = Ocean.Singleton;
    77.  
    78.         if (!children)
    79.         {
    80.             isMeshCollider = GetComponent<MeshCollider>() != null;
    81.  
    82.             bounds = thecollider.bounds;
    83.             if (bounds.size.x < bounds.size.y)
    84.             {
    85.                 voxelHalfHeight = bounds.size.x;
    86.             }
    87.             else
    88.             {
    89.                 voxelHalfHeight = bounds.size.y;
    90.             }
    91.             if (bounds.size.z < voxelHalfHeight)
    92.             {
    93.                 voxelHalfHeight = bounds.size.z;
    94.             }
    95.             voxelHalfHeight /= 2 * slicesPerAxis;
    96.             rigidBody.centerOfMass = new Vector3(0, -bounds.extents.y * 0f, 0) + transform.InverseTransformPoint(bounds.center);
    97.  
    98.             voxels = SliceIntoVoxels(isMeshCollider && isConcave);
    99.             transform.rotation = originalRotation;
    100.             transform.position = originalPosition;
    101.  
    102.             WeldPoints(voxels, voxelsLimit);
    103.             volume = rigidBody.mass / density;
    104.             float archimedesForceMagnitude = WATER_DENSITY * Mathf.Abs(Physics.gravity.y) * volume;
    105.             localArchimedesForce = new Vector3(0, archimedesForceMagnitude, 0) / voxels.Count;
    106.  
    107.             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));
    108.         }
    109.         //allVoxelsCount = 0;
    110.         else if (children)
    111.         {          
    112.             for (int i = 0; i < allColliders.Count; i++)
    113.             {
    114.                 allBounds.Insert(i, allColliders[i].bounds);
    115.                 if (allBounds[i].size.x < allBounds[i].size.y)
    116.                 {
    117.                     allVoxelHalfHeight.Insert(i, allBounds[i].size.x);
    118.                 }
    119.                 else
    120.                 {
    121.                     allVoxelHalfHeight.Insert(i, allBounds[i].size.y);
    122.                 }
    123.                 if (allBounds[i].size.z < allVoxelHalfHeight[i])
    124.                 {
    125.                     allVoxelHalfHeight.RemoveAt(i);
    126.                     allVoxelHalfHeight.Insert(i, allBounds[i].size.z);
    127.                 }
    128.  
    129.                 float tmpHalfHeight = allVoxelHalfHeight[i];
    130.                 allVoxelHalfHeight.RemoveAt(i);
    131.                 allVoxelHalfHeight.Insert(i, tmpHalfHeight /= 2 * slicesPerAxis);
    132.                 //print(i);
    133.                 Vector3 tmp = new Vector3(allColliders[i].transform.localPosition.x, allColliders[i].transform.localPosition.y, allColliders[i].transform.localPosition.z);
    134.                 print(tmp);
    135.                 allCofM.Add(tmp);
    136.  
    137.                 //allMass = weights;
    138.                 //rigidBody.mass = 80;
    139.  
    140.                 allVoxels.Insert(i, SliceIntoVoxels(isMeshCollider && isConcave, allColliders[i], i));
    141.                 subVoxelsCount = allVoxels[i].Count;
    142.                 allVoxelsCount += subVoxelsCount;
    143.             }
    144.             //allVoxelsCount += allVoxels.Count;
    145.             print(allVoxelsCount);
    146.             var average = allCofM.Aggregate(new Vector3(0, 0, 0), (s, v) => s + v) / allCofM.Count;
    147.             print(average);
    148.             rigidBody.centerOfMass = average; //new Vector3(0, -average.y * 0f, 0); /// + transform.InverseTransformPoint(bounds.center);
    149.             //transform.rotation = originalRotation;
    150.             //transform.position = originalPosition;
    151.  
    152.             for (int j = 0; j < allVoxels.Count; j++)
    153.             {
    154.                 foreach (Vector3 tmpVoxels in allVoxels[j])
    155.                 {
    156.                     voxels = SliceIntoVoxels(isMeshCollider && isConcave);
    157.                     transform.rotation = originalRotation;
    158.                     transform.position = originalPosition;
    159.  
    160.                     WeldPoints(voxels, voxelsLimit);
    161.                     WeldPoints(allVoxels[j], voxelsLimit);
    162.                 }
    163.             }
    164.             rigidBody.mass = allMass;
    165.             volume = rigidBody.mass / density;
    166.  
    167.             float archimedesForceMagnitude = WATER_DENSITY * Mathf.Abs(Physics.gravity.y) * volume;
    168.             localArchimedesForce = new Vector3(0, archimedesForceMagnitude, 0) / allVoxelsCount;
    169.  
    170.             Debug.Log(string.Format("[Buoyancy.cs] Name=\"{0}\" volume={1:0.0}, mass={2:0.0}, density={3:0.0}", name, volume, allMass, density));
    171.  
    172.         }            
    173.     }
    174.  
    175.     public void Recalculate()
    176.     {
    177.         allColliders.Clear();
    178.         allMass = 0;
    179.  
    180.         foreach (Transform child in transform)
    181.         {
    182.             if (child.CompareTag("BuildingMaterial"))
    183.             {
    184.                 allColliders.Add(child.GetComponent<Collider>());
    185.                 allMass += child.GetComponent<MyWeight>().myWeight;
    186.             }
    187.         }
    188.  
    189.         // Store original rotation and position
    190.         var originalRotation = transform.rotation;
    191.         var originalPosition = transform.position;
    192.         transform.rotation = Quaternion.identity;
    193.         transform.position = Vector3.zero;
    194.  
    195.         //ocean = Ocean.Singleton;
    196.  
    197.         if (!children)
    198.         {
    199.             isMeshCollider = GetComponent<MeshCollider>() != null;
    200.  
    201.             bounds = thecollider.bounds;
    202.             if (bounds.size.x < bounds.size.y)
    203.             {
    204.                 voxelHalfHeight = bounds.size.x;
    205.             }
    206.             else
    207.             {
    208.                 voxelHalfHeight = bounds.size.y;
    209.             }
    210.             if (bounds.size.z < voxelHalfHeight)
    211.             {
    212.                 voxelHalfHeight = bounds.size.z;
    213.             }
    214.             voxelHalfHeight /= 2 * slicesPerAxis;
    215.             rigidBody.centerOfMass = new Vector3(0, -bounds.extents.y * 0f, 0) + transform.InverseTransformPoint(bounds.center);
    216.  
    217.             voxels = SliceIntoVoxels(isMeshCollider && isConcave);
    218.             transform.rotation = originalRotation;
    219.             transform.position = originalPosition;
    220.  
    221.             WeldPoints(voxels, voxelsLimit);
    222.             volume = rigidBody.mass / density;
    223.             float archimedesForceMagnitude = WATER_DENSITY * Mathf.Abs(Physics.gravity.y) * volume;
    224.             localArchimedesForce = new Vector3(0, archimedesForceMagnitude, 0) / voxels.Count;
    225.  
    226.             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));
    227.         }
    228.         //allVoxelsCount = 0;
    229.         else if (children)
    230.         {          
    231.             for (int i = 0; i < allColliders.Count; i++)
    232.             {
    233.                 allBounds.Insert(i, allColliders[i].bounds);
    234.                 if (allBounds[i].size.x < allBounds[i].size.y)
    235.                 {
    236.                     allVoxelHalfHeight.Insert(i, allBounds[i].size.x);
    237.                 }
    238.                 else
    239.                 {
    240.                     allVoxelHalfHeight.Insert(i, allBounds[i].size.y);
    241.                 }
    242.                 if (allBounds[i].size.z < allVoxelHalfHeight[i])
    243.                 {
    244.                     allVoxelHalfHeight.RemoveAt(i);
    245.                     allVoxelHalfHeight.Insert(i, allBounds[i].size.z);
    246.                 }
    247.  
    248.                 float tmpHalfHeight = allVoxelHalfHeight[i];
    249.                 allVoxelHalfHeight.RemoveAt(i);
    250.                 allVoxelHalfHeight.Insert(i, tmpHalfHeight /= 2 * slicesPerAxis);
    251.                 //print(i);
    252.                 Vector3 tmp = new Vector3(allColliders[i].transform.localPosition.x, allColliders[i].transform.localPosition.y, allColliders[i].transform.localPosition.z);
    253.                 print(tmp);
    254.                 allCofM.Add(tmp);
    255.  
    256.                 //allMass = weights;
    257.                 //rigidBody.mass = 80;
    258.  
    259.                 allVoxels.Insert(i, SliceIntoVoxels(isMeshCollider && isConcave, allColliders[i], i));
    260.                 subVoxelsCount = allVoxels[i].Count;
    261.                 allVoxelsCount += subVoxelsCount;
    262.             }
    263.             //allVoxelsCount += allVoxels.Count;
    264.             print(allVoxelsCount);
    265.             var average = allCofM.Aggregate(new Vector3(0, 0, 0), (s, v) => s + v) / allCofM.Count;
    266.             print(average);
    267.             rigidBody.centerOfMass = average; //new Vector3(0, -average.y * 0f, 0); /// + transform.InverseTransformPoint(bounds.center);
    268.             //transform.rotation = originalRotation;
    269.             //transform.position = originalPosition;
    270.  
    271.             for (int j = 0; j < allVoxels.Count; j++)
    272.             {
    273.                 foreach (Vector3 tmpVoxels in allVoxels[j])
    274.                 {
    275.                     voxels = SliceIntoVoxels(isMeshCollider && isConcave);
    276.                     transform.rotation = originalRotation;
    277.                     transform.position = originalPosition;
    278.  
    279.                     WeldPoints(voxels, voxelsLimit);
    280.                     WeldPoints(allVoxels[j], voxelsLimit);
    281.                 }
    282.             }
    283.             rigidBody.mass = allMass;
    284.             volume = rigidBody.mass / density;
    285.  
    286.             float archimedesForceMagnitude = WATER_DENSITY * Mathf.Abs(Physics.gravity.y) * volume;
    287.             localArchimedesForce = new Vector3(0, archimedesForceMagnitude, 0) / allVoxelsCount;
    288.  
    289.             Debug.Log(string.Format("[Buoyancy.cs] Name=\"{0}\" volume={1:0.0}, mass={2:0.0}, density={3:0.0}", name, volume, allMass, density));
    290.  
    291.         }
    292.  
    293.     }
    294.     /// <summary>
    295.     /// Slices the object into number of voxels represented by their center points.
    296.     /// <param name="concave">Whether the object have a concave shape.</param>
    297.     /// <returns>List of voxels represented by their center points.</returns>
    298.     /// </summary>
    299.     private List<Vector3> SliceIntoVoxels(bool concave)
    300.     {
    301.         var points = new List<Vector3>(slicesPerAxis * slicesPerAxis * slicesPerAxis);
    302.  
    303.         if (concave)
    304.         {
    305.             var meshCol = GetComponent<MeshCollider>();
    306.  
    307.             var convexValue = meshCol.convex;
    308.             meshCol.convex = false;
    309.  
    310.             // Concave slicing
    311.             var bounds = thecollider.bounds;
    312.             for (int ix = 0; ix < slicesPerAxis; ix++)
    313.             {
    314.                 for (int iy = 0; iy < slicesPerAxis; iy++)
    315.                 {
    316.                     for (int iz = 0; iz < slicesPerAxis; iz++)
    317.                     {
    318.                         float x = bounds.min.x + bounds.size.x / slicesPerAxis * (0.5f + ix);
    319.                         float y = bounds.min.y + bounds.size.y / slicesPerAxis * (0.5f + iy);
    320.                         float z = bounds.min.z + bounds.size.z / slicesPerAxis * (0.5f + iz);
    321.  
    322.                         var p = transform.InverseTransformPoint(new Vector3(x, y, z));
    323.  
    324.                         if (PointIsInsideMeshCollider(meshCol, p))
    325.                         {
    326.                             points.Add(p);
    327.                         }
    328.                     }
    329.                 }
    330.             }
    331.             if (points.Count == 0)
    332.             {
    333.                 points.Add(bounds.center);
    334.             }
    335.  
    336.             meshCol.convex = convexValue;
    337.         }
    338.         else
    339.         {
    340.             // Convex slicing
    341.             var bounds = GetComponent<Collider>().bounds;
    342.             for (int ix = 0; ix < slicesPerAxis; ix++)
    343.             {
    344.                 for (int iy = 0; iy < slicesPerAxis; iy++)
    345.                 {
    346.                     for (int iz = 0; iz < slicesPerAxis; iz++)
    347.                     {
    348.                         float x = bounds.min.x + bounds.size.x / slicesPerAxis * (0.5f + ix);
    349.                         float y = bounds.min.y + bounds.size.y / slicesPerAxis * (0.5f + iy);
    350.                         float z = bounds.min.z + bounds.size.z / slicesPerAxis * (0.5f + iz);
    351.  
    352.                         var p = transform.InverseTransformPoint(new Vector3(x, y, z));
    353.  
    354.                         points.Add(p);
    355.                     }
    356.                 }
    357.             }
    358.         }
    359.  
    360.         return points;
    361.     }
    362.  
    363.  
    364.  
    365.  
    366.  
    367.  
    368.     /// <summary>
    369.     /// Slices the object into number of voxels represented by their center points.
    370.     /// <param name="concave">Whether the object have a concave shape.</param>
    371.     /// <returns>List of voxels represented by their center points.</returns>
    372.     /// </summary>
    373.     private List<Vector3> SliceIntoVoxels(bool concave, Collider coll, int index)
    374.     {
    375.         var points = new List<Vector3>(slicesPerAxis * slicesPerAxis * slicesPerAxis);
    376.  
    377.         if (concave)
    378.         {
    379.             var meshCol = GetComponent<MeshCollider>();
    380.  
    381.             var convexValue = meshCol.convex;
    382.             meshCol.convex = false;
    383.  
    384.             // Concave slicing
    385.             var bounds = coll.bounds;
    386.             for (int ix = 0; ix < slicesPerAxis; ix++)
    387.             {
    388.                 for (int iy = 0; iy < slicesPerAxis; iy++)
    389.                 {
    390.                     for (int iz = 0; iz < slicesPerAxis; iz++)
    391.                     {
    392.                         float x = bounds.min.x + bounds.size.x / slicesPerAxis * (0.5f + ix);
    393.                         float y = bounds.min.y + bounds.size.y / slicesPerAxis * (0.5f + iy);
    394.                         float z = bounds.min.z + bounds.size.z / slicesPerAxis * (0.5f + iz);
    395.  
    396.                         var p = transform.InverseTransformPoint(new Vector3(x, y, z));
    397.  
    398.                         if (PointIsInsideMeshCollider(meshCol, p))
    399.                         {
    400.                             points.Add(p);
    401.                         }
    402.                     }
    403.                 }
    404.             }
    405.             if (points.Count == 0)
    406.             {
    407.                 points.Add(bounds.center);
    408.             }
    409.  
    410.             meshCol.convex = convexValue;
    411.         }
    412.         else
    413.         {
    414.             // Convex slicing
    415.             var bounds = coll.bounds;
    416.             for (int ix = 0; ix < slicesPerAxis; ix++)
    417.             {
    418.                 for (int iy = 0; iy < slicesPerAxis; iy++)
    419.                 {
    420.                     for (int iz = 0; iz < slicesPerAxis; iz++)
    421.                     {
    422.                         float x = bounds.min.x + bounds.size.x / slicesPerAxis * (0.5f + ix);
    423.                         float y = bounds.min.y + bounds.size.y / slicesPerAxis * (0.5f + iy);
    424.                         float z = bounds.min.z + bounds.size.z / slicesPerAxis * (0.5f + iz);
    425.  
    426.                         var p = transform.InverseTransformPoint(new Vector3(x, y, z));
    427.  
    428.                         points.Add(p);
    429.                     }
    430.                 }
    431.             }
    432.         }
    433.  
    434.         return points;
    435.     }
    436.  
    437.     /// <summary>
    438.     /// Returns whether the point is inside the mesh collider.
    439.     /// </summary>
    440.     /// <param name="c">Mesh collider.</param>
    441.     /// <param name="p">Point.</param>
    442.     /// <returns>True - the point is inside the mesh collider. False - the point is outside of the mesh collider. </returns>
    443.     private static bool PointIsInsideMeshCollider(Collider c, Vector3 p)
    444.     {
    445.         Vector3[] directions = { Vector3.up, Vector3.down, Vector3.left, Vector3.right, Vector3.forward, Vector3.back };
    446.  
    447.         foreach (var ray in directions)
    448.         {
    449.             RaycastHit hit;
    450.             if (c.Raycast(new Ray(p - ray * 1000, ray), out hit, 1000f) == false)
    451.             {
    452.                 return false;
    453.             }
    454.         }
    455.  
    456.         return true;
    457.     }
    458.  
    459.     /// <summary>
    460.     /// Returns two closest points in the list.
    461.     /// </summary>
    462.     /// <param name="list">List of points.</param>
    463.     /// <param name="firstIndex">Index of the first point in the list. It's always less than the second index.</param>
    464.     /// <param name="secondIndex">Index of the second point in the list. It's always greater than the first index.</param>
    465.     private static void FindClosestPoints(IList<Vector3> list, out int firstIndex, out int secondIndex)
    466.     {
    467.         float minDistance = float.MaxValue, maxDistance = float.MinValue;
    468.         firstIndex = 0;
    469.         secondIndex = 1;
    470.  
    471.         for (int i = 0; i < list.Count - 1; i++)
    472.         {
    473.             for (int j = i + 1; j < list.Count; j++)
    474.             {
    475.                 float distance = Vector3.Distance(list[i], list[j]);
    476.                 if (distance < minDistance)
    477.                 {
    478.                     minDistance = distance;
    479.                     firstIndex = i;
    480.                     secondIndex = j;
    481.                 }
    482.                 if (distance > maxDistance)
    483.                 {
    484.                     maxDistance = distance;
    485.                 }
    486.             }
    487.         }
    488.     }
    489.  
    490.     /// <summary>
    491.     /// Welds closest points.
    492.     /// </summary>
    493.     /// <param name="list">List of points.</param>
    494.     /// <param name="targetCount">Target number of points in the list.</param>
    495.     private static void WeldPoints(IList<Vector3> list, int targetCount)
    496.     {
    497.         if (list.Count <= 2 || targetCount < 2)
    498.         {
    499.             return;
    500.         }
    501.  
    502.         while (list.Count > targetCount)
    503.         {
    504.             int first, second;
    505.             FindClosestPoints(list, out first, out second);
    506.  
    507.             var mixed = (list[first] + list[second]) * 0.5f;
    508.             list.RemoveAt(second); // the second index is always greater that the first => removing the second item first
    509.             list.RemoveAt(first);
    510.             list.Add(mixed);
    511.         }
    512.     }
    513.  
    514.     /// <summary>
    515.     /// Returns the water level at given location.
    516.     /// </summary>
    517.     /// <param name="x">x-coordinate</param>
    518.     /// <param name="z">z-coordinate</param>
    519.     /// <returns>Water level</returns>
    520.     private float GetWaterLevel(float x, float z)
    521.     {
    522.         return ocean == null ? 0.0f : ocean.GetHeightChoppyAtLocation2(x, z);
    523.         //return 5.0f;
    524.     }
    525.  
    526.     /// <summary>
    527.     /// Calculates physics.
    528.     /// </summary>
    529.     private void FixedUpdate()
    530.     {
    531.         forces.Clear(); // For drawing force gizmos
    532.         if (!children)
    533.         {
    534.             foreach (var point in voxels)
    535.             {
    536.                 var wp = transform.TransformPoint(point);
    537.                 waterLevel = GetWaterLevel(wp.x, wp.z);
    538.                 if ((transform.position.y - 0.01f) <= waterLevel)
    539.                 {
    540.                     rigidBody.drag = 1f;
    541.                     rigidBody.angularDrag = 1f;
    542.                 }
    543.                 else
    544.                 {
    545.                     rigidBody.drag = 0f;
    546.                     rigidBody.angularDrag = 0.05f;
    547.                 }
    548.  
    549.  
    550.                 if (wp.y - voxelHalfHeight < waterLevel)
    551.                 {
    552.                     float k = (waterLevel - wp.y) / (2 * voxelHalfHeight) + 0.5f;
    553.                     if (k > 1)
    554.                     {
    555.                         k = 1f;
    556.                     }
    557.                     else if (k < 0)
    558.                     {
    559.                         k = 0f;
    560.                     }
    561.  
    562.                     var velocity = rigidBody.GetPointVelocity(wp);
    563.                     var localDampingForce = -velocity * DAMPFER * rigidBody.mass;
    564.                     var force = localDampingForce + Mathf.Sqrt(k) * localArchimedesForce;
    565.                     rigidBody.AddForceAtPosition(force, wp);
    566.  
    567.                     forces.Add(new[] { wp, force }); // For drawing force gizmos
    568.                 }
    569.             }
    570.         }
    571.         else if (children)
    572.         {
    573.             foreach (var pointlist in allVoxels)
    574.             {
    575.                 foreach (var point in pointlist)
    576.                 {
    577.                     var wp = transform.TransformPoint(point);
    578.                     waterLevel = GetWaterLevel(wp.x, wp.z);
    579.                     if ((transform.position.y - 0.01f) <= waterLevel)
    580.                     {
    581.                         rigidBody.drag = 1f;
    582.                         rigidBody.angularDrag = 1f;
    583.                     }
    584.                     else
    585.                     {
    586.                         rigidBody.drag = 0f;
    587.                         rigidBody.angularDrag = 0.05f;
    588.                     }
    589.  
    590.  
    591.                     if (wp.y - voxelHalfHeight < waterLevel)
    592.                     {
    593.                         float k = (waterLevel - wp.y) / (2 * voxelHalfHeight) + 0.5f;
    594.                         if (k > 1)
    595.                         {
    596.                             k = 1f;
    597.                         }
    598.                         else if (k < 0)
    599.                         {
    600.                             k = 0f;
    601.                         }
    602.  
    603.                         var velocity = rigidBody.GetPointVelocity(wp);
    604.                         var localDampingForce = -velocity * DAMPFER * rigidBody.mass;
    605.                         var force = localDampingForce + Mathf.Sqrt(k) * localArchimedesForce;
    606.                         rigidBody.AddForceAtPosition(force, wp);
    607.  
    608.                         forces.Add(new[] { wp, force }); // For drawing force gizmos
    609.                     }
    610.                 }
    611.             }
    612.         }
    613.     }
    614.  
    615.     /// <summary>
    616.     /// Draws gizmos.
    617.     /// </summary>
    618.     private void OnDrawGizmos()
    619.     {
    620.         if (!children)
    621.         {
    622.             if (voxels == null || forces == null)
    623.             {
    624.                 return;
    625.             }
    626.  
    627.             const float gizmoSize = 0.05f;
    628.             Gizmos.color = Color.yellow;
    629.  
    630.             foreach (var p in voxels)
    631.             {
    632.                 Gizmos.DrawCube(transform.TransformPoint(p), new Vector3(gizmoSize, gizmoSize, gizmoSize));
    633.             }
    634.  
    635.             Gizmos.color = Color.cyan;
    636.  
    637.             foreach (var force in forces)
    638.             {
    639.                 Gizmos.DrawCube(force[0], new Vector3(gizmoSize, gizmoSize, gizmoSize));
    640.                 Gizmos.DrawLine(force[0], force[0] + force[1] / rigidBody.mass);
    641.             }
    642.         }
    643.         else if (children)
    644.         {
    645.             foreach (var voxellist in allVoxels)
    646.             {
    647.                 if (voxellist == null || forces == null)
    648.                 {
    649.                     return;
    650.                 }
    651.  
    652.                 const float gizmoSize = 0.05f;
    653.                 Gizmos.color = Color.yellow;
    654.  
    655.                 foreach (var p in voxellist)
    656.                 {
    657.                     Gizmos.DrawCube(transform.TransformPoint(p), new Vector3(gizmoSize, gizmoSize, gizmoSize));
    658.                 }
    659.  
    660.                 Gizmos.color = Color.cyan;
    661.  
    662.                 foreach (var force in forces)
    663.                 {
    664.                     Gizmos.DrawCube(force[0], new Vector3(gizmoSize, gizmoSize, gizmoSize));
    665.                     Gizmos.DrawLine(force[0], force[0] + force[1] / rigidBody.mass);
    666.                 }
    667.             }
    668.  
    669.             //Gizmos.color = Color.red;
    670.             //Gizmos.DrawSphere( rigidBody.centerOfMass, 0.2f);
    671.         }
    672.     }
    673. }
     
  5. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    466
    What a mess :D

    From what I understood from skimming through the code is that you create a bunch of points (voxels) and check if they're immersed or not. From that, you generate a buoyancy force per voxel. You decide if the drag and angularDrag should be very low or high by checking the transform position y against the water level.

    What I don't get is why for each voxel (foreach loop L.534) you check the rafts transform.position.y against the water level. transform.position.y doesn't describe the geometry of the raft sufficiently. So I guess that might be a typo and you actually wanted to check against wp.y.

    It still doesn't make sense though: You only have one rigidbody and possibly hundreds of voxels. Checking each of their immersions and setting rigidbody.drag/angularDrag just creates a race condition: If the last point in the voxels-array is above the water, drag will be low. If the last voxel is in the water, drag will be high.

    I can see that you're adding a linear damping force in L.563, but you're multiplying by the rigidbodies mass. Consider this: the force exerted by a single voxel increases as your raft grows. Why so? You keep adding parts of the raft on one end, but force at another position get's higher and higher. Try removing the multiplication with rigidbody.mass. You might have to adjust "DAMPFER" (did you mean Dämpfer? Or damper?). I'd also try removing lines 538-547 as they're not needed for the way you modelled the physics but might cover underlying problems.
     
    Last edited: Aug 10, 2020
  6. Splatacat

    Splatacat

    Joined:
    Jan 10, 2013
    Posts:
    13
    Hi there, I haven't been on recently to give feedback.

    You got me thinking and indeed it was a mess, so I got down to writing the code over.

    This got me thinking further and i suddenly realized that i can split the work and let the child objects work out their own voxels and data points and forces for each point, by its weight and not the rafts entire weight. and then pass the force on to the parent rigidbody.

    the parent then calculates the aggregate of all the children's CoM to get a good average of the raft's CoM and recalculates the InertiaTensor.

    So far so good and is working like a charm.

    It also ends up more realistic because different parts of the raft can now have different densities and be more buoyant than other parts :D.

    If I get a chance I'll post a link to a video.

    Thanks for your help.
     
  7. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    466
    I’m glad I could help:)

    please do! I’m working on something similar :)