Issues with ecs.input.SCREEN_TOUCH_START and ecs.Hidden.set

I’ve been stuck for like 2 days and can’t seem to figure out what I’m doing wrong.

I basically have a top down view map with pins or points of interest that are 3d Planes. For each of the pins I have this component attached to it:

import * as ecs from '@8thwall/ecs'
import {addCleanup, doCleanup} from './cleanup'

const MapPin = ecs.registerComponent({
  name: 'MapPin',
  schema: {
    title: ecs.string,
    imageUrl: ecs.string,  // URL or asset ID for the portal image
    ctaText: ecs.string,  // e.g. "OPEN PORTAL"
    spaceName: ecs.string,
  },
  schemaDefaults: {
    title: 'Dallas Arboretum & Botanical Garden',
    imageUrl: 'assets/portalImage.png',
    ctaText: 'Find the blue cardinal at Dallas Arboretum and Botanical Garden & share it on your story @reuniontower for a suprise! ',
    spaceName: 'LizardPortalSpace',
  },
  add: (world, component) => {
    // listen for clicks on *this* pin
    const handler = (e) => {
      console.log('SCREEN_TOUCH_START on eid', component.eid)
      // broadcast globally that *this* pin was tapped
      world.events.dispatch(
        world.events.globalId,
        'pinSelected',
        {pin: component.eid}
      )
    }
    world.events.addListener(component.eid, ecs.input.SCREEN_TOUCH_START, handler)
    addCleanup(component, () => {
      world.events.removeListener(component.eid, ecs.input.SCREEN_TOUCH_START, handler)
    })
  },
  remove: (world, component) => {
    doCleanup(component)
  },
})
export {MapPin}

and then in like a General Empty Entity I have this MapControler attached to it:

// This is a component file. You can use this file to define a custom component for your project.
// This component will appear as a custom component in the editor.

import * as ecs from '@8thwall/ecs'  // This is how you access the ecs library.
import {MapPin} from './MapPin'

ecs.registerComponent({
  name: 'MapControler',
  schema: {
    popUp: ecs.eid,
    popUpButton: ecs.eid,
    popUpImage: ecs.eid,
    popUpTitle: ecs.eid,
    popUpText: ecs.eid,
  },
  // schemaDefaults: {
  // },
  data: {
    selectedPin: ecs.eid,
  },
  // add: (world, component) => {
  // },
  // tick: (world, component) => {
  // },
  // remove: (world, component) => {
  // },
  stateMachine: ({world, eid, schemaAttribute, dataAttribute}) => {
    // Trigger to open the popup
    const open = ecs.defineTrigger()

    ecs.defineState('idle')
      .initial()
      .onEnter(() => {
        const {popUp} = schemaAttribute.get(eid)
        console.log('Entered Default State')

        // hide popUp
        ecs.Hidden.set(world, popUp)
        console.log('Pop should be hidden?')
      })
      // When the global "pinSelected" event fires...
      .listen(
        world.events.globalId,
        'pinSelected',
        (e) => {
          console.log('pinSelected fired!', e.data.pin)
          dataAttribute.set(eid, {selectedPin: e.data.pin})
          open.trigger()
        }
      )
      .onTrigger(open, 'showPopup')

    ecs.defineState('showPopup')
      .onEnter(() => {
        const {selectedPin} = dataAttribute.get(eid)
        const {
          title,
          imageUrl,
          ctaText,
        } = MapPin.get(world, selectedPin)
        const {
          popUp,
          popUpImage,
          popUpTitle,
          popUpText,
        } = schemaAttribute.get(eid)
        // 1) show the popup overlay
        ecs.Hidden.remove(world, popUp)

        ecs.Ui.mutate(world, popUpImage, (cursor) => {
          cursor.image = imageUrl
        })

        ecs.Ui.mutate(world, popUpTitle, (cursor) => {
          cursor.text = title
        })

        ecs.Ui.mutate(world, popUpText, (cursor) => {
          cursor.text = ctaText
        })
      })

      .onEvent(
        ecs.input.UI_CLICK,
        'idle',
        {
          target: schemaAttribute.get(eid).popUpButton,
          where: () => {
            const {selectedPin} = dataAttribute.get(eid)
            const {spaceName} = MapPin.get(world, selectedPin)
            world.spaces.loadSpace(`${spaceName}`)
            // hide popup now or rely on idle.onEnter to re-hide
            ecs.Hidden.set(world, schemaAttribute.get(eid).popUp)
            return true
          },
        }
      )
  },
})

For some the Touch Event never fires and same with the ecs.Hidden.set

For additional context the UI is 3d.

Would really appreciate any help or ways I could trouble shoot. Thank you!

1 Like

I would recommend using a StateMachine to ensure the event listeners are cleaned up correctly. Also make sure you add logging for your events to ensure they’re actually being called.

I am calling these functions for clean up:

// cleanup.ts
const cleanups = new WeakMap<any, (()=>void)[]>()

export function addCleanup(comp: any, fn: ()=>void) {
  const arr = cleanups.get(comp) || []
  arr.push(fn)
  cleanups.set(comp, arr)
}

export function doCleanup(comp: any) {
  for (const fn of cleanups.get(comp) || []) fn()
  cleanups.delete(comp)
}

Is this not the correct way to do it?

I am also confused why in my MapControler ‘idle’ state, ecs.Hidden.set(world, popUp) does not hide the 3d UI Frame?

It’s not that it’s not correct, it’s that the StateMachine system reduces the amount of code you have to write. :slight_smile:

Could it be that the popUp cursor is stale? If you log that Eid what do you get?

what does Stale mean?

Stale is when the object that the Eid is pointing to no longer exists.

I’ve tried a lot of different troubleshooting and it doesn’t look like the 3d popUp is stale. I would really appreciate some help :slight_smile:

I’ve forwarded this to our engineering team who will take a look. Doesn’t look to me like anything obvious would result in Hidden not working.

How would state machine reduce this?

Like rather than adding the event listener in add: it would be in StateMachine on Enter() and remove them on Exit() ?

No, you’d use listen which does that automatically.