Calling UInstancedStaticMeshComponent::RemoveInstance(int32 InstanceIndex) invalidates all indexes > removed index.
This would have been ok (not good but acceptable) if the description at least suggested this might happen. However, neither the description of AddInstance nor RemoveInstance provides any insight in why things get totally ■■■■■■ up after removing an instance.
RemoveInstance description:
/** Remove the instance specified. Returns True on success. */
AddInstance description:
** Add an instance to this component. Transform is given in local space of this component. */
#if WITH_EDITOR
// remove selection flag if array is filled in
if (SelectedInstances.IsValidIndex(InstanceIndex))
{
SelectedInstances.RemoveAt(InstanceIndex);
}
#endif
// update the physics state
if (bPhysicsStateCreated)
{
// TODO: it may be possible to instead just update the BodyInstanceIndex for all bodies after the removed instance.
ClearAllInstanceBodies();
CreateAllInstanceBodies();
}
// Indicate we need to update render state to reflect changes
ReleasePerInstanceRenderData();
MarkRenderStateDirty();
return true;
}
Invalidating the only way of tracking something you create is serious malpractice. I advise you to change that. If not, please update the description in the header file to properly portray that those are volatile.
I understand your issue however it is not something I’m familiar with myself. To help me test reproducing this on my end could you post the code class where you’re setting up your instanced static mesh and calling RemoveInstance() so that I can try to follow your setup?
From my understanding, when you remove the element at the specified index the array shrinks, so the element at “Index+1” moves to “Index”, and everything else rearranges accordingly. In this case, after removing instance1, the element that was stored in the location of instance2 would now be in the instance1 location. In the call to UpdateInstanceTransform() do you see the original instance2 element update if you replace “instance2” with “instance1”.
The documentation for TArray::RemoveAt mentions that the array will resize (if suitable) after removing the specified index. When you make the call to RemoveInstance and the subsequent call to RemoveAt is made, the array re-sizes to eliminate the gap (when index 2 is removed, index 3 moves into index 2’s location rather than leaving index 2 null or filled with a garbage value).
The link to the documentation page for UInstancedStaticMeshComponent::RemoveInstance can be found at the following link. It does not appear that the page has been updated to mention the array shrinking after removing the instance yet. I have updated the original report for better visibility.
Here is the current documentation for RemoveInstance():
Can you please make it more clear in the documentation, that the instance_idx is an always continous index with no gaps and not a handle? That’s what I assumed, but wasn’t sure. Thank you.
Oh geez, this makes it a LOT harder to use instanced meshes for a large number of objects with their own lifetimes. Thanks for the thread, saved me a lot of time trying to figure out the problem – now I just have to figure out the solution.
My personal suggestion is copying the code and changing the variable from TArray to your own implementation with fixed indices.
I would share mine but I went ahead and personalized so it won’t help you.
My suggestion would be index mapping. An easy to understand method is the following:
Instances
InstanceLinks
ExternalLinks
Instance[x] = some instance
InstanceLinks[x] = y (index for externalLinks)
ExternalLinks[y] = x (index for Instance)
remove x:
Instance[x] = Instance[Instance.lastIndex]
InstanceLinks[x] = InstanceLinks[Instance.lastIndex]
Instance.lastIndex--
ExternalLinks[InstanceLinks[x]] = x