Possessable
MonoBehaviour component placed on entities that can be possessed (characters, NPCs, vehicles). Manages the possession state — tracks the current Possessor, reparents transforms for spatial coupling, executes configurable PossessAction hooks, and fires events on possession/release.
Definition
Namespace: Paragon.Townskeep.PossessionSystem
Assembly: Townskeep.dll
public class Possessable : ParagonBehaviourInheritance: SerializedMonoBehaviour → ParagonBehaviour → Possessable
Remarks
Possessable is one half of the possession pattern. An entity that can be possessed has a Possessable component; an entity that can possess has a Possessor component. The flow is initiated by the Possessor calling Possess(possessable).
Possession lifecycle
When OnPossessed(possessor) is called:
Stores the
possessorreferenceMoves the possessor's
GameObjectto the possessable's sceneSnaps the possessor's position to the possessable's position
Parents the possessable's transform under the possessor's transform
Executes the
possessedAction(if assigned)Fires the
Possessedevent
When OnReleased(possessor) is called:
Clears the
possessorreference tonullUnparents the possessable's transform (sets parent to
null)Executes the
releasedAction(if assigned)Fires the
Releasedevent
Spatial coupling
While possessed, LateUpdate synchronizes the possessor's position to the possessable's position and resets the possessable's local position to zero. This keeps the possessor physically attached to the possessed entity (e.g., the Player follows the Character).
IPossessable interface
The sibling IPossessable component (e.g., Character) provides the higher-level identity. Possessable.Owner returns the IPossessable, enabling typed queries like possessable.IsPossessedBy<Player>() which checks the possessor's IPossessor owner type.
Quick Lookup
Check if possessed
possessable.IsPossessed()
Check possessor type
possessable.IsPossessedBy<Player>()
Get the possessor
possessable.GetPossessor()
Get typed possessor
possessable.TryGetPossessor<Player>(out var player)
Properties
Owner (internal)
The IPossessable component on the same GameObject. Used internally by the extension methods for typed queries.
Fields
possessedAction
Optional PossessAction executed when this entity is possessed. Configured in the Inspector.
releasedAction
Optional PossessAction executed when this entity is released. Configured in the Inspector.
Events
Possessed
Fires when this entity is possessed.
Released
Fires when this entity is released from possession.
Methods
Awake
Caches the IPossessable component from the same GameObject.
IsPossessed
Returns true if this entity is currently possessed.
IsPossessedBy<TPossessor>
Returns true if the possessor's IPossessor.Owner is of type TPossessor.
GetPossessor
Returns the current Possessor, or null if not possessed.
TryGetPossessor<TPossessor>
Attempts to get the possessor's IPossessor.Owner as a specific type.
OnPossessed
Called by Possessor.Possess(). Sets up spatial coupling, executes the possessedAction, and fires the Possessed event.
possessor
Possessor
The possessor taking control
OnReleased
Called by Possessor.Release(). Clears the possessor, unparents, executes the releasedAction, and fires the Released event.
possessor
Possessor
The possessor releasing control
LateUpdate
Synchronizes the possessor's position to match the possessable's position while possessed. Resets the possessable's local position to zero.
Common Pitfalls
IPossessable required on same GameObject
Awake() calls GetComponent<IPossessable>(). If no component implementing IPossessable exists on the GameObject, Owner will be null and typed queries like IsPossessedBy<T>() will throw NullReferenceException.
Transform reparenting on possession
OnPossessed() parents the possessable's transform under the possessor's transform. This changes the possessable's local coordinate space. Ensure child transforms and sibling components handle reparenting gracefully.
LateUpdate runs every frame while possessed
The position sync in LateUpdate runs unconditionally when possessor != null. For entities possessed long-term (e.g., the player's character), this is expected. The early return when possessor == null prevents unnecessary work.
TryGetPossessor throws if not possessed
TryGetPossessor<T>() accesses this.possessor.Owner without null-checking this.possessor. If called when not possessed, it will throw NullReferenceException. Check IsPossessed() first.
See Also
Last updated