Class Interactable
Abstract base class for interactable objects in the game world. Handles interaction logic, network payloads, and UI display. Implements IInteractable and ISpawnable for scene registration and spawning.
public abstract class Interactable : NetworkBehaviour, IInteractable, ISceneObject, ISpawnable
- Inheritance
-
ObjectComponentBehaviourMonoBehaviourNetworkBehaviourInteractable
- Implements
- Derived
- Inherited Members
-
NetworkBehaviour.IsSpawnedNetworkBehaviour.ComponentIndexNetworkBehaviour.NetworkObjectNetworkBehaviour.MAXIMUM_NETWORKBEHAVIOURSNetworkBehaviour.UNSET_NETWORKBEHAVIOUR_IDNetworkBehaviour.ToString()NetworkBehaviour.Reset()NetworkBehaviour.OnValidate()NetworkBehaviour.IsBehaviourReconcilingNetworkBehaviour.ClearReplicateCache()NetworkBehaviour.CreateReconcile()NetworkBehaviour.Reconcile_Reader<T>(PooledReader, ref T)NetworkBehaviour.OnStartServerCalledNetworkBehaviour.OnStartClientCalledNetworkBehaviour.OnStartNetwork()NetworkBehaviour.OnStopNetwork()NetworkBehaviour.OnStopServer()NetworkBehaviour.OnOwnershipServer(NetworkConnection)NetworkBehaviour.OnSpawnServer(NetworkConnection)NetworkBehaviour.OnDespawnServer(NetworkConnection)NetworkBehaviour.OnStartClient()NetworkBehaviour.OnStopClient()NetworkBehaviour.OnOwnershipClient(NetworkConnection)NetworkBehaviour.ClearBuffedRpcs()NetworkBehaviour.ExcludeOwnerFromUnbufferedObserversRpcsNetworkBehaviour.IsClientOnlyNetworkBehaviour.IsServerOnlyNetworkBehaviour.IsHostNetworkBehaviour.IsClientNetworkBehaviour.IsServerNetworkBehaviour.IsDeinitializingNetworkBehaviour.NetworkManagerNetworkBehaviour.ServerManagerNetworkBehaviour.ClientManagerNetworkBehaviour.ObserverManagerNetworkBehaviour.TransportManagerNetworkBehaviour.TimeManagerNetworkBehaviour.SceneManagerNetworkBehaviour.PredictionManagerNetworkBehaviour.RollbackManagerNetworkBehaviour.NetworkObserverNetworkBehaviour.IsClientInitializedNetworkBehaviour.IsClientStartedNetworkBehaviour.IsClientOnlyInitializedNetworkBehaviour.IsClientOnlyStartedNetworkBehaviour.IsServerInitializedNetworkBehaviour.IsServerStartedNetworkBehaviour.IsServerOnlyInitializedNetworkBehaviour.IsServerOnlyStartedNetworkBehaviour.IsHostInitializedNetworkBehaviour.IsHostStartedNetworkBehaviour.IsOfflineNetworkBehaviour.IsNetworkedNetworkBehaviour.GetIsNetworked()NetworkBehaviour.IsManagerReconcilingNetworkBehaviour.ObserversNetworkBehaviour.IsOwnerNetworkBehaviour.IsControllerNetworkBehaviour.HasAuthorityNetworkBehaviour.OwnerNetworkBehaviour.OwnerIdNetworkBehaviour.ObjectIdNetworkBehaviour.LocalConnectionNetworkBehaviour.OwnerMatches(NetworkConnection)NetworkBehaviour.Despawn(GameObject, DespawnType?)NetworkBehaviour.Despawn(NetworkObject, DespawnType?)NetworkBehaviour.Despawn(DespawnType?)NetworkBehaviour.Spawn(GameObject, NetworkConnection, Scene)NetworkBehaviour.Spawn(NetworkObject, NetworkConnection, Scene)NetworkBehaviour.RemoveOwnership()NetworkBehaviour.GiveOwnership(NetworkConnection)NetworkBehaviour.GetInstance<T>()NetworkBehaviour.TryRegisterInstance<T>(T)NetworkBehaviour.UnregisterInstance<T>()NetworkBehaviour.CanLog(LoggingType)MonoBehaviour.IsInvoking()MonoBehaviour.CancelInvoke()MonoBehaviour.StopCoroutine(Coroutine)MonoBehaviour.StopAllCoroutines()MonoBehaviour.destroyCancellationTokenMonoBehaviour.useGUILayoutMonoBehaviour.didStartMonoBehaviour.didAwakeMonoBehaviour.runInEditModeBehaviour.enabledBehaviour.isActiveAndEnabledComponent.GetComponent<T>()Component.TryGetComponent<T>(out T)Component.GetComponentInChildren<T>()Component.GetComponentsInChildren<T>()Component.GetComponentInParent<T>()Component.GetComponentsInParent<T>()Component.GetComponents<T>()Component.GetComponentIndex()Component.CompareTag(TagHandle)Component.transformComponent.transformHandleComponent.gameObjectComponent.tagObject.GetEntityId()Object.GetInstanceID()Object.GetHashCode()Object.InstantiateAsync<T>(T)Object.InstantiateAsync<T>(T, Transform)Object.InstantiateAsync<T>(T, Vector3, Quaternion)Object.InstantiateAsync<T>(T, Transform, Vector3, Quaternion)Object.Instantiate(Object, Vector3, Quaternion)Object.Instantiate(Object, Vector3, Quaternion, Transform)Object.Instantiate(Object)Object.Instantiate(Object, Scene)Object.Instantiate<T>(T, InstantiateParameters)Object.Instantiate<T>(T, Vector3, Quaternion, InstantiateParameters)Object.Instantiate(Object, Transform)Object.Instantiate<T>(T)Object.Instantiate<T>(T, Vector3, Quaternion)Object.Instantiate<T>(T, Vector3, Quaternion, Transform)Object.Instantiate<T>(T, Transform)Object.Destroy(Object)Object.DestroyImmediate(Object)Object.DontDestroyOnLoad(Object)Object.DestroyObject(Object)Object.FindObjectsOfType<T>()Object.FindObjectsByType<T>(FindObjectsSortMode)Object.FindObjectsByType<T>(FindObjectsInactive, FindObjectsSortMode)Object.FindObjectOfType<T>()Object.FindFirstObjectByType<T>()Object.FindAnyObjectByType<T>()Object.FindFirstObjectByType<T>(FindObjectsInactive)Object.FindAnyObjectByType<T>(FindObjectsInactive)Object.FindObjectsByType<T>()Object.FindObjectsByType<T>(FindObjectsInactive)Object.nameObject.hideFlags
Fields
GizmoColor
public Color GizmoColor
Field Value
- Color
InteractionRange
The maximum distance (in units) at which a player can interact with this object.
public float InteractionRange
Field Value
Properties
GameObject
The GameObject associated with this interactable.
public GameObject GameObject { get; }
Property Value
- GameObject
ID
Unique ID for this interactable object (used for network sync).
public long ID { get; set; }
Property Value
InteractRateLimit
The rate limit (in milliseconds) between allowed interactions for this object.
public virtual double InteractRateLimit { get; }
Property Value
Name
The name of this interactable object (defaults to GameObject name).
public virtual string Name { get; }
Property Value
ObjectSpawner
Reference to the object spawner responsible for spawning/despawning this object.
public ObjectSpawner ObjectSpawner { get; set; }
Property Value
OnInteractTriggers
ECA triggers invoked server-side when a player successfully interacts with this object. Configure these in the Inspector to define per-object interaction behaviour without code.
public List<Trigger> OnInteractTriggers { get; }
Property Value
SpawnableSettings
Settings for spawning this object (e.g., prefab, spawn rules).
public SpawnableSettings SpawnableSettings { get; set; }
Property Value
Title
The display title for this interactable, shown in the UI.
public virtual string Title { get; }
Property Value
TitleColor
The color of the title displayed for this interactable in the UI.
public virtual Color TitleColor { get; }
Property Value
- Color
Transform
The transform of this object in the scene.
public Transform Transform { get; }
Property Value
- Transform
Methods
CanInteract(IPlayerCharacter)
Returns true if the specified player character may interact with this object.
public virtual bool CanInteract(IPlayerCharacter character)
Parameters
characterIPlayerCharacterThe player character attempting to interact.
Returns
- bool
True if interaction is allowed, false otherwise.
Remarks
A pure question with no side effects. It used to stamp the character's interact rate-limit as part of answering, which made it unusable as a check: the scene server, the quest system and the client's input handler all call it, and each call silently consumed a limiter meant for one of the others. A quest accepted within the limiter's window of the interaction that opened the dialogue was refused by a debounce nobody intended to spend, and the refusal path sends nothing back — so the player saw a dead keypress. Spend the limiter explicitly with TryConsumeInteractRateLimit(IPlayerCharacter).
Despawn()
Despawns this interactable, returning it to the object pool.
public void Despawn()
Remarks
Routes through the owning ObjectSpawner when there is one, so the spawner can schedule a respawn. Falls back to despawning directly otherwise.
The fallback is the important half. An interactable with no spawner — ground loot dropped by a kill, a container placed by script — used to hit a null-conditional and do nothing at all, so picking it up left the object spawned in the world forever: an invisible, already-looted pickup that every client kept observing. Pooled despawn is also what keeps world items inside the map's fixed memory budget rather than being destroyed and re-instantiated on every drop.
ExecuteOnInteract(EventData)
Fires all OnInteractTriggers with the provided event data. Called by FishMMO.Server.Implementation.World.SceneServer.Interactable.InteractableSystem after server-side validation succeeds.
public bool ExecuteOnInteract(EventData eventData)
Parameters
eventDataEventDataContext for this interaction (initiator, interactable reference, etc.).
Returns
- bool
True when at least one trigger was fired.
Remarks
Every entry is null-checked. A list element left empty in the inspector is an easy authoring slip and used to take the whole interaction down with a NullReferenceException, losing the triggers after it as well as the one that was blank.
An interactable with no triggers at all is warned about once per interaction rather than passing silently. There is no default behaviour left in these classes — the ECA list is the entire implementation — so an empty list means the object does nothing when used, and that is indistinguishable from a broken object unless it says so.
InRange(Transform)
Returns true if the specified transform is within interaction range of this object. Uses squared distance for efficiency.
public bool InRange(Transform transform)
Parameters
transformTransformThe transform to check range against.
Returns
- bool
True if in range, false otherwise.
OnAwake()
Called when the object is awakened. Override to implement custom initialization logic.
public virtual void OnAwake()
OnStartServer()
Registers this interactable in the scene object registry and assigns its ID.
public override void OnStartServer()
Remarks
Deliberately not in Awake under #if UNITY_SERVER, which is where it used
to live. UNITY_SERVER is a build-target define, and the scene server also runs
from the editor — where it is undefined. Every interactable therefore skipped
registration there, kept ID 0, and sent 0 to every client in WritePayload(NetworkConnection, Writer);
the server's own registry stayed empty, so ValidateSceneObject refused every
interaction in the world. Nothing could be talked to, looted, or bound to outside a
dedicated server build. This is the same gate that had already been found and removed
from NPC.OnStartServer, for the same reason.
OnStartServer() is the correct home: FishNet invokes it only on a peer that is actually running a server, and it runs before the spawn message — and therefore before WritePayload(NetworkConnection, Writer) — is built, so the ID a client receives is the one assigned here. Re-registration on pool reuse is a no-op that preserves the existing ID.
ReadPayload(NetworkConnection, Reader)
Reads network payload data for this interactable, setting its ID and registering it in the scene.
public override void ReadPayload(NetworkConnection connection, Reader reader)
Parameters
connectionNetworkConnectionThe network connection.
readerReaderThe network reader.
ResetState(bool)
Resets the state of this interactable, clearing event handlers and spawn settings.
public override void ResetState(bool asServer)
Parameters
asServerboolTrue if called on the server.
TryConsumeInteractRateLimit(IPlayerCharacter)
Spends the character's interact rate-limit, returning false when it is still cooling.
public bool TryConsumeInteractRateLimit(IPlayerCharacter character)
Parameters
characterIPlayerCharacterThe interacting character.
Returns
- bool
True when the limiter was free and has now been spent.
Remarks
Separate from CanInteract(IPlayerCharacter) so a caller that only needs to validate — the quest system, which already holds its own ingress guard — does not consume a budget belonging to the interaction path.
WritePayload(NetworkConnection, Writer)
Writes network payload data for this interactable, sending its ID to the writer.
public override void WritePayload(NetworkConnection connection, Writer writer)
Parameters
connectionNetworkConnectionThe network connection.
writerWriterThe network writer.
Events
OnDespawn
Event invoked when this object is despawned.
public event Action<ISpawnable> OnDespawn