-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
446 additions
and
219 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import * as components from '../components' | ||
import { PBTween, TweenLoop, TweenStateStatus } from '../components' | ||
import { Entity, IEngine } from '../engine' | ||
import { ReadWriteByteBuffer } from '../serialization/ByteBuffer' | ||
import { dataCompare } from './crdt/utils' | ||
|
||
export type TweenSystem = { | ||
tweenCompleted(entity: Entity): boolean | ||
tweenChanged(entity: Entity): boolean | ||
} | ||
|
||
/** | ||
* @internal | ||
* @returns tween helper to be used on the scene | ||
*/ | ||
export function createTweenSystem(engine: IEngine): TweenSystem { | ||
const Tween = components.Tween(engine) | ||
const TweenState = components.TweenState(engine) | ||
const TweenSequence = components.TweenSequence(engine) | ||
|
||
const cache = new Map< | ||
Entity, | ||
{ | ||
// Used to detect new tweens for the same entity | ||
tween: Uint8Array | ||
// Avoid updaing again the tween in the case we receieve a network tween from other client | ||
frames: number | ||
// Trigger the isCompleted only once per tween | ||
completed: boolean | ||
// Tween has changed on this frame | ||
changed: boolean | ||
} | ||
>() | ||
|
||
function isCompleted(entity: Entity) { | ||
const tweenState = TweenState.getOrNull(entity) | ||
const tween = Tween.getOrNull(entity) | ||
const tweenCache = cache.get(entity) | ||
if (!tweenState || !tween) return false | ||
|
||
if ( | ||
// Renderer notified that the tween is completed | ||
(tweenChanged(entity) || tweenState.state === TweenStateStatus.TS_COMPLETED) && | ||
// Avoid sending isCompleted multiple times | ||
!tweenCache?.completed && | ||
// Amount of frames needed to consider a tween completed | ||
(tweenCache?.frames ?? 0) > 2 | ||
) { | ||
return true | ||
} | ||
|
||
return false | ||
} | ||
|
||
function tweenChanged(entity: Entity) { | ||
const currentTween = Tween.getOrNull(entity) | ||
const prevTween = cache.get(entity)?.tween | ||
|
||
if ((currentTween && !prevTween) || (!currentTween && prevTween)) { | ||
return true | ||
} | ||
|
||
const currentBuff = new ReadWriteByteBuffer() | ||
Tween.schema.serialize(currentTween!, currentBuff) | ||
const equal = dataCompare(currentBuff.toBinary(), prevTween) | ||
|
||
return equal | ||
} | ||
|
||
const restartTweens: (() => void)[] = [] | ||
// Logic for sequence tweens | ||
engine.addSystem(() => { | ||
for (const restart of restartTweens) { | ||
restart() | ||
} | ||
restartTweens.length = 0 | ||
for (const [entity, tween] of engine.getEntitiesWith(Tween)) { | ||
if (tweenChanged(entity)) { | ||
const buffer = new ReadWriteByteBuffer() | ||
Tween.schema.serialize(tween, buffer) | ||
cache.set(entity, { | ||
tween: buffer.toBinary(), | ||
frames: 0, | ||
completed: false, | ||
changed: true | ||
}) | ||
continue | ||
} | ||
const tweenCache = cache.get(entity)! | ||
tweenCache.frames += 1 | ||
tweenCache.changed = false | ||
if (isCompleted(entity)) { | ||
// Reset tween frames. | ||
tweenCache.frames = 0 | ||
// set the tween completed to avoid calling this again for the same tween | ||
tweenCache.completed = true | ||
|
||
const tweenSequence = TweenSequence.getOrNull(entity) | ||
if (!tweenSequence) continue | ||
const { sequence } = tweenSequence | ||
|
||
if (sequence && sequence.length) { | ||
const [nextTweenSequence, ...otherTweens] = sequence | ||
Tween.createOrReplace(entity, nextTweenSequence) | ||
const mutableTweenHelper = TweenSequence.getMutable(entity) | ||
mutableTweenHelper.sequence = otherTweens | ||
if (tweenSequence.loop === TweenLoop.TL_RESTART) { | ||
mutableTweenHelper.sequence.push(tween) | ||
} | ||
} else if (tweenSequence.loop === TweenLoop.TL_YOYO) { | ||
const newTween = backwardsTween(tween) | ||
Tween.createOrReplace(entity, newTween) | ||
} else if (tweenSequence.loop === TweenLoop.TL_RESTART) { | ||
// Tween.getMutable(entity).currentTime = (tween.currentTime || 0) + 0.00001 | ||
Tween.deleteFrom(entity) | ||
cache.delete(entity) | ||
|
||
restartTweens.push(() => { | ||
Tween.createOrReplace(entity, tween) | ||
}) | ||
} | ||
} | ||
} | ||
}, Number.NEGATIVE_INFINITY) | ||
|
||
function backwardsTween(tween: PBTween): PBTween { | ||
if (tween.mode?.$case === 'move' && tween.mode.move) { | ||
return { ...tween, mode: { ...tween.mode, move: { start: tween.mode.move.end, end: tween.mode.move.start } } } | ||
} | ||
if (tween.mode?.$case === 'rotate' && tween.mode.rotate) { | ||
return { | ||
...tween, | ||
mode: { ...tween.mode, rotate: { start: tween.mode.rotate.end, end: tween.mode.rotate.start } } | ||
} | ||
} | ||
if (tween.mode?.$case === 'scale' && tween.mode.scale) { | ||
return { ...tween, mode: { ...tween.mode, scale: { start: tween.mode.scale.end, end: tween.mode.scale.start } } } | ||
} | ||
|
||
throw new Error('Invalid tween') | ||
} | ||
|
||
return { | ||
// This event is fired only once per tween | ||
tweenCompleted: isCompleted, | ||
tweenChanged: (entity) => !!cache.get(entity)?.changed | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
test/build-ecs/fixtures/sdk7-humming-birds-sync/src/moving-platforms-old.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { GltfContainer, Transform, SyncComponents, Entity } from '@dcl/ecs' | ||
import * as utils from '@dcl-sdk/utils' | ||
import { Vector3 } from '@dcl/sdk/math' | ||
import { engine } from '@dcl/sdk/ecs' | ||
import { NetworkManager } from '@dcl/sdk/network-transport/types' | ||
|
||
const diffZ = 20 | ||
|
||
export function createMovingPlatformsOld(networkedEntityFactory: NetworkManager) { | ||
//// triggerable platform | ||
|
||
//// only horizontal | ||
const platform1 = networkedEntityFactory.addEntity(engine) | ||
GltfContainer.create(platform1, { | ||
src: 'models/movingPlatform.glb' | ||
}) | ||
Transform.create(platform1, { | ||
position: Vector3.create(2, 1.5, 8 + diffZ) | ||
}) | ||
SyncComponents.create(platform1, { componentIds: [Transform.componentId] }) | ||
//// only vertical | ||
const platform2 = networkedEntityFactory.addEntity(engine) | ||
GltfContainer.create(platform2, { | ||
src: 'models/movingPlatform.glb' | ||
}) | ||
Transform.create(platform2, { | ||
position: Vector3.create(4, 1.5, 14 + diffZ) | ||
}) | ||
SyncComponents.create(platform2, { componentIds: [Transform.componentId] }) | ||
|
||
//// path with many waypoints | ||
const platform4 = networkedEntityFactory.addEntity(engine) | ||
GltfContainer.create(platform4, { | ||
src: 'models/movingPlatform.glb' | ||
}) | ||
Transform.create(platform4, { | ||
position: Vector3.create(6.5, 7, 4 + diffZ) | ||
}) | ||
SyncComponents.create(platform4, { componentIds: [Transform.componentId] }) | ||
|
||
const platform3 = networkedEntityFactory.addEntity(engine) | ||
GltfContainer.create(platform3, { | ||
src: 'models/movingPlatform.glb' | ||
}) | ||
Transform.create(platform3, { | ||
position: Vector3.create(14, 4, 12 + diffZ) | ||
}) | ||
SyncComponents.create(platform3, { componentIds: [Transform.componentId] }) | ||
startPath( | ||
platform3, | ||
[Vector3.create(14, 4, 12 + diffZ), Vector3.create(14, 4, 4 + diffZ), Vector3.create(14, 4, 12 + diffZ)], | ||
3, | ||
false, | ||
true | ||
) | ||
|
||
startPath( | ||
platform1, | ||
[Vector3.create(2, 1.5, 8 + diffZ), Vector3.create(2, 1.5, 10 + diffZ), Vector3.create(2, 1.5, 8 + diffZ)], | ||
3, | ||
false, | ||
true | ||
) | ||
|
||
startPath( | ||
platform2, | ||
[Vector3.create(4, 1.5, 14 + diffZ), Vector3.create(4, 4, 14 + diffZ), Vector3.create(4, 1.5, 14 + diffZ)], | ||
2, | ||
false, | ||
true | ||
) | ||
|
||
startPath( | ||
platform4, | ||
[ | ||
Vector3.create(6.5, 7, 4 + diffZ), | ||
Vector3.create(6.5, 7, 12 + diffZ), | ||
Vector3.create(6.5, 10.5, 12 + diffZ), | ||
Vector3.create(6.5, 10.5, 4 + diffZ), | ||
Vector3.create(6.5, 7, 4 + diffZ) | ||
], | ||
40, | ||
false, | ||
true | ||
) | ||
} | ||
|
||
// function to make path following recursive | ||
function startPath(entity: Entity, path: Vector3[], duration: number, facePath?: boolean, loop?: boolean) { | ||
utils.paths.startStraightPath(entity, path, duration, false, function () { | ||
if (loop) startPath(entity, path, duration, facePath, loop) | ||
}) | ||
} |
Oops, something went wrong.