Providing the 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()
let isSpawned = false
let newEid = null
function findNodeByName(object3D, targetName) {
let targetNode = null
object3D.traverse((child) => {
if (child.name === targetName) {
targetNode = child
}
})
return targetNode
}
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: 5.0, // Default minimum scale is 1.0
maxScale: 10.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
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:', ecs)
return
}
const intersects = raycaster.intersectObject(groundObject, true)
if (intersects.length > 0 && !isSpawned) {
touchPoint.setFrom(intersects[0].point)
const prompt = document.getElementById('promptText')
if (prompt) {
prompt.style.display = 'none'
}
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())
const spawnedObject = world.three.entityToObject.get(newEid)
console.log('world scene', componentsForClone)
if (spawnedObject) {
console.log('Node names in the spawned model:')
const baseNode = findNodeByName(world.scene, 'Base')
if (baseNode) {
console.log('Node "Base" found:', baseNode)
// Perform operations on baseNode, like changing its properties
}
} else {
console.error('Could not find Object3D for newEid:', newEid)
}
isSpawned = true
} 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,
})
}
}
function playAnimation() {
const randomScale = Math.random() * (maxScale - minScale) + minScale
ScaleAnimation.set(world, newEid, {
fromX: 0,
fromY: 0,
fromZ: 0,
toX: randomScale,
toY: randomScale,
toZ: randomScale,
duration: 500,
loop: false,
easeIn: true,
easeOut: true,
})
}
ecs.defineState('default').initial().onEnter(() => {
window.addEventListener('click', handleInteraction)
window.addEventListener('touchstart', handleInteraction)
}).onExit(() => {
window.removeEventListener('click', handleInteraction)
window.removeEventListener('touchstart', handleInteraction)
})
},
})