onIntersectionEnter action leaves dangling intersection after removal

I’d like to be able to toggle an OnIntersectionEnterTrigger/OnIntersectionExitTrigger action on or off based on game state, but I have a problem that toggling it off and back on again does not behave as expected if there is an active collision while toggling. I created the behavior below to manage the actions and toggle it via addBehavior/removeBehavior.

However, if I remove the behavior and unregister the actions while an intersection is active, that formerly active intersection remains in mesh._intersectionsInProgress indefinitely. Later, when adding the behavior back in, I see the effect of that lingering behavior as follows:

Scenario: Player has returned to the same zone they were in when the action was unregistered

  • Expected behavior: onIntersectionEnter is triggered
  • Observed behavior: no action

Scenario: Player is not in a zone

  • Expected behavior: no action
  • Observed behavior: onIntersectionExit is triggered

Troubleshooting steps that I have tried:

  • Disposing & recreating action manager when toggling
  • Discarding & recreating ExecuteCodeActions when toggling
  • Manually triggering the exit action with ActionManager.processTrigger

While I’d expect the action to clean up its own state when being removed, I’d be happy to manually clear the intersections, but I cannot find any way to do that without modifying the private mesh._intersectionsInProgress property directly.

import { Mesh, ActionManager, ExecuteCodeAction } from "@babylonjs/core";

export default class ActiveZoneBehavior {
  /** ActiveZoneBehavior
   * Triggers a callback when a target mesh enters or exits this mesh's AABB
   * @param  {Mesh} meshToWatch
   * @param  {Function} cbEnter - call when target mesh enters zone
   * @param  {Function} cbExit - call when target mesh exits zone
   */
  constructor(meshToWatch, cbEnter, cbExit) {
    this.name = "ActiveZoneBehavior";
    this.meshToWatch = meshToWatch;
    this.cbEnter = cbEnter;
    this.cbExit = cbExit;
  }
  init() {
    this.enterAction = new ExecuteCodeAction(
      {
        trigger: ActionManager.OnIntersectionEnterTrigger,
        parameter: this.meshToWatch
      },
      this.cbEnter
    );
    this.exitAction = new ExecuteCodeAction(
      {
        trigger: ActionManager.OnIntersectionExitTrigger,
        parameter: this.meshToWatch
      },
      this.cbExit
    );
  }
  attach(mesh) {
    if (!mesh.actionManager) {
      mesh.actionManager = new ActionManager(mesh.getScene());
    }
    mesh.actionManager.registerAction(this.enterAction);
    mesh.actionManager.registerAction(this.exitAction);
    this.mesh = mesh;
  }
  detach() {
    if (!this.mesh || !this.mesh.actionManager) {
      return;
    }
    this.mesh.actionManager.unregisterAction(this.enterAction);
    this.mesh.actionManager.unregisterAction(this.exitAction);
  }
}
1 Like

Pinging @raananw

This sure feels like a small bug in the remove implementation. It’s not even a “feature” :slight_smile:

Looking into that! thanks for reporting.

Quick question - do you have a playground reproduction oyou can share with me?

1 Like

Thanks, Raanan. I don’t have a playground built. Let me know if you require one