Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Bug / Crash  Collider World Bug
#1
Hi, for convenience I moved Obi from Assets to local package and from that moment I encountered very strange bug, I am still not sure if this has been caused by pure random chance because script execution order changed after moving them, or I modified my scene, but in any case there is bug in collider world.

Observation - when new prefab is instantiated into scene (and old one is removed), then for some reason ObiColliders are not registered properly and in result there is no collision with them, unless reenabled.

Investigation - it looks like collider world is destroyed (and also solver implementation and everything else) when there are no colliders to manage. However it does not take into account colliders that should be added during next update. I think that Collider becomes enabled so it registers itself in collision world, but because other collider become destroyed/disabled, then it runs various checks, including DestroyIfUnused() and because number of colliders drops to zero it gets destroyed, but it does not care that there are colliders waiting to be registered! Also actor needs to be reset and is disabled for split of second so Teardown() is called and whole simulation dropped for no reason.

Is adding additional check 'collidersToAdd.Count == 0' in the if statement below valid way to fix this problem?

Code:
private void DestroyIfUnused()
{
    // when there is no data and no implementations, the world gets destroyed.
    // don't check materialHandles.Count == 0, as these are scriptable objects and may outlive the world.
    if (colliderHandles.Count == 0 &&
        rigidbodyHandles.Count == 0 &&
        forceZoneHandles.Count == 0 &&
        implementations.Count == 0)

        Destroy();
}
Reply
#2
Hi Qriva!

Your analysis is correct, but this is a bug that was fixed quite long ago. Which Obi version are you using? DestroyIfUnused() should look like this:

Code:
private void DestroyIfUnused()
        {
            // when there is no data and no implementations, the world gets destroyed.
            // don't check materialHandles.Count == 0, as these are scriptable objects and may outlive the world.
            if (collidersToCreate.Count == 0 &&
                rigidbodiesToCreate.Count == 0 &&
                forceZonesToCreate.Count == 0 &&

                colliderHandles.Count == 0 &&
                rigidbodyHandles.Count == 0 &&
                forceZoneHandles.Count == 0 &&

                implementations.Count == 0)

                Destroy();
        }

In our git the PR adding checks for pending colliders/rigidbodies/forceZones is dated 19 Jun 2025, so it should be included in the latest version. There's been other minor changes to ObiColliderWorld.cs in the production branch since then, I'm leaving the full file here:

