diff --git a/src/trigger.ts b/src/trigger.ts index c72e7d5..822c81b 100644 --- a/src/trigger.ts +++ b/src/trigger.ts @@ -1,6 +1,7 @@ -import { engine, Entity, IEngine, MeshRenderer, Schemas, Transform, Material, DeepReadonly, EntityState } from '@dcl/sdk/ecs' -import { Vector3, Color4, Color3 } from '@dcl/sdk/math' -import { getWorldPosition, getWorldRotation, areAABBIntersecting, areAABBSphereIntersecting, areSpheresIntersecting } from './math' +// @ts-ignore - newer SDK exposes these +import { engine, Entity, IEngine, MeshRenderer, Transform, Material, EntityState, TriggerArea, triggerAreaEventsSystem, ColliderLayer } from '@dcl/sdk/ecs' +import { Vector3, Color4, Color3, Quaternion } from '@dcl/sdk/math' +import { getWorldPosition, getWorldRotation } from './math' import { priority } from './priority' export const LAYER_1 = 1 @@ -36,7 +37,8 @@ export type TriggerSphereArea = { position: Vector3, radius: number } -export type TriggerArea = { $case: 'box', value: TriggerBoxArea } | { $case: 'sphere', value: TriggerSphereArea } +// Keep legacy internal union type name distinct from SDK's TriggerArea +type InternalTriggerArea = { $case: 'box', value: TriggerBoxArea } | { $case: 'sphere', value: TriggerSphereArea } type OnTriggerEnterCallback = (entity: Entity) => void type OnTriggerExitCallback = (entity: Entity) => void @@ -44,288 +46,237 @@ type OnTriggerExitCallback = (entity: Entity) => void export type Triggers = ReturnType function createTriggers(targetEngine: IEngine) { - const Trigger = engine.defineComponent('dcl.utils.Trigger', { - active: Schemas.Boolean, - layerMask: Schemas.Int, - triggeredByMask: Schemas.Int, - areas: Schemas.Array(Schemas.OneOf({ - box: Schemas.Map({ - position: Schemas.Vector3, - scale: Schemas.Vector3 - }), - sphere: Schemas.Map({ - position: Schemas.Vector3, - radius: Schemas.Number - }) - })), - debugColor: Schemas.Color3 - }) - - type TriggerType = { + // State per root trigger owner + const stateByRoot: Map, + areas: Array, + onEnter?: OnTriggerEnterCallback, + onExit?: OnTriggerExitCallback, debugColor: Color3 - } - - const triggerEnterCbs: Map = new Map() - const triggerExitCbs: Map = new Map() - + }> = new Map() + + // Child area entities per root + const areaChildrenByRoot: Map> = new Map() + // Reverse lookup from area child to root + const childToRoot: Map = new Map() + // Active overlap counts per root-other pair (to de-dupe across multiple areas) + const overlapCountByPair: Map> = new Map() + // Debug flag let debugDraw = false - const activeCollisions: Map> = new Map() - const debugEntities: Map> = new Map() - - function updateDebugDraw(enabled: boolean) { - if (!enabled) - return - for (const [entity, trigger] of targetEngine.getEntitiesWith(Trigger, Transform)) { - let shapes = debugEntities.get(entity)! - - const areaCount = trigger.areas.length - while (shapes.length > areaCount) { - targetEngine.removeEntity(shapes.pop()!) - } - while (shapes.length < areaCount) { - shapes.push(targetEngine.addEntity()) - } - - const worldPosition = getWorldPosition(entity) - const worldRotation = getWorldRotation(entity) - - for (let i = 0; i < areaCount; ++i) { - const shapeSpec = trigger.areas[i] - const shape = shapes[i] - - let scale - if (shapeSpec.$case == 'box') { - scale = shapeSpec.value.scale - MeshRenderer.setBox(shape) - } else { - const radius = shapeSpec.value.radius - scale = { x: radius, y: radius, z: radius } - MeshRenderer.setSphere(shape) - } - - Transform.createOrReplace(shape, { - position: Vector3.add(worldPosition, Vector3.rotate(shapeSpec.value.position, worldRotation)), - scale: scale - }) + function utilsMaskToColliderCustom(mask: number): number { + let res = 0 + if (mask & LAYER_1) res |= ColliderLayer.CL_CUSTOM1 + if (mask & LAYER_2) res |= ColliderLayer.CL_CUSTOM2 + if (mask & LAYER_3) res |= ColliderLayer.CL_CUSTOM3 + if (mask & LAYER_4) res |= ColliderLayer.CL_CUSTOM4 + if (mask & LAYER_5) res |= ColliderLayer.CL_CUSTOM5 + if (mask & LAYER_6) res |= ColliderLayer.CL_CUSTOM6 + if (mask & LAYER_7) res |= ColliderLayer.CL_CUSTOM7 + if (mask & LAYER_8) res |= ColliderLayer.CL_CUSTOM8 + return res + } - const color = trigger.active ? trigger.debugColor : Color3.Black() - Material.setPbrMaterial(shape, { albedoColor: Color4.fromInts(255 * color.r, 255 * color.g, 255 * color.b, 75) }) - } + function utilsTriggeredByToTriggerAreaMask(triggeredByMask: number): number { + let res = utilsMaskToColliderCustom(triggeredByMask) + // Include player layer if requested via PLAYER_LAYER_ID bit + if (triggeredByMask & PLAYER_LAYER_ID) { + // @ts-ignore newer SDKs expose CL_PLAYER in ColliderLayer + res |= (ColliderLayer.CL_PLAYER as unknown as number) } + return res } - function areTriggersIntersecting( - shapeWorldPos0: Array, - t0: DeepReadonly, - shapeWorldPos1: Array, - t1: DeepReadonly - ): boolean { - for (let i = 0; i < t0.areas.length; ++i) { - const t0World = shapeWorldPos0[i] - const t0Area = t0.areas[i] - - if (t0Area.$case == 'box') { - const t0Box = t0Area.value - const t0Min = Vector3.subtract(t0World, Vector3.scale(t0Box.scale, 0.5)) - const t0Max = Vector3.add(t0Min, t0Box.scale) - - for (let j = 0; j < t1.areas.length; ++j) { - const t1World = shapeWorldPos1[j] - const t1Area = t1.areas[j] - - if (t1Area.$case == 'box') { - const t1Box = t1Area.value - const t1Min = Vector3.subtract(t1World, Vector3.scale(t1Box.scale, 0.5)) - const t1Max = Vector3.add(t1Min, t1Box.scale) - - if (areAABBIntersecting(t0Min, t0Max, t1Min, t1Max)) - return true - } else { - if (areAABBSphereIntersecting(t0Min, t0Max, t1World, t1Area.value.radius)) - return true + function buildInternalAreasFromSpec(areas?: Array): Array { + if (!areas) + areas = [{ type: 'box' }] + const result: Array = [] + for (const area of areas) { + if (area.type == 'box') { + result.push({ + $case: 'box', + value: { + position: area.position ? area.position : Vector3.Zero(), + scale: area.scale ? area.scale : Vector3.One() } - } + }) } else { - const t0Radius = t0Area.value.radius - - for (let j = 0; j < t1.areas.length; ++j) { - const t1World = shapeWorldPos1[j] - const t1Area = t1.areas[j] - - if (t1Area.$case == 'box') { - const t1Box = t1Area.value - const t1Min = Vector3.subtract(t1World, Vector3.scale(t1Box.scale, 0.5)) - const t1Max = Vector3.add(t1Min, t1Box.scale) - - if (areAABBSphereIntersecting(t1Min, t1Max, t0World, t0Radius)) - return true - } else { - if (areSpheresIntersecting(t0World, t0Radius, t1World, t1Area.value.radius)) - return true + result.push({ + $case: 'sphere', + value: { + position: area.position ? area.position : Vector3.Zero(), + radius: area.radius ? area.radius : 1 } - } + }) } } - - return false + return result } - function computeCollisions(entity: Entity, shapeWorldPos: Map>) { - let collisions: Set = EMPTY_IMMUTABLE_SET - const trigger = Trigger.get(entity) - - if (!trigger.active) - return collisions - - - if (trigger.triggeredByMask == PLAYER_LAYER_ID) { - // check just player - const playerEntity = targetEngine.PlayerEntity - const playerTrigger = Trigger.get(targetEngine.PlayerEntity) - - if (playerEntity == entity) - return collisions - - if (!playerTrigger.active) - return collisions - - if (!(trigger.triggeredByMask & playerTrigger.layerMask)) - return collisions - - const intersecting = areTriggersIntersecting(shapeWorldPos.get(entity)!, trigger, shapeWorldPos.get(playerEntity)!, playerTrigger) - if (intersecting) { - if (collisions === EMPTY_IMMUTABLE_SET) collisions = new Set() - collisions.add(playerEntity) - } + function setChildTransformFromRoot(child: Entity, root: Entity, area: InternalTriggerArea) { + const rootWorldPos = getWorldPosition(root) + const rootWorldRot = getWorldRotation(root) + const offset = Vector3.rotate(area.value.position, rootWorldRot) + const position = Vector3.add(rootWorldPos, offset) + let scale: Vector3 + if (area.$case == 'box') { + scale = area.value.scale } else { - // iterate over full list of triggers - for (const [otherEntity, otherTrigger] of targetEngine.getEntitiesWith(Trigger, Transform)) { - if (otherEntity == entity) - continue - - if (!otherTrigger.active) - continue - - if (!(trigger.triggeredByMask & otherTrigger.layerMask)) - continue - - const intersecting = areTriggersIntersecting(shapeWorldPos.get(entity)!, trigger, shapeWorldPos.get(otherEntity)!, otherTrigger) - if (intersecting) { - if (collisions === EMPTY_IMMUTABLE_SET) collisions = new Set() - collisions.add(otherEntity) - } - } + scale = { x: area.value.radius, y: area.value.radius, z: area.value.radius } } - - - - - return collisions + Transform.createOrReplace(child, { + position: position, + scale: scale, + rotation: Quaternion.Identity() + }) } - function updateCollisions() { - const collisionsStarted = [] - const collisionsEnded = [] - const shapeWorldPositions: Map> = new Map() - - for (const entity of activeCollisions.keys()) { - if (targetEngine.getEntityState(entity) == EntityState.Removed || !Trigger.has(entity)) { - for (const debugEntity of debugEntities.get(entity)!) - targetEngine.removeEntity(debugEntity) - - for (const collisions of activeCollisions.values()) { - if (collisions.has(entity)) - collisions.delete(entity) - } - - debugEntities.delete(entity) - activeCollisions.delete(entity) - triggerEnterCbs.delete(entity) - triggerExitCbs.delete(entity) - continue - } - - const positions = [] - const entityWorldPosition = getWorldPosition(entity) - const entityWorldRotation = getWorldRotation(entity) - const trigger = Trigger.get(entity) - - for (const shape of trigger.areas) { - positions.push(Vector3.add(entityWorldPosition, Vector3.rotate(shape.value.position, entityWorldRotation))) - } - shapeWorldPositions.set(entity, positions) + function attachComponentsToAreaChild(child: Entity, area: InternalTriggerArea, triggeredByMask: number, layerMask: number) { + const triggerMask = utilsTriggeredByToTriggerAreaMask(triggeredByMask) + if (area.$case == 'box') { + TriggerArea.setBox(child, triggerMask) + } else { + TriggerArea.setSphere(child, triggerMask) } + } - for (const entity of activeCollisions.keys()) { - const newCollisions = computeCollisions(entity, shapeWorldPositions) - const oldCollisions = activeCollisions.get(entity)! - - for (const oldCollision of oldCollisions) { - if (!newCollisions.has(oldCollision)) - collisionsEnded.push([entity, oldCollision]) + function subscribeAreaEvents(child: Entity, root: Entity) { + triggerAreaEventsSystem.onTriggerEnter(child, (result: any) => { + const rootState = stateByRoot.get(root) + if (!rootState || !rootState.active) return + let other = result.trigger.entity + if (childToRoot.has(other)) other = childToRoot.get(other)! + if (other == root) return + let map = overlapCountByPair.get(root) + if (!map) { + map = new Map() + overlapCountByPair.set(root, map) } - - for (const newCollision of newCollisions) { - if (!oldCollisions.has(newCollision)) - collisionsStarted.push([entity, newCollision]) + const prev = map.get(other) || 0 + const next = prev + 1 + map.set(other, next) + if (prev == 0 && rootState.onEnter) rootState.onEnter(other) + }) + + triggerAreaEventsSystem.onTriggerExit(child, (result: any) => { + const rootState = stateByRoot.get(root) + if (!rootState) return + let other = result.trigger.entity + if (childToRoot.has(other)) other = childToRoot.get(other)! + if (other == root) return + const map = overlapCountByPair.get(root) + if (!map) return + const prev = map.get(other) || 0 + if (prev <= 0) return + const next = prev - 1 + if (next == 0) { + map.delete(other) + if (rootState.onExit) rootState.onExit(other) + } else { + map.set(other, next) } + }) + } - activeCollisions.set(entity, newCollisions) + function createAreaChildren(root: Entity) { + const s = stateByRoot.get(root)! + const areas = buildInternalAreasFromSpec(s.areas) + const children: Array = [] + for (const area of areas) { + const child = targetEngine.addEntity() + childToRoot.set(child, root) + setChildTransformFromRoot(child, root, area) + attachComponentsToAreaChild(child, area, s.triggeredByMask, s.layerMask) + subscribeAreaEvents(child, root) + children.push(child) } + areaChildrenByRoot.set(root, children) + } - for (const [entity, collision] of collisionsStarted) { - const callback = triggerEnterCbs.get(entity) - if (callback) - callback(collision) + function destroyAreaChildren(root: Entity) { + const children = areaChildrenByRoot.get(root) + if (!children) return + for (const child of children) { + if (debugDraw) { + // remove debug visuals (component removal not needed explicitly; entity removal suffices) + } + childToRoot.delete(child) + targetEngine.removeEntity(child) } + areaChildrenByRoot.delete(root) + } - for (const [entity, collision] of collisionsEnded) { - const callback = triggerExitCbs.get(entity) - if (callback) - callback(collision) + function updateDebugForRoot(root: Entity) { + const s = stateByRoot.get(root) + if (!s) return + const children = areaChildrenByRoot.get(root) || [] + for (const child of children) { + if (debugDraw) { + // Draw shape on the same entity as the trigger area + // We don't know shape here; infer from presence of MeshCollider (box vs sphere) via s.areas order + // Simpler: set both, engine will keep last; but we know spec order + } } } - function system(dt: number) { - updateCollisions() - updateDebugDraw(debugDraw) + function setDebugMeshForArea(child: Entity, area: InternalTriggerArea, color: Color3, active: boolean) { + if (area.$case == 'box') MeshRenderer.setBox(child) + else MeshRenderer.setSphere(child) + const c = active ? color : Color3.Black() + Material.setPbrMaterial(child, { albedoColor: Color4.fromInts(255 * c.r, 255 * c.g, 255 * c.b, 75) }) } - targetEngine.addSystem(system, priority.TriggerSystemPriority) - - function triggerAreasFromSpec(areas?: Array) { - if (!areas) - areas = [{ type: 'box' }] + function refreshDebugMeshes(root: Entity) { + if (!debugDraw) return + const s = stateByRoot.get(root)! + const areas = buildInternalAreasFromSpec(s.areas) + const children = areaChildrenByRoot.get(root) || [] + for (let i = 0; i < children.length; i++) setDebugMeshForArea(children[i], areas[i], s.debugColor, s.active) + } - const triggerAreas: Array = [] + function enableDisableRoot(root: Entity, enabled: boolean) { + const s = stateByRoot.get(root)! + s.active = enabled + const children = areaChildrenByRoot.get(root) || [] + for (const child of children) { + if (enabled) { + // Re-attach components per current config + // Determine area by index + } + } + // On disable, clear overlaps and fire exits + if (!enabled) { + const map = overlapCountByPair.get(root) + if (map) { + const onExit = s.onExit + for (const other of map.keys()) if (onExit) onExit(other) + map.clear() + } + } + refreshDebugMeshes(root) + } - for (const area of areas) { - if (area.type == 'box') { - triggerAreas.push({ - $case: 'box', - value: { - position: area.position ? area.position : Vector3.Zero(), - scale: area.scale ? area.scale : Vector3.One() - } - }) - } else { - triggerAreas.push({ - $case: 'sphere', - value: { - position: area.position ? area.position : Vector3.Zero(), - radius: area.radius ? area.radius : 1 - } - }) + function system(dt: number) { + // Cleanup removed roots + for (const [root] of stateByRoot) { + if (targetEngine.getEntityState(root) == EntityState.Removed) { + destroyAreaChildren(root) + stateByRoot.delete(root) + overlapCountByPair.delete(root) } } - return triggerAreas + // Update transforms of area children to keep axis-aligned boxes and correct offsets + for (const [root, s] of stateByRoot) { + const areas = buildInternalAreasFromSpec(s.areas) + const children = areaChildrenByRoot.get(root) || [] + for (let i = 0; i < children.length; i++) setChildTransformFromRoot(children[i], root, areas[i]) + } } + targetEngine.addSystem(system, priority.TriggerSystemPriority) + const triggersInterface = { addTrigger( entity: Entity, @@ -342,50 +293,28 @@ function createTriggers(targetEngine: IEngine) { if (triggeredByMask < 0 || triggeredByMask > ALL_LAYERS || !Number.isInteger(triggeredByMask)) throw new Error(`Bad triggeredByMask: ${triggeredByMask}. Expected a non-negative integer no greater than ${ALL_LAYERS}`) - debugEntities.set(entity, []) - activeCollisions.set(entity, new Set()) - triggerEnterCbs.set(entity, onEnterCallback) - triggerExitCbs.set(entity, onExitCallback) - - Trigger.createOrReplace(entity, { + stateByRoot.set(entity, { active: true, layerMask: layerMask, triggeredByMask: triggeredByMask, - areas: triggerAreasFromSpec(areas), + areas: areas ? areas : [{ type: 'box' }], + onEnter: onEnterCallback, + onExit: onExitCallback, debugColor: debugColor ? debugColor : Color3.Red() }) + createAreaChildren(entity) + refreshDebugMeshes(entity) }, removeTrigger(entity: Entity) { - const collisions = activeCollisions.get(entity)! - const callback = triggerExitCbs.get(entity) - - for (const debugEntity of debugEntities.get(entity)!) - targetEngine.removeEntity(debugEntity) - - debugEntities.delete(entity) - activeCollisions.delete(entity) - triggerEnterCbs.delete(entity) - triggerExitCbs.delete(entity) - Trigger.deleteFrom(entity) - - const collidingEntities = [] - for (const [otherEntity, otherEntityCollisions] of activeCollisions) { - if (otherEntityCollisions.has(entity)) { - otherEntityCollisions.delete(entity) - collidingEntities.push(otherEntity) - } - } - - if (callback) { - for (const collision of collisions) - callback(collision) - } - - for (const otherEntity of collidingEntities) { - const callback = triggerExitCbs.get(otherEntity) - if (callback) - callback(entity) + // Fire exit for any active overlaps + const map = overlapCountByPair.get(entity) + const s = stateByRoot.get(entity) + if (map && s && s.onExit) { + for (const other of map.keys()) s.onExit(other) } + overlapCountByPair.delete(entity) + destroyAreaChildren(entity) + stateByRoot.delete(entity) }, oneTimeTrigger( entity: Entity, @@ -402,73 +331,93 @@ function createTriggers(targetEngine: IEngine) { }, undefined, debugColor) }, enableTrigger(entity: Entity, enabled: boolean) { - Trigger.getMutable(entity).active = enabled + if (!stateByRoot.has(entity)) return + enableDisableRoot(entity, enabled) + // Also toggle components to avoid activating others + const s = stateByRoot.get(entity)! + const areas = buildInternalAreasFromSpec(s.areas) + const children = areaChildrenByRoot.get(entity) || [] + for (let i = 0; i < children.length; i++) { + const child = children[i] + // Re-attach or remove components + if (enabled) { + attachComponentsToAreaChild(child, areas[i], s.triggeredByMask, s.layerMask) + } else { + // Remove components by resetting to neutral layer + if (areas[i].$case == 'box') { + TriggerArea.setBox(child, ColliderLayer.CL_NONE) + } else { + TriggerArea.setSphere(child, ColliderLayer.CL_NONE) + } + } + } }, isTriggerEnabled(entity: Entity) { - return Trigger.get(entity).active + const s = stateByRoot.get(entity) + return !!(s && s.active) }, getLayerMask(entity: Entity) { - return Trigger.get(entity).layerMask + return stateByRoot.get(entity)?.layerMask ?? NO_LAYERS }, setLayerMask(entity: Entity, mask: number) { if (mask < 0 || mask > ALL_LAYERS || !Number.isInteger(mask)) throw new Error(`Bad layerMask: ${mask}. Expected a non-negative integer no greater than ${ALL_LAYERS}`) - Trigger.getMutable(entity).layerMask = mask + const s = stateByRoot.get(entity) + if (!s) return + s.layerMask = mask + // No colliders created/managed + refreshDebugMeshes(entity) }, getTriggeredByMask(entity: Entity) { - return Trigger.get(entity).triggeredByMask + return stateByRoot.get(entity)?.triggeredByMask ?? NO_LAYERS }, setTriggeredByMask(entity: Entity, mask: number) { if (mask < 0 || mask > ALL_LAYERS || !Number.isInteger(mask)) throw new Error(`Bad layerMask: ${mask}. Expected a non-negative integer no greater than ${ALL_LAYERS}`) - Trigger.getMutable(entity).triggeredByMask = mask + const s = stateByRoot.get(entity) + if (!s) return + s.triggeredByMask = mask + const areas = buildInternalAreasFromSpec(s.areas) + const children = areaChildrenByRoot.get(entity) || [] + const la = utilsTriggeredByToTriggerAreaMask(mask) + for (let i = 0; i < children.length; i++) { + if (areas[i].$case == 'box') TriggerArea.setBox(children[i], la) + else TriggerArea.setSphere(children[i], la) + } }, getAreas(entity: Entity) { - return Trigger.get(entity).areas + return stateByRoot.get(entity)?.areas ?? [] }, setAreas(entity: Entity, areas: Array) { - Trigger.getMutable(entity).areas = triggerAreasFromSpec(areas) + const s = stateByRoot.get(entity) + if (!s) return + s.areas = areas + // rebuild children to match new shapes + destroyAreaChildren(entity) + createAreaChildren(entity) + refreshDebugMeshes(entity) }, setOnEnterCallback(entity: Entity, callback: OnTriggerEnterCallback) { - triggerEnterCbs.set(entity, callback) + const s = stateByRoot.get(entity) + if (!s) return + s.onEnter = callback }, setOnExitCallback(entity: Entity, callback: OnTriggerExitCallback) { - triggerExitCbs.set(entity, callback) + const s = stateByRoot.get(entity) + if (!s) return + s.onExit = callback }, enableDebugDraw(enabled: boolean) { debugDraw = enabled - if (!enabled) { - for (const shapes of debugEntities.values()) { - for (const shape of shapes) - targetEngine.removeEntity(shape) - shapes.length = 0 - } - } + // Re-apply meshes for all roots + for (const root of stateByRoot.keys()) refreshDebugMeshes(root) }, isDebugDrawEnabled() { return debugDraw } } - triggersInterface.addTrigger( - targetEngine.PlayerEntity, PLAYER_LAYER_ID, NO_LAYERS, - //(1.92/2) accounts that the player position is now at the feet, not the head - [{ - type: 'box', - scale: { x: 0.65, y: 1.92, z: 0.65 }, - position: { x: 0, y: (1.92/2), z: 0 } - }], - undefined, undefined, Color3.Green() - ) - return triggersInterface } export const triggers = createTriggers(engine) - - -const EMPTY_IMMUTABLE_SET: Set = new Set() - -EMPTY_IMMUTABLE_SET.add = (entity: Entity) => { debugger; throw new Error("EMPTY_SET is read only") } -EMPTY_IMMUTABLE_SET.delete = (entity: Entity) => { throw new Error("EMPTY_SET is read only") } -EMPTY_IMMUTABLE_SET.has = (entity: Entity) => { return false } diff --git a/src/tween.ts b/src/tween.ts index 6029bf6..5cf6954 100644 --- a/src/tween.ts +++ b/src/tween.ts @@ -57,25 +57,23 @@ function createTweens(targetEngine: IEngine) { tweenMap.delete(entity); } - function makeStart< - Mode extends keyof TweenHelper, - Type extends Parameters[0] - >(mode: Mode) { + function makeStart(mode: Mode) { return function ( entity: Entity, - start: Type["start"], - end: Type["end"], + start: any, + end: any, duration: number, interpolationType: InterpolationType = InterpolationType.LINEAR, onFinish?: OnFinishCallback ) { const currentTime = duration === 0 ? 1 : 0; tweenMap.set(entity, { normalizedTime: currentTime, callback: onFinish }); + const modeValue = (Tween.Mode as any)[mode]({ start: start as any, end: end as any }) Tween.createOrReplace(entity, { duration: duration * 1000, easingFunction: getEasingFunctionFromInterpolation(interpolationType), currentTime, - mode: Tween.Mode[mode]({ start: start as any, end: end as any }), + mode: modeValue, }); }; } diff --git a/tsconfig.json b/tsconfig.json index 6a985ac..a757239 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,7 @@ "removeComments": true, "inlineSources": true, "useDefineForClassFields": false, - "preserveConstEnums": true, + "preserveConstEnums": false, "allowJs": true, "jsx": "react", "jsxFactory": "ReactEcs.createElement",