/// <summary> /// Teleport (as opposed to Move) means that the object is meant to have disappeared at its old position /// and instantaneously reappeared at its new position in frozen space without traversing the space in between. /// </summary> /// <remarks> /// This is equivalent to releasing the existing attachment point and creating a new one, /// except in that the attachment point reference remains valid. /// See <see cref="WorldLockingManager.TeleportAttachmentPoint"/>. /// </remarks> /// <param name="attachPointIface">The attachment point to teleport</param> /// <param name="newFrozenPosition">The position to teleport to.</param> /// <param name="context">The optional context.</param> public void TeleportAttachmentPoint(IAttachmentPoint attachPointIface, Vector3 newFrozenPosition, IAttachmentPoint context) { AttachmentPoint attachPoint = attachPointIface as AttachmentPoint; if (attachPoint != null) { attachPoint.ObjectPosition = newFrozenPosition; // Save the fragment it's currently in, in case it changes here. FragmentId oldFragmentId = attachPoint.FragmentId; // If it's not in a valid fragment, it is still pending and will get processed when the system is ready. if (oldFragmentId.IsKnown()) { FragmentId newFragmentId = GetTargetFragmentId(context); // If there is a valid current fragment, if (newFragmentId.IsKnown()) { // Fill it in with a new one. SetupAttachmentPoint(plugin, attachPoint, context); if (attachPoint.FragmentId != oldFragmentId) { ChangeAttachmentPointFragment(oldFragmentId, attachPoint); } } else { AddPendingAttachmentPoint(attachPoint, context); } } } }
/// <summary> /// If conditions have changed to allow finalizing creation of any pending attachment points, /// do it now. /// </summary> private void ProcessPendingAttachmentPoints() { if (CurrentFragmentId.IsKnown() && pendingAttachments.Count > 0) { // We have a valid destination fragment. Note that since this queue is in order of submission, // if an attachment point depends on a second attachment point for context, // that second will be either earlier in the list (because there was no valid current fragment when it was // created) or it will have a valid fragment. So by the time we get to the one with a dependency (pending.context != null), // its dependency will have a valid fragment id. int pendingCount = pendingAttachments.Count; for (int i = 0; i < pendingCount; ++i) { AttachmentPoint target = pendingAttachments[i].target; Vector3 frozenPosition = pendingAttachments[i].target.ObjectPosition; IAttachmentPoint context = pendingAttachments[i].context; SetupAttachmentPoint(plugin, target, context); FragmentId fragmentId = CurrentFragmentId; if (context != null) { fragmentId = context.FragmentId; } Debug.Assert(fragmentId.IsKnown(), $"FragmentId {fragmentId.FormatStr()} invalid from {(context != null ? "context" : "head")} in processing pending"); Fragment fragment = EnsureFragment(fragmentId); Debug.Assert(fragment != null, "Valid fragmentId but no fragment found"); fragment.AddAttachmentPoint(target); } // All pending must now be in a good home fragment, clear the to-do list. pendingAttachments.Clear(); } }
/// <summary> /// Release any resources bound to this reference point. /// </summary> public void Release() { if (attachmentPoint != null) { manager.AttachmentPointManager.ReleaseAttachmentPoint(attachmentPoint); attachmentPoint = null; } }
/// <summary> /// Dispose of any previously created attachment point. /// </summary> protected void ReleaseAttachment() { if (attachmentPoint != null) { manager.AttachmentPointManager.ReleaseAttachmentPoint(attachmentPoint); attachmentPoint = null; } }
/// <summary> /// Ask the manager for an attachment point, passing delegates for update /// </summary> private void Start() { AttachmentPoint = Manager.CreateAttachmentPoint(gameObject.transform.position, null, HandleAdjustLocation, // Handle adjustments to position HandleAdjustState // Handle connectedness of fragment ); AttachmentPoint.Name = string.Format($"{gameObject.name}=>{AttachmentPoint.Name}"); }
/// <summary> /// Establish which fragment a new attachment point should join. /// </summary> /// <param name="context">Optional spawning attachment point. May be null to "spawn from head".</param> /// <returns>Id of fragment to join. May be FragmentId.Invalid if not currently tracking.</returns> private FragmentId GetTargetFragmentId(IAttachmentPoint context) { FragmentId fragmentId = CurrentFragmentId; if (context != null) { fragmentId = context.FragmentId; } return(fragmentId); }
/// <summary> /// Add a new attachment point to the pending list to be processed when the system is ready. /// </summary> /// <param name="attachPoint">Attachment point to process later.</param> /// <param name="context">Optional spawning attachment point, may be null.</param> private void AddPendingAttachmentPoint(AttachmentPoint attachPoint, IAttachmentPoint context) { // Flag as being in an invalid state attachPoint.HandleStateChange(AttachmentPointStateType.Pending); pendingAttachments.Add( new PendingAttachmentPoint { target = attachPoint, context = context } ); }
/// <summary> /// When the reference point position is initially set, create an attachment point if there isn't one, /// or if there is, updated its position. /// </summary> private void CheckAttachmentPoint() { IAttachmentPointManager mgr = manager.AttachmentPointManager; if (attachmentPoint == null) { attachmentPoint = mgr.CreateAttachmentPoint(LockedPose.position, null, OnLocationUpdate, null); } else { mgr.TeleportAttachmentPoint(attachmentPoint, LockedPose.position, null); } }
/// <summary> /// Notify system attachment point is no longer needed. See <see cref="IAttachmentPointManager.ReleaseAttachmentPoint"/> /// </summary> /// <param name="attachmentPoint"></param> public void ReleaseAttachmentPoint(IAttachmentPoint attachmentPoint) { AttachmentPoint attachPoint = attachmentPoint as AttachmentPoint; if (attachPoint != null) { if (attachPoint.StateHandler != null) { updateStateAllAttachments -= attachPoint.StateHandler; } attachPoint.HandleStateChange(AttachmentPointStateType.Released); attachmentList.Remove(attachPoint); } else { Debug.LogError("On release, IAttachmentPoint isn't AttachmentPoint"); } }
/// <summary> /// Create and register a new attachment point. /// </summary> /// <remarks> /// The attachment point itself is a fairly opaque handle. Its effects are propagated to the client via the /// two handlers associated with it. /// The optional context attachment point provides an optional contextual hint to where in the anchor /// graph to bind the new attachment point. /// See <see cref="IAttachmentPointManager.CreateAttachmentPoint"/>. /// </remarks> /// <param name="frozenPosition">The position in the frozen space at which to start the attachment point</param> /// <param name="context">The optional context into which to create the attachment point (may be null)</param> /// <param name="locationHandler">Delegate to handle WorldLocking system adjustments to position</param> /// <param name="stateHandler">Delegate to handle WorldLocking connectivity changes</param> /// <returns>The new attachment point interface.</returns> public IAttachmentPoint CreateAttachmentPoint(Vector3 frozenPosition, IAttachmentPoint context, AdjustLocationDelegate locationHandler, AdjustStateDelegate stateHandler) { FragmentId fragmentId = GetTargetFragmentId(context); AttachmentPoint attachPoint = new AttachmentPoint(locationHandler, stateHandler); attachPoint.ObjectPosition = frozenPosition; if (fragmentId.IsKnown()) { SetupAttachmentPoint(plugin, attachPoint, context); Fragment fragment = EnsureFragment(fragmentId); Debug.Assert(fragment != null, "Valid fragmentId but no fragment found"); fragment.AddAttachmentPoint(attachPoint); } else { AddPendingAttachmentPoint(attachPoint, context); } return(attachPoint); }
/// <summary> /// Release an attachment point for disposal. The attachment point is no longer valid after this call. /// </summary> /// <remarks> /// In the unlikely circumstance that another attachment point has been spawned from this one /// but has not yet been processed (is still in the pending queue), /// that relationship is broken on release of this one, and when the other attachment point is /// finally processed, it will be as if it was created with a null context. /// </remarks> /// <param name="attachPointIface">The attachment point to release.</param> public void ReleaseAttachmentPoint(IAttachmentPoint attachPointIface) { AttachmentPoint attachPoint = attachPointIface as AttachmentPoint; if (attachPoint != null) { Fragment fragment = EnsureFragment(attachPoint.FragmentId); if (fragment != null) { // Fragment handles notification. fragment.ReleaseAttachmentPoint(attachPoint); } else { // Notify of the state change to released. attachPoint.HandleStateChange(AttachmentPointStateType.Released); // The list of pending attachments is expected to be small, and release of an attachment // point while there are pending attachments is expected to be rare. So brute force it here. // If the attachment point being released is a target in the pending list, remove it. // If it is the context of another pending target, set that context to null. // Proceed through the list in reverse order, because context fixes will only be found // later in the list than the original, and once the original is found we are done. int pendingCount = pendingAttachments.Count; for (int i = pendingCount - 1; i >= 0; --i) { if (pendingAttachments[i].context == attachPoint) { var p = pendingAttachments[i]; p.context = null; pendingAttachments[i] = p; } else if (pendingAttachments[i].target == attachPoint) { pendingAttachments.RemoveAt(i); break; } } } } }
public bool AttachTo(GameObject otherObject, IAttachmentPoint attachmentPoint) { // check for null pointers if (!otherObject) { return(false); } BlockController otherBlock = otherObject.GetComponent <BlockController>(); if (!otherBlock) { return(false); } AttachmentPlug plug = attachmentPoint.GetGameObject().GetComponent <AttachmentPlug>(); if (!plug) { return(false); } attachedTo = plug; DestroyRigidbody(); EnableCollision(); BlockController newRoot = null; if (otherBlock.rootBlock) { newRoot = otherBlock.rootBlock; } else { newRoot = otherBlock; } Reroot(newRoot); attached = true; return(true); }
/// <summary> /// Move (as opposed to Teleport) means that the object is meant to have traversed /// flozen space from its old position to the given new position on some continuous path. /// </summary> /// <remarks> /// Not to be used for automatic (i.e. FrozenWorld Engine instigated) moves. /// See <see cref="WorldLockingManager.MoveAttachmentPoint"/> /// </remarks> /// <param name="attachPoint">Attachment point to move</param> /// <param name="newFrozenPosition">The new position in frozen space</param> public void MoveAttachmentPoint(IAttachmentPoint attachPointIface, Vector3 newFrozenPosition) { AttachmentPoint attachPoint = attachPointIface as AttachmentPoint; if (attachPoint != null) { attachPoint.ObjectPosition = newFrozenPosition; // If it's not in a valid fragment, it is still pending and will get processed when the system is ready. if (attachPoint.FragmentId.IsKnown()) { float minDistToUpdateSq = 0.5f * 0.5f; float moveDistanceSq = (newFrozenPosition - attachPoint.CachedPosition).sqrMagnitude; if (moveDistanceSq > minDistToUpdateSq) { attachPoint.LocationFromAnchor = plugin.MoveAttachmentPoint(newFrozenPosition, attachPoint.AnchorId, attachPoint.LocationFromAnchor); attachPoint.CachedPosition = newFrozenPosition; } // Else we haven't moved enough to bother doing anything. } } }
/// <summary> /// Helper function for setting up the internals of an AttachmentPoint /// </summary> /// <param name="plugin">The global plugin</param> /// <param name="target">The attachment point to setup</param> /// <param name="context">The optional context <see cref="CreateAttachmentPoint"/></param> public static void SetupAttachmentPoint(IPlugin plugin, AttachmentPoint target, IAttachmentPoint context) { if (context != null) { AnchorId anchorId; Vector3 locationFromAnchor; plugin.CreateAttachmentPointFromSpawner(context.AnchorId, context.LocationFromAnchor, target.ObjectPosition, out anchorId, out locationFromAnchor); FragmentId fragmentId = context.FragmentId; target.Set(fragmentId, target.ObjectPosition, anchorId, locationFromAnchor); } else { FragmentId currentFragmentId = plugin.GetMostSignificantFragmentId(); AnchorId anchorId; Vector3 locationFromAnchor; plugin.CreateAttachmentPointFromHead(target.ObjectPosition, out anchorId, out locationFromAnchor); FragmentId fragmentId = currentFragmentId; target.Set(fragmentId, target.ObjectPosition, anchorId, locationFromAnchor); } }
/// <inheritdoc /> public void TeleportTo(IAttachmentPointManager manager, Vector3 newFrozenPosition, IAttachmentPoint parent) { manager.TeleportAttachmentPoint(this, newFrozenPosition, parent); }
private void OnDestroy() { // Let the manager know the attachment point is freed. Manager.ReleaseAttachmentPoint(AttachmentPoint); AttachmentPoint = null; }
/// <summary> /// Let the manager know the attachment point is freed. /// </summary> private void OnDestroy() { Manager.ReleaseAttachmentPoint(AttachmentPoint); AttachmentPoint = null; }
private string DebugAttachmentPoint(string label, IAttachmentPoint att) { return($"aaa {label} A:{att.AnchorId} F:{att.FragmentId} {att.State} {att.LocationFromAnchor.ToString("F3")}\n"); }