mesh.addBehavior() type error with custom behavior

Hi, I tried to create a playground, but it doesn’t support import statements, so I’m not sure how to make one.

Anyways, my code uses imports, the behavior works if I force the type, but the type error makes me think there is a hidden problem.

I have a plane mesh that I’m adding a PointerDragBehavior, and a custom behavior called PointerInputBehavior.

import { PointerDragBehavior } from '@babylonjs/core'
import { PointerInputBehavior } from './PointerInputBehavior'

...

    // Add Grip and scroll behavior
    const scrollDragBehavior = new PointerDragBehavior({ dragPlaneNormal: this.root.up })
    ...
    this.mesh.addBehavior(scrollDragBehavior)

    const pointerInputBehavior = new PointerInputBehavior()
    // TODO FIXME as unknown?
    this.mesh.addBehavior(pointerInputBehavior as unknown as PointerDragBehavior)

The type error is I have to cast my custom behavior to PointerDragBehavior to get it to work (as unknown as …).

If I don’t cast as unknown, I get this type error:

Argument of type 'PointerInputBehavior' is not assignable to parameter of type 'Behavior<Node>'.
  Types of property 'attach' are incompatible.
    Type '(target: AbstractMesh) => void' is not assignable to type '(target: Node) => void'.
      Types of parameters 'target' and 'target' are incompatible.
        Type 'Node' is missing the following properties from type 'AbstractMesh': _internalAbstractMeshDataInfo, cullingStrategy, facetNb, partitioningSubdivisions, and 254 more.

I don’t understand why it see’s the ‘Node’ type.

Here’s my custom behavior’s file (stripped down version of the PointerDragBehavior):

import type { Behavior, Nullable, Observer, PointerInfo } from 'babylonjs'
import { AbstractMesh, Scene, Observable } from 'babylonjs'

export type PointerInputBehaviorOptions = { hello?: string }
const DEFAULTS = {}

/**
 * A behavior that when attached to a mesh will allow the mesh to be dragged around the screen based on pointer events
 */
export class PointerInputBehavior implements Behavior<AbstractMesh> {
  /**
   * Abstract mesh the behavior is set on
   */
  public attachedNode?: AbstractMesh
  private _scene?: Scene
  private _pointerObserver?: Nullable<Observer<PointerInfo>>

  /**
   *  Fires each time behavior enabled state changes
   */
  public onEnabledObservable = new Observable<boolean>()

  /**
   *  If the behavior will emit pointer input events (Default: true)
   */
  public set enabled(value: boolean) {
    if (value != this._enabled) {
      this.onEnabledObservable.notifyObservers(value)
    }
    this._enabled = value
  }

  public get enabled() {
    return this._enabled
  }
  private _enabled = true

  private _options: PointerInputBehaviorOptions

  /**
   * Gets the options used by the behavior
   */
  public get options(): PointerInputBehaviorOptions {
    return this._options
  }

  /**
   * Sets the options used by the behavior
   */
  public set options(options: PointerInputBehaviorOptions) {
    this._options = options
  }

  /**
   * Creates a pointer drag behavior that can be attached to a mesh
   */
  constructor(options?: PointerInputBehaviorOptions) {
    this._options = options || DEFAULTS
  }

  /**
   *  The name of the behavior
   */
  public get name(): string {
    return 'PointerInput'
  }

  /**
   *  Initializes the behavior
   */
  public init() {
    console.log('init')
  }

  /**
   * Attaches the drag behavior the passed in mesh
   * @param target The mesh that will emit inputs when under the pointer
   */
  public attach(target: AbstractMesh): void {
    this._scene = target.getScene()
    this.attachedNode = target

    this._pointerObserver = this._scene.onPointerObservable.add((pointerInfo) => {
      if (!this.enabled) return
      switch (pointerInfo.type) {
        case BABYLON.PointerEventTypes.POINTERDOWN:
          console.log('POINTER DOWN')
          break
        case BABYLON.PointerEventTypes.POINTERUP:
          console.log('POINTER UP')
          break
        case BABYLON.PointerEventTypes.POINTERMOVE:
          console.log('POINTER MOVE')
          break
        case BABYLON.PointerEventTypes.POINTERWHEEL:
          console.log('POINTER WHEEL')
          break
        case BABYLON.PointerEventTypes.POINTERPICK:
          console.log('POINTER PICK')
          break
        case BABYLON.PointerEventTypes.POINTERTAP:
          console.log('POINTER TAP')
          break
        case BABYLON.PointerEventTypes.POINTERDOUBLETAP:
          console.log('POINTER DOUBLE-TAP')
          break
      }
    })
  }

  /**
   *  Detaches the behavior from the mesh
   */
  public detach(): void {
    if (this._pointerObserver) {
      this._scene?.onPointerObservable.remove(this._pointerObserver)
    }
  }
}

Any suggestions? Thank you!

What version of Babylon/core are you using ?

cc @RaananW our Build and Stuff GURU :slight_smile:

You are mixing the use of @babylonjs/core and babylonjs in your imports

You should only use either the es6 ones (@babylonjs/core) or the UMD ones (babylonjs) but not both together or their types wont be compatibles.

2 Likes

Thanks Sebavan, that worked great!

1 Like