Tap functions with Object and Audio

Hello,

I am currently using the Studio:World Effects template to create my scene.
I need help with making the object disapear unless I tap on the screen (meaning that the scene should only open to the UI element, and the object only show up when I tap), and the UI element to disappear once I tap.
I would also love to know how I can prompt audio with the tap as well. Thank you so much.
Below is my code:
import * as ecs from ‘@8thwall/ecs’

const {THREE} = (window as any)

// Components used by the Tap Place spawner
const {BoxGeometry, Material, Position, Quaternion, Scale, Shadow, ScaleAnimation} = ecs
const {vec3, quat} = ecs.math

const rgb = (color: number) => ({
r: (color >> 16) % 256, // eslint-disable-line no-bitwise
g: (color >> 8) % 256, // eslint-disable-line no-bitwise
b: color % 256,
})

const componentsForClone = [
Position, Quaternion, Scale, Shadow, BoxGeometry, Material, ScaleAnimation, ecs.PositionAnimation,
ecs.RotateAnimation, ecs.CustomPropertyAnimation, ecs.CustomVec3Animation, ecs.FollowAnimation,
ecs.LookAtAnimation, ecs.GltfModel, ecs.Collider, ecs.ParticleEmitter, ecs.Ui, ecs.Audio,
]

const cloneComponents = (sourceEid, targetEid, world) => {
componentsForClone.forEach((component) => {
if (component.has(world, sourceEid)) {
const properties = component.get(world, sourceEid)
component.set(world, targetEid, {…properties})
}
})
}

const q = quat.zero()
const v = vec3.zero()
const touchPoint = vec3.zero()

ecs.registerComponent({
name: ‘Tap Place’,
schema: {
entityToSpawn: ecs.eid, // Entity ID for the entity to spawn
groundEntity: ecs.eid, // Entity ID for the ground object
minScale: ecs.f32, // Minimum scale for the spawned entity
maxScale: ecs.f32, // Maximum scale for the spawned entity
},
schemaDefaults: {
minScale: 1.0, // Default minimum scale is 1.0
maxScale: 3.0, // Default maximum scale is 3.0
},
stateMachine: ({world, eid, schemaAttribute}) => {
const {groundEntity, entityToSpawn, minScale, maxScale} = schemaAttribute.get(eid)

const raycaster = new THREE.Raycaster()
const mouse = new THREE.Vector2()
let lastInteractionTime = 0
const toFinished = ecs.defineTrigger()

function handleInteraction(event) {
  if (!groundEntity) {
    console.error('Ground entity not set in schema')
    return
  }
  const newGroundEntity = schemaAttribute.get(eid).groundEntity
  const currentTime = Date.now()
  if (currentTime - lastInteractionTime < 500) {
    return
  }
  lastInteractionTime = currentTime

  if (event.touches) {
    mouse.x = (event.touches[0].clientX / window.innerWidth) * 2 - 1
    mouse.y = -(event.touches[0].clientY / window.innerHeight) * 2 + 1
  } else {
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  }

  raycaster.setFromCamera(mouse, world.three.activeCamera)

  const groundObject = world.three.entityToObject.get(newGroundEntity)

  if (!groundObject) {
    console.error('Ground object not found for entity ID:', newGroundEntity)
    return
  }

  const intersects = raycaster.intersectObject(groundObject, true)

  if (intersects.length > 0) {
    touchPoint.setFrom(intersects[0].point)

    const prompt = document.getElementById('promptText')
    if (prompt) {
      prompt.style.display = 'none'
    }

    const newEid = world.createEntity()

    const randomScale = Math.random() * (maxScale - minScale) + minScale

    if (entityToSpawn) {
      // Clone an entity at the target position, with an initial scale of zero.
      cloneComponents(entityToSpawn, newEid, world)
      Position.set(world, newEid, touchPoint)
      Scale.set(world, newEid, v.makeZero())
    } else {
      // If no clone entity is indicated, spawn a box instead.
      Position.set(world, newEid, v.setXyz(touchPoint.x, randomScale / 2, touchPoint.z))
      Quaternion.set(world, newEid, q.makeYDegrees(Math.random() * 360))
      Scale.set(world, newEid, v.makeZero())
      Shadow.set(world, newEid, {castShadow: true, receiveShadow: false})
      BoxGeometry.set(world, newEid, {width: 1, height: 1, depth: 1})
      Material.set(world, newEid, rgb(0xAE00FF))
    }

    ScaleAnimation.set(world, newEid, {
      fromX: 0,
      fromY: 0,
      fromZ: 0,
      toX: randomScale,
      toY: randomScale,
      toZ: randomScale,
      duration: 500,
      loop: false,
      easeIn: true,
      easeOut: true,
    })

    toFinished.trigger()
  }
}

ecs.defineState('finished')
ecs.defineState('tapping')
  .initial()
  .onEnter(() => {
    window.addEventListener('click', handleInteraction)
    window.addEventListener('touchstart', handleInteraction)
  })
  .onExit(() => {
    window.removeEventListener('click', handleInteraction)
    window.removeEventListener('touchstart', handleInteraction)
  })
  .onTrigger(toFinished, 'finished')

},
})

@GeorgeButler Hi George, I met with Pablo a few weeks ago about a technical issue as well. Was hoping to hear your thoughts on this, thanks a lot!

To achieve this, I’d recommend the following steps:

  1. Add a new state and set it as the initial state.
  2. In the add() function, use ecs.Ui.set to attach the UI component to the current entity.
  3. In the new initial state you created, set the text of the UI component to something like “Tap to begin.”
  4. Add an event listener so that when the UI is tapped, it transitions to the “tapping allowed” state.
  5. In the “tapping allowed” state, either remove the UI component or set its text to an empty string (“”).