Niantic Studio: Cycling through placed models on click? (n00b)

Previously, there was a great example posted for choosing a 3D model to place in AR using buttons:

I have been trying to adapt this to, instead of buttons, simply cycling through an array of a dozen or so models, so that a different model would be placed each click (maybe looping back at the end of the list and removing the first model placed), but I am not yet up to speed with the scripting required (and Claude.ai is absolutely failing me!). Could someone point me in the right direction? Many Thanks!

I’ll help you! Are you using Niantic Studio or Cloud Editor?

Thanks! Working in Studio…

Here’s an example, I made, of a randomized tap to place component.

If you want to use it, clone our World Effects studio project and replace the tap-place component with this code:

import * as ecs from '@8thwall/ecs'

const {THREE} = (window as any)

const {vec3} = ecs.math
const touchPoint = vec3.zero()

const assets = [
  'assets/Soda_Can.glb',
  'assets/Soda_Glass.glb',
  'assets/Soda.glb',
]

ecs.registerComponent({
  name: 'Tap Place',
  schema: {
    groundEntity: ecs.eid,
  },
  schemaDefaults: {
  },
  add: (world, component) => {
    const {groundEntity} = component.schema
    const {eid, schemaAttribute} = component

    if (!groundEntity) {
      console.error('Ground entity not set in schema')
      return
    }

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

    function handleInteraction(event) {
      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 newEid = world.createEntity()
        const randomModel = `${assets[Math.floor(Math.random() * assets.length)]}`

        console.log(randomModel)

        ecs.GltfModel.set(world, newEid, {
          url: randomModel,
        })

        world.setPosition(newEid, touchPoint.x, touchPoint.y, touchPoint.z)

        ecs.Shadow.set(world, newEid, {
          castShadow: true,
          receiveShadow: true,
        })

        ecs.ScaleAnimation.set(world, newEid, {
          fromX: 0,
          fromY: 0,
          fromZ: 0,
          toX: 5,
          toY: 5,
          toZ: 5,
          duration: 500,
          loop: false,
          easingFunction: 'elastic',
          easeOut: true,
        })
      }
    }

    window.addEventListener('click', handleInteraction)
    window.addEventListener('touchstart', handleInteraction)
  },
})

Just keep in mind you’ll need to change the assets at the top of the file to match your own assets.

1 Like

Very generous of you, thanks!
I am traveling right now, but will give this some study over the weekend.
Thanks again!