This document is about: FUSION 2
SWITCH TO

Structure cohesion


Available in the Industries Circle
Circle
Fusion XR prototyping addons

This module allows to collaboratively build a network object made a several parts, forming a common structure.

Goals

The design goals of this system where the following:

  • a structure can be made of several structure parts, that can be independently moving (grabbed, in most cases)
  • when a structure part is moving, all the structure parts of the same structure should follow its move seamlessly
  • every part of the structure can be as much as possible added/removed freely by several people at the same time. To limit possible conflict/transition times, authority changes are as limited as possible
  • when a structure part is released near an existing structure (or single structure parts), it should snap to it along predefined attachment point
  • when a structure is grabbed on 2 distinct structure part, it should split in 2. The breaking point should occur on be the most recently sticky together parts
  • to allow breaking a structure with one hand only, some parts can have a lower "weight". When grabbed a structure by a part of lower weight than the max weight of the structure, the structure should break near this part.
  • optionally, we could configure the structure to limit bandwidth usage (to network only one change position when the structure moves, instead of transmitting a new position for all its parts)

Logic overview

To allow collaborative manipulation of a structure made of several structure parts, the logic is the following:

  • structure parts are networked concepts, as well as the link between them. The structure is a local reconstruction, rebuild on each client based on the detection of created/deleted attachments between structure parts, including the one on which the player does not have authority
  • a structure part can contain several attachment points, specific points that can be attached to another structure part's attachment point (unless the part is already in the structure)
  • a link between two attachment point is stored on one of them only, so the state authority is only needed on this one to modify the networked attachment info
  • the state authority of the structure parts in a structure can be shared among several players. The state authority is needed when you manipulate it (grab,...) or when an attachment is created to another attachment point
  • structure part store a networked IsMoving property. This allow all players to know when the structure should break due to moving parts
  • all attachment have an order associated to them, that increase for each new attachment (application wise). This allows to know what are the most recent attachments, which is used in determining where to break a structure when needed
  • structure can break for 2 reasons:
    • when 2 of their parts move at the same time
    • when a structure part is moved, if its StructuralCohesionMode is set to WeightBasedCohesion, and if it has a lower weight than another part included in their common structure
  • a StructureManager keeps track of all structures and deal with new structures creation
  • an optional StructurePartsManager can help to keep track of all parts and their attachment points' tags (for visual effects, lookup, ...)
Structure logic overview

Attachment creation on ungrabbing

When a structure part is ungrabbed, AttachClosestPartInProximity() is called. In it, we look in its current structure for the attachment point that is the closest to another structure part's attachment point that is not already in the structure. The link is stored on the structure part that was in the ungrabbed structure, as there is higher chances that this structure part is already under the ungrabbing user authority. If it is not the case, we take the authority before storing the attachment

New attachment on ungrabbing

The ungrabbed part, that store the attachment, is snapped to match the part it has attached: it is more natural that the part we were not moving stay immobile, while the ungrabbed part position itself to match the existing part/structure position.

AttachmentPoint is an abstract class, and concrete implementation have to determine how to find the closest attachment point (TryFindClosestAttachmentPoint(out AttachmentPoint closestPoint, out float minDistance, bool excludeSameGroupId = true) override), and how to determine the snap position when a matching attachment point is chosen (Snap(AttachmentPoint other) override).

Attachment deletion when 2 parts are moving at the same time in a given structure

When 2 parts are moving in a structure, the structure look for the most recent attachment in the structure that is between the 2 moving parts. The structure will break on this attachment, to give a "rewind" impression by removing the most recent link between the 2 grabbed parts.

splitting structure with 2 moving parts

Attachment deletion when 1 "lightweight" part is moving in a structure

When just one part is moving in a structure, and this structure has a structuralCohesionMode set to StructuralCohesionMode.WeightBasedCohesion, its partWeight is compared to the greatest weight of other parts in the structure.

If its weight is lower, then the structure will break to separate this light-weight part, alongside its "half" of the structure.

If the lightweight part is a "leaf", attached with just one point to the structure, we simply break this attachment.

Otherwise, to determine on which attachment point to break its structure, we look for the attachment points linked to sibling parts. For each of them, we compute the whole structure section "starting" from them, to determine the highest weight of this section. Then, we split on priority on the attachment point leading to the section of the structure with the highest weight, or, in case of equality, on the one with highest attachment order (aka, the most recent attachment).