Code:
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Obi
{
    public class ObiResourceHandle<T> where T : class
    {
        public T owner = null;               /**< reference to the owner instance*/
        public int index = -1;               /**< index of this resource in the collision world.*/
        private int referenceCount = 0;      /**< amount of references to this handle. Can be used to clean up any associated resources after it reaches zero.*/

        public bool isValid
        {
            get { return index >= 0; }
        }

        public void Invalidate()
        {
            index = -1;
            referenceCount = 0;
        }

        public void Reference()
        {
            referenceCount++;
        }

        public bool Dereference()
        {
            return --referenceCount == 0;
        }

        public ObiResourceHandle(int index = -1)
        {
            this.index = index;
            owner = null;
        }
    }

    public class ObiColliderHandle : ObiResourceHandle<ObiColliderBase>
    {
        public ObiColliderHandle(int index = -1) : base(index) { }
    }
    public class ObiForceZoneHandle : ObiResourceHandle<ObiForceZone>
    {
        public ObiForceZoneHandle(int index = -1) : base(index) { }
    }
    public class ObiCollisionMaterialHandle : ObiResourceHandle<ObiCollisionMaterial>
    {
        public ObiCollisionMaterialHandle(int index = -1) : base(index) { }
    }
    public class ObiRigidbodyHandle : ObiResourceHandle<ObiRigidbodyBase>
    {
        public ObiRigidbodyHandle(int index = -1) : base(index) { }
    }

    public class ObiColliderWorld
    {
        [NonSerialized] public List<IColliderWorldImpl> implementations;

        [NonSerialized] public List<ObiColliderHandle> colliderHandles;           // list of collider handles, used by ObiCollider components to retrieve them.
        [NonSerialized] public ObiNativeColliderShapeList colliderShapes;         // list of collider shapes.
        [NonSerialized] public ObiNativeAabbList colliderAabbs;                   // list of collider bounds.
        [NonSerialized] public ObiNativeAffineTransformList colliderTransforms;   // list of collider transforms.

        [NonSerialized] public List<ObiForceZoneHandle> forceZoneHandles;         // list of collider handles, used by ObiForceZone components to retrieve them.
        [NonSerialized] public ObiNativeForceZoneList forceZones;                 // list of collider force zones.

        [NonSerialized] public List<ObiCollisionMaterialHandle> materialHandles;  // list of material handles, used by ObiCollisionMaterial components to retrieve them.
        [NonSerialized] public ObiNativeCollisionMaterialList collisionMaterials; // list of collision materials.

        [NonSerialized] public List<ObiRigidbodyHandle> rigidbodyHandles;         // list of rigidbody handles, used by ObiRigidbody components to retrieve them.
        [NonSerialized] public ObiNativeRigidbodyList rigidbodies;                // list of rigidbodies.

        [NonSerialized] public ObiTriangleMeshContainer triangleMeshContainer;
        [NonSerialized] public ObiEdgeMeshContainer edgeMeshContainer;
        [NonSerialized] public ObiDistanceFieldContainer distanceFieldContainer;
        [NonSerialized] public ObiHeightFieldContainer heightFieldContainer;

        private List<ObiColliderHandle> collidersToCreate;
        private List<ObiColliderHandle> collidersToDestroy;

        private List<ObiForceZoneHandle> forceZonesToCreate;
        private List<ObiForceZoneHandle> forceZonesToDestroy;

        private List<ObiRigidbodyHandle> rigidbodiesToCreate;
        private List<ObiRigidbodyHandle> rigidbodiesToDestroy;

        public int collidersToUpdateCount { private set; get; } = 0;    // amount of colliders that need to be updated. These are always grouped at the start of the collider arrays (handles, shapes, etc).

        private bool dirty = false;

        private static ObiColliderWorld instance;

        public static ObiColliderWorld GetInstance()
        {
            if (instance == null)
            {
                instance = new ObiColliderWorld();
                instance.Initialize();
            }
            return instance;
        }

        private void Initialize()
        {
            // Allocate all lists:
            if (implementations == null)
                implementations = new List<IColliderWorldImpl>();

            if (colliderHandles == null)
                colliderHandles = new List<ObiColliderHandle>();
            if (colliderShapes == null)
                colliderShapes = new ObiNativeColliderShapeList();
            if (colliderAabbs == null)
                colliderAabbs = new ObiNativeAabbList();
            if (colliderTransforms == null)
                colliderTransforms = new ObiNativeAffineTransformList();

            if (forceZoneHandles == null)
                forceZoneHandles = new List<ObiForceZoneHandle>();
            if (forceZones == null)
                forceZones = new ObiNativeForceZoneList();

            if (materialHandles == null)
                materialHandles = new List<ObiCollisionMaterialHandle>();
            if (collisionMaterials == null)
                collisionMaterials = new ObiNativeCollisionMaterialList();

            if (rigidbodyHandles == null)
                rigidbodyHandles = new List<ObiRigidbodyHandle>();
            if (rigidbodies == null)
                rigidbodies = new ObiNativeRigidbodyList();

            if (triangleMeshContainer == null)
                triangleMeshContainer = new ObiTriangleMeshContainer();
            if (edgeMeshContainer == null)
                edgeMeshContainer = new ObiEdgeMeshContainer();
            if (distanceFieldContainer == null)
                distanceFieldContainer = new ObiDistanceFieldContainer();
            if (heightFieldContainer == null)
                heightFieldContainer = new ObiHeightFieldContainer();

            if (collidersToCreate == null)
                collidersToCreate = new List<ObiColliderHandle>();
            if (collidersToDestroy == null)
                collidersToDestroy = new List<ObiColliderHandle>();

            if (forceZonesToCreate == null)
                forceZonesToCreate = new List<ObiForceZoneHandle>();
            if (forceZonesToDestroy == null)
                forceZonesToDestroy = new List<ObiForceZoneHandle>();

            if (rigidbodiesToCreate == null)
                rigidbodiesToCreate = new List<ObiRigidbodyHandle>();
            if (rigidbodiesToDestroy == null)
                rigidbodiesToDestroy = new List<ObiRigidbodyHandle>();
        }

        private void Destroy()
        {
            dirty = false;
            for (int i = 0; i < implementations.Count; ++i)
            {
                implementations[i].SetColliders(colliderShapes, colliderAabbs, colliderTransforms);
                implementations[i].UpdateWorld(0);
            }

            // Invalidate all handles:
            if (colliderHandles != null)
                foreach (var handle in colliderHandles)
                    handle.Invalidate();

            if (rigidbodyHandles != null)
                foreach (var handle in rigidbodyHandles)
                    handle.Invalidate();

            if (materialHandles != null)
                foreach (var handle in materialHandles)
                    handle.Invalidate();

            if (forceZoneHandles != null)
                foreach (var handle in forceZoneHandles)
                    handle.Invalidate();

            // Dispose of all lists:
            implementations = null;
            colliderHandles = null;
            rigidbodyHandles = null;
            materialHandles = null;
            forceZoneHandles = null;

            collidersToCreate = null;
            collidersToDestroy = null;
            forceZonesToCreate = null;
            forceZonesToDestroy = null;
            rigidbodiesToCreate = null;
            rigidbodiesToDestroy = null;

            colliderShapes?.Dispose();
            colliderAabbs?.Dispose();
            colliderTransforms?.Dispose();
            forceZones?.Dispose();
            collisionMaterials?.Dispose();
            rigidbodies?.Dispose();

            triangleMeshContainer?.Dispose();
            edgeMeshContainer?.Dispose();
            distanceFieldContainer?.Dispose();
            heightFieldContainer?.Dispose();

            instance = null;
        }

        private void DestroyIfUnused()
        {
            // when there is no data and no implementations, the world gets destroyed.
            // don't check materialHandles.Count == 0, as these are scriptable objects and may outlive the world.
            if (collidersToCreate.Count == 0 &&
                rigidbodiesToCreate.Count == 0 &&
                forceZonesToCreate.Count == 0 &&

                colliderHandles.Count == 0 &&
                rigidbodyHandles.Count == 0 &&
                forceZoneHandles.Count == 0 &&

                implementations.Count == 0)

                Destroy();
        }

        public void RegisterImplementation(IColliderWorldImpl impl)
        {
            if (!implementations.Contains(impl))
                implementations.Add(impl);
        }

        public void UnregisterImplementation(IColliderWorldImpl impl)
        {
            implementations.Remove(impl);
            DestroyIfUnused();
        }

        public ObiColliderHandle CreateCollider()
        {
            var handle = new ObiColliderHandle();

            // in-editor, we create data right away since the simulation is not running.
            if (!Application.isPlaying)
                CreateColliderData(handle);
            else
                collidersToCreate.Add(handle);

            return handle;
        }

        public ObiForceZoneHandle CreateForceZone()
        {
            var handle = new ObiForceZoneHandle();

            // in-editor, we create data right away since the simulation is not running.
            if (!Application.isPlaying)
                CreateForceZoneData(handle);
            else
                forceZonesToCreate.Add(handle);

            return handle;
        }

        public ObiRigidbodyHandle CreateRigidbody()
        {
            var handle = new ObiRigidbodyHandle();

            // in-editor, we create data right away since the simulation is not running.
            if (!Application.isPlaying)
                CreateRigidbodyData(handle);
            else
                rigidbodiesToCreate.Add(handle);

            return handle;
        }

        public ObiCollisionMaterialHandle CreateCollisionMaterial()
        {
            var handle = new ObiCollisionMaterialHandle(materialHandles.Count);
            materialHandles.Add(handle);

            collisionMaterials.Add(new CollisionMaterial());

            return handle;
        }

        public ObiTriangleMeshHandle GetOrCreateTriangleMesh(Mesh mesh)
        {
            return triangleMeshContainer.GetOrCreateTriangleMesh(mesh);
        }

        public void DestroyTriangleMesh(ObiTriangleMeshHandle meshHandle)
        {
            triangleMeshContainer.DestroyTriangleMesh(meshHandle);
        }

        public ObiEdgeMeshHandle GetOrCreateEdgeMesh(EdgeCollider2D collider)
        {
            return edgeMeshContainer.GetOrCreateEdgeMesh(collider);
        }

        public void DestroyEdgeMesh(ObiEdgeMeshHandle meshHandle)
        {
            edgeMeshContainer.DestroyEdgeMesh(meshHandle);
        }

        public ObiDistanceFieldHandle GetOrCreateDistanceField(ObiDistanceField df)
        {
            return distanceFieldContainer.GetOrCreateDistanceField(df);
        }

        public void DestroyDistanceField(ObiDistanceFieldHandle dfHandle)
        {
            distanceFieldContainer.DestroyDistanceField(dfHandle);
        }

        public ObiHeightFieldHandle GetOrCreateHeightField(TerrainData hf)
        {
            return heightFieldContainer.GetOrCreateHeightField(hf);
        }

        public void DestroyHeightField(ObiHeightFieldHandle hfHandle)
        {
            heightFieldContainer.DestroyHeightField(hfHandle);
        }

        public void DestroyCollider(ObiColliderHandle handle)
        {
            // Destroy data right away if no simulation is running.
            if (!Application.isPlaying || implementations.Count == 0)
                DestroyColliderData(handle);
            else
            {
                // In case the handle is in the creation queue, just remove it.
                if (!collidersToCreate.Remove(handle))
                    collidersToDestroy.Add(handle);
            }
        }

        public void DestroyForceZone(ObiForceZoneHandle handle)
        {
            // Destroy data right away if no simulation is running.
            if (!Application.isPlaying || implementations.Count == 0)
                DestroyForceZoneData(handle);
            else
            {
                // In case the handle is in the creation queue, just remove it.
                if (!forceZonesToCreate.Remove(handle))
                    forceZonesToDestroy.Add(handle);
            }
        }

        public void DestroyRigidbody(ObiRigidbodyHandle handle)
        {
            // Destroy data right away if no simulation is running.
            if (!Application.isPlaying || implementations.Count == 0)
                DestroyRigidbodyData(handle);
            else
            {
                // In case the handle is in the creation queue, just remove it.
                if (!rigidbodiesToCreate.Remove(handle))
                    rigidbodiesToDestroy.Add(handle);
            }
        }

        public void DestroyCollisionMaterial(ObiCollisionMaterialHandle handle)
        {
            if (collisionMaterials != null && handle != null && handle.isValid && handle.index < materialHandles.Count)
            {
                int index = handle.index;
                int lastIndex = materialHandles.Count - 1;

                // swap all collider info:
                materialHandles.Swap(index, lastIndex);
                collisionMaterials.Swap(index, lastIndex);

                // update the index of the handle we swapped with:
                materialHandles[index].index = index;

                // invalidate our handle:
                // (after updating the swapped one!
                // in case there's just one handle in the array,
                // we need to write -1 after 0)
                handle.Invalidate();

                // remove last index:
                materialHandles.RemoveAt(lastIndex);
                collisionMaterials.count--;

                DestroyIfUnused();
            }
        }

        private void DestroyColliderData (ObiColliderHandle handle)
        {
            if (colliderShapes != null && handle != null && handle.isValid && handle.index < colliderHandles.Count)
            {
                int index = handle.index;
                int lastIndex = colliderHandles.Count - 1;

                // swap all collider info:
                colliderHandles.Swap(index, lastIndex);
                colliderShapes.Swap(index, lastIndex);
                colliderAabbs.Swap(index, lastIndex);
                colliderTransforms.Swap(index, lastIndex);

                // update the index of the handle we swapped with:
                colliderHandles[index].index = index;

                // invalidate our handle:
                // (after updating the swapped one!
                // in case there's just one handle in the array,
                // we need to write -1 after 0)
                handle.Invalidate();

                // remove last index:
                colliderHandles.RemoveAt(lastIndex);
                colliderShapes.count--;
                colliderAabbs.count--;
                colliderTransforms.count--;

                // force all colliders to update next frame, as the index of the data they reference
                // (eg the mesh in a MeshCollider) may have changed as a result of deleting this collider's data.
                collidersToUpdateCount = colliderHandles.Count;

                DestroyIfUnused();
            }
        }

        private void DestroyForceZoneData(ObiForceZoneHandle handle)
        {
            if (forceZones != null && handle != null && handle.isValid && handle.index < forceZoneHandles.Count)
            {
                int index = handle.index;
                int lastIndex = forceZoneHandles.Count - 1;

                // swap all force zone info:
                forceZoneHandles.Swap(index, lastIndex);
                forceZones.Swap(index, lastIndex);

                // update the index of the handle we swapped with:
                forceZoneHandles[index].index = index;

                // invalidate our handle:
                // (after updating the swapped one!
                // in case there's just one handle in the array,
                // we need to write -1 after 0)
                handle.Invalidate();

                // remove last index:
                forceZoneHandles.RemoveAt(lastIndex);
                forceZones.count--;

                DestroyIfUnused();
            }
        }

        private void DestroyRigidbodyData(ObiRigidbodyHandle handle)
        {
            if (rigidbodies != null && handle != null && handle.isValid && handle.index < rigidbodyHandles.Count)
            {
                int index = handle.index;
                int lastIndex = rigidbodyHandles.Count - 1;

                // swap all collider info:
                rigidbodyHandles.Swap(index, lastIndex);
                rigidbodies.Swap(index, lastIndex);

                // update the index of the handle we swapped with:
                rigidbodyHandles[index].index = index;

                // invalidate our handle:
                // (after updating the swapped one!
                // in case there's just one handle in the array,
                // we need to write -1 after 0)
                handle.Invalidate();

                // remove last index:
                rigidbodyHandles.RemoveAt(lastIndex);
                rigidbodies.count--;

                DestroyIfUnused();
            }

        }

        private void CreateColliderData(ObiColliderHandle handle)
        {
            handle.index = colliderHandles.Count;
            colliderHandles.Add(handle);
            colliderShapes.Add(new ColliderShape { materialIndex = -1, rigidbodyIndex = -1, dataIndex = -1 });
            colliderAabbs.Add(new Aabb());
            colliderTransforms.Add(new AffineTransform());

            MarkColliderAsNeedingUpdate(handle);
        }

        private void CreateForceZoneData(ObiForceZoneHandle handle)
        {
            handle.index = forceZoneHandles.Count;
            forceZoneHandles.Add(handle);
            forceZones.Add(new ForceZone());
        }

        private void CreateRigidbodyData(ObiRigidbodyHandle handle)
        {
            handle.index = rigidbodyHandles.Count;
            rigidbodyHandles.Add(handle);
            rigidbodies.Add(new ColliderRigidbody());
        }

        public bool DoesColliderRequireToBeUpdated(ObiColliderHandle handle)
        {
            if (handle != null && handle.isValid &&
                handle.index < colliderHandles.Count && handle.index < collidersToUpdateCount)
                return true;
            return false;
        }

        public void MarkColliderAsNeedingUpdate(ObiColliderHandle handle)
        {
            if (colliderShapes != null && handle != null && handle.isValid &&
                handle.index < colliderHandles.Count && handle.index >= collidersToUpdateCount &&
                collidersToUpdateCount < colliderHandles.Count)
            {
                int index = handle.index;
                int lastIndex = collidersToUpdateCount;

                // swap all collider info:
                colliderHandles.Swap(index, lastIndex);
                colliderShapes.Swap(index, lastIndex);
                colliderAabbs.Swap(index, lastIndex);
                colliderTransforms.Swap(index, lastIndex);

                // update handles:
                colliderHandles[lastIndex].index = lastIndex;
                colliderHandles[index].index = index;

                collidersToUpdateCount++;
            }
        }

        public void MarkColliderAsNotNeedingUpdate(ObiColliderHandle handle)
        {
            if (colliderShapes != null && handle != null && handle.isValid &&
                handle.index < colliderHandles.Count && handle.index < collidersToUpdateCount)
            {
                int index = handle.index;
                int lastIndex = collidersToUpdateCount-1;

                // swap all collider info:
                colliderHandles.Swap(index, lastIndex);
                colliderShapes.Swap(index, lastIndex);
                colliderAabbs.Swap(index, lastIndex);
                colliderTransforms.Swap(index, lastIndex);

                // update handles:
                colliderHandles[lastIndex].index = lastIndex;
                colliderHandles[index].index = index;

                collidersToUpdateCount--;
            }
        }

        public void FlushHandleBuffers()
        {
            // First process destruction, then process creation.
            // In case we create a handle and then destroy it,
            // we should enqueue it for destruction only if it's not in the creation queue.
            // If it is, just remove if from the creation queue.

            if (collidersToDestroy != null)
            {
                foreach (var handle in collidersToDestroy)
                    DestroyColliderData(handle);
                collidersToDestroy?.Clear();
            }

            if (forceZonesToDestroy != null)
            {
                foreach (var handle in forceZonesToDestroy)
                    DestroyForceZoneData(handle);
                forceZonesToDestroy?.Clear();
            }

            if (rigidbodiesToDestroy != null)
            {
                foreach (var handle in rigidbodiesToDestroy)
                    DestroyRigidbodyData(handle);
                rigidbodiesToDestroy?.Clear();
            }

            if (collidersToCreate != null)
            {
                foreach (var handle in collidersToCreate)
                    CreateColliderData(handle);
                collidersToCreate?.Clear();
            }

            if (forceZonesToCreate != null)
            {
                foreach (var handle in forceZonesToCreate)
                    CreateForceZoneData(handle);
                forceZonesToCreate?.Clear();
            }

            if (rigidbodiesToCreate != null)
            {
                foreach (var handle in rigidbodiesToCreate)
                    CreateRigidbodyData(handle);
                rigidbodiesToCreate?.Clear();
            }
          
        }

        public void UpdateWorld(float deltaTime, bool updateDynamics = true)
        {
            if (!dirty)
                return;

            dirty = false;

            // ensure all objects have valid handles.
            // May destroy the world if it's empty,
            // so we next check that handle/implementations are not null.
            FlushHandleBuffers();

            // update all colliders:
            if (colliderHandles != null)
                for (int i = 0; i < collidersToUpdateCount; ++i)
                    colliderHandles[i].owner.UpdateIfNeeded();

            // update all force zones:
            if (forceZoneHandles != null)
                for (int i = 0; i < forceZoneHandles.Count; ++i)
                    forceZoneHandles[i].owner.UpdateIfNeeded();

            // update rigidbodies:
            if (rigidbodyHandles != null && updateDynamics)
                for (int i = 0; i < rigidbodyHandles.Count; ++i)
                    rigidbodyHandles[i].owner.UpdateIfNeeded(deltaTime);

            // update implementations:
            if (implementations != null)
                for (int i = 0; i < implementations.Count; ++i)
                {
                    if (implementations[i].referenceCount > 0)
                    {
                        // set arrays:
                        implementations[i].SetColliders(colliderShapes, colliderAabbs, colliderTransforms);
                        implementations[i].SetForceZones(forceZones);
                        implementations[i].SetRigidbodies(rigidbodies);
                        implementations[i].SetCollisionMaterials(collisionMaterials);
                        implementations[i].SetTriangleMeshData(triangleMeshContainer.headers, triangleMeshContainer.bihNodes, triangleMeshContainer.triangles, triangleMeshContainer.vertices);
                        implementations[i].SetEdgeMeshData(edgeMeshContainer.headers, edgeMeshContainer.bihNodes, edgeMeshContainer.edges, edgeMeshContainer.vertices);
                        implementations[i].SetDistanceFieldData(distanceFieldContainer.headers, distanceFieldContainer.dfNodes);
                        implementations[i].SetHeightFieldData(heightFieldContainer.headers, heightFieldContainer.samples);

                        // update world implementation:
                        if (updateDynamics)
                            implementations[i].UpdateWorld(deltaTime);
                    }
                }
        }

        public void SetDirty()
        {
            dirty = true;
        }

        public void UpdateCollisionMaterials()
        {
            if (implementations != null)
                for (int i = 0; i < implementations.Count; ++i)
                {
                    if (implementations[i].referenceCount > 0)
                    {
                        implementations[i].SetCollisionMaterials(collisionMaterials);
                    }
                }
        }

        public void UpdateRigidbodyVelocities(ObiSolver solver)
        {
            if (solver != null && solver.initialized)
            {
                int count = Mathf.Min(rigidbodyHandles.Count, solver.rigidbodyLinearDeltas.count);

                for (int i = 0; i < count; ++i)
                    rigidbodyHandles[i].owner.UpdateVelocities(solver.rigidbodyLinearDeltas[i], solver.rigidbodyAngularDeltas[i]);
            }

            solver.rigidbodyLinearDeltas.WipeToZero();
            solver.rigidbodyAngularDeltas.WipeToZero();
            solver.rigidbodyLinearDeltas.Upload();
            solver.rigidbodyAngularDeltas.Upload();
        }

    }
}


let me know how it goes,

kind regards
Reply
#3
Hi!

To be honest when it comes to asset store assets it is pretty confusing, perhaps I am missing something, but is there way to check exact version? 
There is only major and minor version. My version is 7.1, but for some reason I already encountered some strange bahviour before where it was marked as 7.1, but something older was inside the project.
   

I compared my code and what you sent above and actually it is simply different, my DestroyIfUnused()
.
On top of that there is another file ObiColliderWorld,cs.orig and I think this is even older version.

.png   obiscript.png (Size: 4.57 KB / Downloads: 1)

Now I wonder if something is wrong with asset store package cache in my projects or is there anything else? Is obi conditional? I mean different one is downloaded for various unity versions? Or maybe obi modules got different scripts in "Common" folder?
Reply