the symptom, in your words

"My interface scan returns nothing at runtime"

✓ verified on 26.07.22
lane Writing gameplayposted updated

▸ SYMPTOM

snippet
Scene.GetAllComponents<Component>().OfType<IInteractable>()

compiles, runs, and returns nothing, even though interactable components are clearly in the scene. Prompts never appear; nearest-target logic always finds null.

▸ CAUSE

Interface-based scene scans are unreliable in this engine. GetAllComponents<Component>().OfType<I…>() has been observed to return an empty sequence at runtime even when concrete implementors exist.

Mechanism (confirmed on engine 26.07.22): Scene.GetAllComponents<T>() answers off a component index keyed by concrete type: it does not walk every component in the scene doing an is T runtime check. Asking for the base Component type and filtering the result with .OfType<ISomeInterface>() compiles cleanly and always returns an empty sequence, with no compiler error and no runtime exception. That silence is what lets it ship unnoticed: two call sites reading the identical fact can disagree for a whole session before anyone compares them against a live check.

The gap is specific to interfaces, not to any non-exact-type query. An abstract base class works fine as T: GetAllComponents<SomeAbstractBase>() correctly returns every concrete subclass instance when every candidate shares that one base class. Only the interface-then-OfType pattern comes back empty.

▸ FIX

Keep an explicit union of concrete types:

snippet
public interface IInteractable
{
    string GetPrompt(/* player */);
    void Interact(/* player */);
}

static IEnumerable<IInteractable> AllInteractables(Scene scene)
{
    foreach (var c in scene.GetAllComponents<Door>()) yield return c;
    foreach (var c in scene.GetAllComponents<Chest>()) yield return c;
    foreach (var c in scene.GetAllComponents<Workbench>()) yield return c;
    // one GetAllComponents<T> per concrete implementor
}

If every implementor happens to share one abstract base class, a single GetAllComponents<SomeAbstractBase>() call replaces the whole union. Either way, centralize the enumeration in one shared function so a future implementer of the interface is a single added line rather than a rediscovery of this gotcha at another call site.

Cache the list on a short timer (e.g. 0.25 s) if you scan every frame.

Pick nearest whose prompt is non-null: components can decline (healthy panel → no prompt; damaged → "re-anchor").

UX that works: floating marker over the target (bounds-top + bob) + a [F] prompt line in the HUD.

▸ WHY IT WORKS

GetAllComponents<T> is typed against concrete Component subclasses the scene graph knows how to enumerate. Filtering a broad Component query by interface does not reliably surface those instances at runtime, so you pay the boring explicit list and get correct results.

Verified on engine 26.07.22: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Added the mechanism (concrete-type index, not a runtime is-T walk) and the abstract-base-class exception; re-verified on engine 26.07.22
  • Published

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord