I’m trying to make an application where, when the user hit the ground/screen, one object is projected in the position of the touch. The object appears from the ground and moves to the 0,0 (origin) point in the ground while going from 0 to 3 in the scale. It should also rotate 180 degrees when moving, but the rotation doesn’t work. Here is where I need help!!
The ground has a Tap Placer and when spawning the object I apply a ScaleAnimation, a PositionAnimation and a RotateAnimation. But only the ScaleAnimation and the PositionAnimation are working properly. But the RotateAnimation is not. Why?
All 3 functions should work the same, but in my case it is not happening. The object never rotates. I can see it moving, I can see it scaling, but the rotation never happens.
In my code you can see they all have loop set to false. When I set loop to true in the RotateAnimation, it works partially. The rotation keeps going forever and never stop.
It is rotating, but not smoothly, like it is not respecting the duration parameter.
What I’m missing??
Why the rotation is not happening in the same way that the scaling and the position ??
Here is my full Tap Place 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, RotateAnimation, PositionAnimation} = 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,
RotateAnimation, PositionAnimation, 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: 1.0, // Default maximum scale is 3.0
},
add: (world, component) => {
const {groundEntity, entityToSpawn, minScale, maxScale} = component.schema
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 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(groundEntity)
if (!groundObject) {
console.error('Ground object not found for entity ID:', groundEntity)
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: 5,
toY: 5,
toZ: 5,
duration: 500,
loop: false,
easeIn: true,
easeOut: true,
})
PositionAnimation.set(world, newEid, {
fromX: touchPoint.x,
fromY: touchPoint.y,
fromZ: touchPoint.z,
toX: 0,
toY: 0,
toZ: 0,
duration: 5000,
loop: false,
easeIn: true,
easeOut: true,
})
RotateAnimation.set(world, newEid, {
fromX: 0,
fromY: 0,
fromZ: 0,
toX: 0,
toY: 180,
toZ: 0,
duration: 1000,
loop: false,
easeIn: true,
easeOut: true,
})
}
}
window.addEventListener('click', handleInteraction)
window.addEventListener('touchstart', handleInteraction)
},
})