-
Notifications
You must be signed in to change notification settings - Fork 10
continuous tweens #301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
continuous tweens #301
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| --- | ||
| layout: adr | ||
| adr: 285 | ||
| title: Continuous tweens | ||
| date: 2025-07-31 | ||
| status: Living | ||
| type: RFC | ||
| spdx-license: CC0-1.0 | ||
| authors: | ||
| - nearnshaw | ||
| --- | ||
|
|
||
| # Abstract | ||
|
|
||
| This specification defines how to extend the Tween component to support continuous, infinite or time-bound motion types that move or rotate entities at a constant rate. These new modes include `RotateContinuous`, `MoveContinuous`, and `TextureMoveContinuous`. The motion is defined by a direction and a speed, rather than a start/end pair. The specification also outlines protocol changes, serialization details, and runtime behavior. | ||
|
|
||
| Currently, the Tween component supports only tweens with a start and end value, which is suitable for discrete, finite transitions (e.g., moving from position A to B or rotating from angle X to Y). However, many game elements — such as rotating collectibles, moving textures (e.g., waterfalls), or floating platforms — require continuous, direction-based motion at a fixed speed, not based on a start-end interpolation. | ||
|
|
||
| The current workaround (chaining multiple finite tweens) introduces complexity for creators and causes visual artifacts due to frame-precision issues and unnatural looping. | ||
|
|
||
| ## Decision | ||
|
|
||
| We will introduce three new continuous tween modes to the Tween component: | ||
|
|
||
| - `RotateContinuous` | ||
| - `MoveContinuous` | ||
| - `TextureMoveContinuous` | ||
|
Comment on lines
+23
to
+27
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’d like to start a conversation about an alternative approach—similar to how Unity’s DOTween works. The idea is to introduce the following new modes to the message MoveIncrement {
decentraland.common.Vector3 value = 1;
}
message RotateIncrement {
decentraland.common.Quaternion value = 1;
}
message ScaleIncrement {
decentraland.common.Vector3 value = 1;
}
message TextureMoveIncrement {
decentraland.common.Vector2 value = 1;
optional TextureMovementType movement_type = 2; // default = TextureMovementType.TMT_OFFSET
}In addition, we could introduce a new loop mode for the tween sequence: Here’s how it would work: Tween.create(entity, {
mode: Tween.Mode.MoveIncrement({ value: Vector3.create(10, 10, 10), duration: 2.0 })
})This would move the entity by 10 units along all axes over 2 seconds—executed once. If used in a sequence with different loop modes:
So, using RESTART and YOYO, you can achieve the same results as with the existing non-incremental modes (START/END), but instead of moving between fixed points, the effect is achieved through value addition and subtraction. This would still work with the existing What do you think?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey @kuruk-mm spinning.carpincho.1.movScreen.Recording.2025-07-31.at.11.01.52.AM.movUnless we also move the interpretation of the TweenSequence to the engine, which is something that we should do eventually, but at least short term I don't think we have the bandwidth for it in the Foundation. Both approaches could coexist I think, we don't need to pick just one or the other.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This ADR should not address that issue. It is focused on introducing new functionality (incremental/continuous tweens). To resolve the sequence issue, we should push TweenSequence into the engine. |
||
|
|
||
| These modes enable perpetual, direction-based motion at a fixed speed, optionally for a finite duration. They improve usability and output quality by delegating the motion to engine-level logic, ensuring smooth, jitter-free behavior. | ||
|
|
||
| ## RotateContinuous | ||
|
|
||
|
|
||
| The `RotateContinuous` mode allows to rotate an entity at a constant speed in a given direction. The rotation is defined by a direction and a speed. | ||
|
|
||
| The `RotateContinuous` mode will have the following fields: | ||
|
|
||
| - `direction`: _Quaternion_, the direction of the rotation | ||
| - `speed`: _number_, the speed of the rotation in radians per second | ||
| - `duration`: _number_, the duration of the rotation in milliseconds | ||
| - `easingFunction`: _EasingFunction_ (optional), the easing function to use for the rotation | ||
|
|
||
| Note: The `duration` field is optional. If it is not specified, the rotation will be continuous and infinite. The `easingFunction` field will only be used if the `duration` field is specified. | ||
|
|
||
|
|
||
| ## MoveContinuous | ||
|
|
||
| The `MoveContinuous` mode allows to move an entity at a constant speed in a given direction. The movement is defined by a direction and a speed. | ||
|
|
||
| The `MoveContinuous` mode will have the following fields: | ||
|
|
||
| - `direction`: _Vector3_, the direction of the movement | ||
| - `speed`: _number_, the speed of the movement in meters per second | ||
| - `duration`: _number_, the duration of the movement in milliseconds | ||
| - `easingFunction`: _EasingFunction_ (optional), the easing function to use for the movement | ||
|
|
||
| Note: The `duration` field is optional. If it is not specified, the movement will be continuous and infinite. The `easingFunction` field will only be used if the `duration` field is specified. | ||
|
|
||
|
|
||
| ## TextureMoveContinuous | ||
|
|
||
| The `TextureMoveContinuous` mode allows to move the texture of a material at a constant speed in a given direction. The movement is defined by a direction and a speed. | ||
|
|
||
| The `TextureMoveContinuous` mode will have the following fields: | ||
|
|
||
| - `direction`: _Vector2_, the direction of the movement | ||
| - `speed`: _number_, the speed of the movement expressed as a percentage of the texture size per second | ||
| - `duration`: _number_, the duration of the movement in milliseconds | ||
| - `easingFunction`: _EasingFunction_ (optional), the easing function to use for the movement | ||
|
|
||
| Note: The `duration` field is optional. If it is not specified, the movement will be continuous and infinite. The `easingFunction` field will only be used if the `duration` field is specified. | ||
|
|
||
|
|
||
| ## Easing function to be optional | ||
|
|
||
| Currently the `EasingFunction` field is required for all tweens. We will make it optional for all tweens, including the already existing ones. Setting this field is a nuissance for creators, since it involves importing an enum reference which is not always obvious. The vast majority of the time, creators will want to use the default value, which is `EasingFunction.EF_LINEAR`. | ||
|
|
||
| ## Serialization | ||
|
|
||
| ```yaml | ||
|
|
||
| ``` | ||
|
|
||
| ```protobuf | ||
|
|
||
| ``` | ||
|
|
||
| ## Semantics | ||
|
|
||
| ### Example | ||
|
|
||
|
|
||
| Simple continuos rotation: | ||
|
|
||
| ```ts | ||
| const myEntity = engine.addEntity() | ||
|
|
||
| MeshRenderer.setCube(myEntity) | ||
|
|
||
| Transform.create(myEntity, { | ||
| position: Vector3.create(4, 1, 4), | ||
| }) | ||
|
|
||
| Tween.create(myEntity, { | ||
| mode: Tween.Mode.RotateContinuous({ | ||
| direction: Quaternion.create(0, 0, 0, 1), | ||
| speed: 10, | ||
| }), | ||
| }) | ||
| ``` | ||
|
|
||
| Simple continuous movement: | ||
| ```ts | ||
| const myEntity = engine.addEntity() | ||
|
|
||
| MeshRenderer.setCube(myEntity) | ||
|
|
||
| Transform.create(myEntity, { | ||
| position: Vector3.create(4, 1, 4), | ||
| }) | ||
|
|
||
| Tween.create(myEntity, { | ||
| mode: Tween.Mode.MoveContinuous({ | ||
| direction: Vector3.create(0, 0, 1), | ||
| speed: 10, | ||
| }), | ||
| }) | ||
| ``` | ||
|
|
||
| Simple continuous texture movement: | ||
| ```ts | ||
| const myEntity = engine.addEntity() | ||
|
|
||
| MeshRenderer.setPlane(myEntity) | ||
|
|
||
| Transform.create(myEntity, { | ||
| position: Vector3.create(4, 1, 4), | ||
| }) | ||
|
|
||
| Material.setPbrMaterial(myEntity, { | ||
| texture: Material.Texture.Common({ | ||
| src: 'materials/water.png', | ||
| wrapMode: TextureWrapMode.TWM_REPEAT, | ||
| }), | ||
| }) | ||
|
|
||
| Tween.create(myEntity, { | ||
| mode: Tween.Mode.TextureMoveContinuous({ | ||
| direction: Vector2.create(0, 1), | ||
| speed: 0.1, | ||
| }), | ||
| }) | ||
|
|
||
| TweenSequence.create(myEntity, { sequence: [], loop: TweenLoop.TL_RESTART }) | ||
| ``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By definition a tween is an interpolation, and naming a tween "continuous tween" is in conflict with "linear tweens", besides not reflecting the true nature of what the rest of the component does.
This ADR suits best a brand new component that may be called "Animation", "TransformAnimation" or similar as the semantics are different from a tween(not lerping/interpolating) and closer to CSS animations.
https://en.m.wikipedia.org/wiki/Inbetweening
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pravusjif @nearnshaw JIC you didn't see this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @menduz
sorry yes, we saw it
Yes it's technically true that a continuous tween is an oxymoron
But on the other hand, we felt it was a lot more discoverable to have this functionality in the Tween component. Writing
Tween.and seeing all the options is way better than having to remember that a different component name exists for when you want this other behavior.So we prioritized ease of use over technical correctness
The feature is already developed and in production BTW
https://docs.decentraland.org/creator/development-guide/sdk7/move-entities/#constant-rotation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nearnshaw the link 404
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hukasu here you go:
https://docs.decentraland.org/creator/scenes-sdk7/3d-content-essentials/move-entities#constant-rotation