Repositioning

When a part is moved, other parts have to move too to make sure the structure cohesion persists.

To do so, one part is designed as a reference part, and from there the structure attachments are browsed to reposition each parts to match the already repositioned ones.

This reference parts is usually:

  • the moving part if there is one (when want the structure to follow the grabbed part if there is any)
  • if no part is moving, the one with the lowest id (it is a deterministic criteria, so that all player rebuild the positions in the same way, as the state authority, and hence the authority on positioning is shared among several player)
  • as much as possible, the last known reference part is memorized and kept, to avoid "jumps" in repositioning choices (so we try to stay on the last known moving part instead on using immediately the lowest id as soon as an object is ungrabbed). In rare cases (simultaneous ungrab of 2 objects by 2 players, where the "last moving" part is not the same on all clients), this could lead to distinct repositioning on clients, so this temporary preference is always replaced by the previous deterministic ones after a few seconds. This can lead to one jump of the whole structure in these rare cases, but the structure remains coherent in the meantime.

The repositioning is done during the FixedUpdateNetwork (in fact, more precisely in AfterTick) so that the actual position are stored and synched over the network, but it is also done in the Render phase (in fact, after it in LateUpdate) so that we display a coherent structure locally even if we don't have the authority on it to actually update the positions in real time. The position of each object will be logically coherent in the end, and the render extrapolation of proxy structure part make it visually coherent in between.

Tag compatibility

In AttachmentPoint:

  • AttachmentPointTagsare tags describing the attachment
  • CompatibleAttachmentPointTags are a list of compatible tags, one of them being expected to be present in attached AttachmentPoint's AttachmentPointTags list. Concrete implementation of TryFindClosestAttachmentPoint should enforce this (not enforced in AttachmentPoint itself)

Underlying snap detection

The StructurePart class is independent of any grabbing logic. To set its IsMoving based on its grabbing status, or to call AttachClosestPartInProximity(), the GrabbableStructurePart subclass handles the connection with the XRShared add-on grabbing logic.

Regarding the proximity detection, and snap position logic, the add-on provide an implementation based on the Magnet add-on. In this implementation, the AttachmentPointTags/CompatibleAttachmentPointTags relies on the magnet tags (the magnet tags are automatically set up based on those attachment point fields at start)

Actual classes implementation

Optional containment

To optimize bandwidth, it is possible to use NetworkTransform's parenting: when a parent moves, no data is set on the network for its child repositioning (only the relative position is synched, and it does not change here).

The optional ContainmentHandler class automatically creates a parent container object for its sibling structure parts, if it is in a structure and it has no container available for it. It ensure that all the structure pairs end up in the same container.

The ProxyGrabbing class ensures that moving a GrabbableStructurePart's NetworkGrabbable moves in fact its parent (here, the container).

Finally, the Structure class can decide to skip repositioning its structure parts, if the structure is considered to be stable, aka:

  • no change to the structure occurred recently
  • and all structure parts have a container

Note that the current implementation would imply recurrent object creations / despawns, and should be improved with containers pooling.

Setup

To keep track of available structures and create new ones, a StructureManager needs to be present in the scene.

Optional containment Setup

To be able to use the optional containment logic, a ContainmentManager needs to be present in the scene.

Also, every GrabbableStructurePart needs to have sibling ContainmentHandler and ProxyGrabbing components.

To quickly compare results with or without containment, the ContainmentManager's disableContainerParenting option is similar to removing it from the scene, even while ContainmentHandler components are set up next to StructurePart components.

Stand alone usage of AttachmentPoint

The AttachmentPoint base abstract class can be used out of a structure context: its goal is simply to keep track, synchronize and and notify all clients of a "link" between 2 Attachment. To be used in a structure, an AttachmentPoint subclass must also implement IStructurePartPoint (which is the case of MagnetStructureAttachmentPoint).

MagnetAttachmentPoint

For instance, it can be used to track that a Magnet component just snapped on another (without them being part of a structure).

This is demonstrated in the MagnetAttachmentPoint class. This class will store an attachment on a magnet snap, and will remove it when the 2 magnets are moved apart.

Dependencies

  • XRShared addon
  • Magnets

Download

This addon latest version is included into the Industries addon project

Supported topologies

  • shared mode

Changelog

  • Version 2.0.0: First release
Back to top