Skip to content

feat: add cinematic goto departure and landing animations - #10024

Open
dalkia wants to merge 1 commit into
devfrom
feat/goto-teleport-animation
Open

dalkia wants to merge 1 commit into
devfrom
feat/goto-teleport-animation

Conversation

@dalkia

@dalkia dalkia commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Pull Request Description

What does this PR change?

Test Instructions

Steps (standard run):

metaforge explorer run XXXX  # ← replace with this PR number

Expected result:

Steps (fresh account):

metaforge account create --clear
metaforge explorer run XXXX  # ← replace with this PR number

Expected result:

Automation (if applicable):

metaforge explorer test XXXX

Prerequisites

  • List any required setup steps
  • Include environment/configuration requirements

Test Steps

  1. First step
  2. Second step
  3. Expected result after step 2
  4. ...

Additional Testing Notes

  • Note any edge cases to verify
  • Mention specific areas that need careful testing
  • List known limitations or potential issues

Quality Checklist

  • Changes have been tested locally
  • Documentation has been updated (if required)
  • Performance impact has been considered
  • For SDK features: Test scene is included

Code Review Reference

Please review our Branch & PR Standards before submitting. It explains the automated review flow, QA/DEV approval requirements, and what each label does — especially useful for first-time contributors.

@dalkia
dalkia requested review from a team as code owners September 7, 2026 17:23
@github-actions
github-actions Bot requested a review from DafGreco September 7, 2026 17:23
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

🚦 CI Status

Build

Windows and Mac built successfully in Unity Cloud.

Name Links & timing
Build 6497285 · Logs · built 2026-09-07T18:58:23Z
Windows GitHub job · Unity Cloud #1 · Unity log · ⏱ 55m 35s build + 9m 7s queue · Download .zip · .zip via S3
Mac GitHub job · Unity Cloud #1 · Unity log · ⏱ 1h 25m build + 4m 2s queue · Download .zip · .zip via S3

Lint

Warnings not reduced: 12091 => 12285 — remove at least 195 warnings to merge.

Warnings/errors in files changed by this PR (18)
Assets/DCL/Chat/Teleport/Systems/GotoTeleportTrails.cs:66  ArrangeRedundantParentheses  Redundant parentheses
Assets/DCL/Chat/Teleport/Systems/GotoTeleportAnimationSystem.cs:63  CSharpWarnings::CS8600  Converting null literal or possible null value into non-nullable type
Assets/DCL/Chat/Teleport/Systems/GotoTeleportAnimationSystem.cs:70  CSharpWarnings::CS8600  Converting null literal or possible null value into non-nullable type
Assets/DCL/Chat/Teleport/Systems/GotoTeleportAnimationSystem.cs:90  CSharpWarnings::CS8600  Converting null literal or possible null value into non-nullable type
Assets/DCL/Chat/Teleport/Systems/GotoTeleportAnimationSystem.cs:81  CSharpWarnings::CS8602  Dereference of a possibly null reference
Assets/DCL/Chat/Teleport/Systems/GotoTeleportAnimationSystem.cs:100  CSharpWarnings::CS8604  Possible null reference argument for parameter 'avatar' in 'DCL.Chat.Teleport.GotoTeleportAnimationSystem.Begin'
Assets/DCL/Chat/Teleport/Systems/GotoTeleportAnimationSystem.cs:64  CSharpWarnings::CS8604  Possible null reference argument for parameter 'state' in 'DCL.Chat.Teleport.GotoTeleportAnimationSystem.Restore'
Assets/DCL/Chat/Teleport/Systems/GotoTeleportAnimationSystem.cs:77  CSharpWarnings::CS8604  Possible null reference argument for parameter 'state' in 'DCL.Chat.Teleport.GotoTeleportAnimationSystem.Restore'
Assets/DCL/Chat/Teleport/Systems/GotoTeleportAnimationSystem.cs:134  CSharpWarnings::CS8604  Possible null reference argument for parameter 'state' in 'DCL.Chat.Teleport.GotoTeleportAnimationSystem.Restore'
Assets/DCL/PluginSystem/Global/ChatPlugin.cs:512  CSharpWarnings::CS8618  Non-nullable property 'ChatSendMessageAudio' is uninitialized. Consider adding the 'required' modifier or declaring the property as nullable.
Assets/DCL/PluginSystem/Global/ChatPlugin.cs:515  CSharpWarnings::CS8618  Non-nullable property 'ReactionsConfig' is uninitialized. Consider adding the 'required' modifier or declaring the property as nullable.
Assets/DCL/PluginSystem/Global/ChatPlugin.cs:74  InconsistentNaming  Name 'mainUIView' does not match rule 'non_public_members_should_be_camel_case'. Suggested name is 'mainUiView'.
Assets/DCL/PluginSystem/Global/ChatPlugin.cs:123  InconsistentNaming  Name 'mainUIView' does not match rule 'parameters_should_be_camel_case'. Suggested name is 'mainUiView'.
Assets/DCL/PluginSystem/Global/ChatPlugin.cs:28  RedundantUsingDirective  Using directive is not required by the code and can be safely removed
Assets/DCL/PluginSystem/Global/ChatPlugin.cs:36  RedundantUsingDirective  Using directive is not required by the code and can be safely removed
Assets/DCL/PluginSystem/Global/ChatPlugin.cs:48  RedundantUsingDirective  Using directive is not required by the code and can be safely removed
Assets/DCL/PluginSystem/Global/ChatPlugin.cs:55  RedundantUsingDirective  Using directive is not required by the code and can be safely removed
Assets/DCL/PluginSystem/Global/ChatPlugin.cs:421  VariableCanBeNotNullable  'view' can be declared as non-nullable

Lint run · took 28m 12s

Tests

All Unity tests passed ✅

TESTS SUITE Result Passed Failed Skipped Tests time Job time
EditMode ✅ Passed 25858 0 13 4m 31s 17m 31s
PlayMode ✅ Passed 248 0 37 44s 12m 11s

Tests time sums the test cases; Job time is the job's wall clock including checkout, licensing and asset import.

Slowest tests
  • [editmode] 16.5s DCL.AuthenticationScreenFlow.Tests.ProfileFetchingAuthStateShould.CancelStalledFetchOnTimeout
  • [editmode] 15.3s DCL.Tests.Editor.ValidationTests.CheckForDebugUsage
  • [editmode] 12.5s DCL.Tests.Editor.ValidationTests.CheckUnityObjectsForMissingReferences
  • [editmode] 10.0s DCL.Notifications.Tests.NotificationsRequestControllerShould.ReuseSingleListInstanceAcrossPollIterations
  • [editmode] 5.2s DCL.Tests.Editor.ValidationTests.SettingsAreValid
  • [editmode] 5.0s DCL.Friends.Tests.FriendsConnectivityStatusTrackerShould.RaiseOnlineEventWhenSameStatusIsRebroadcastAfterReset
  • [editmode] 5.0s CrdtEcsBridge.WorldSynchronizer.Tests.CrdtWorldSynchronizerShould.ThrowIfSyncBufferIsAlreadyRented
  • [editmode] 4.4s DCL.AvatarRendering.AvatarShape.Tests.FinishAvatarMatricesCalculationSystemShould.PlaceTheAvatarBoundsInTheWorldTheSameWayTheReferenceFormDoes
  • [editmode] 4.2s SceneRunner.Tests.SceneFacadeShould.ContinueUpdateLoopOnBackgroundThread(180,4000)
  • [editmode] 4.1s SceneRunner.Tests.SceneFacadeShould.ContinueUpdateLoopOnBackgroundThread(30,4000)
  • [playmode] 4.4s Global.Tests.PlayMode.CubeWaveSceneShould.EmitECSComponents
  • [playmode] 3.1s DCL.SDKComponents.Tween.Tests.TweenUpdaterSystemShould.ContinuousTweensRunIndefinitelyWhenDurationIsZero
  • [playmode] 2.4s DCL.SDKComponents.Tween.Tests.TweenSequenceSystemShould.TextureMoveSequenceUpdatesMaterial
  • [playmode] 2.3s DCL.SDKComponents.Tween.Tests.TweenUpdaterSystemShould.MoveContinuousMovesAndCompletesAfterDuration
  • [playmode] 2.3s DCL.SDKComponents.Tween.Tests.TweenSequenceSystemShould.TweenSequenceWithoutLoopCompletesOnce
  • [playmode] 2.3s DCL.SDKComponents.Tween.Tests.TweenUpdaterSystemShould.RotateContinuousCompletesAfterDuration
  • [playmode] 2.3s DCL.SDKComponents.Tween.Tests.TweenUpdaterSystemShould.TextureMoveContinuousOffsetCompletesAndUpdatesMaterial
  • [playmode] 1.6s DCL.SDKComponents.Tween.Tests.TweenSequenceSystemShould.TweenSequenceWithMoveRotateScaleWithOmittedScale_ResolvesScaleFromCurrentTransform
  • [playmode] 1.6s DCL.SDKComponents.Tween.Tests.TweenUpdaterSystemShould.RotateContinuousPositiveAndNegativeYDirectionsAreOpposite
  • [playmode] 1.6s DCL.SDKComponents.Tween.Tests.TweenUpdaterSystemShould.RotateContinuousAroundXAxisRotatesAroundXNotZ

Full report: run summary · results + editor logs: editmode · playmode

Performance

🏁 Bare-metal benchmark finished — run #34153790084.

Full report

PR #10024, run #34153790084

Overall: ✅ no significant changes

Builds: Windows change, Windows baseline, macOS change, macOS baseline

How to read this table
  • Each build is measured 3 times, interleaved with the other build (change, baseline, change, baseline, ...) in the same session, so both see the same world content and machine state. The values are the median, and (min–max) is the lowest and highest of those runs.
  • Δ is Change minus Baseline (a negative Δ means Change is faster).
  • 🟢 faster / 🔴 slower — a difference that passed every check: the runs are fully separated (every run of one build faster than every run of the other), and the median difference is at least 3% and at least 0.5 ms.
  • ⚪ within noise — the builds' runs overlap, or the difference is tiny; it cannot be told apart from random variation. Treat it as no change.
  • — informational — the 0.1% worst metrics average only the few worst frames of a run, so a single OS hiccup swings them by a lot; they are shown for context and never earn a verdict.
  • ⚠️ no verdict — the two builds' sessions were not comparable (very different sample counts, or too few usable runs), so no conclusion is drawn from them.
  • Exceptions per run — the average number of exceptions in a run's log, not counting teardown ones logged while the app quits. Flagged only on a difference of at least 2 per run and 1.5× the other build; exception kinds the baseline never threw are called out under the table. The Exception breakdown groups all of them by the explorer's report category and exception type (as totals across the runs).
  • A run that logged unusually many exceptions (at least 10 and 5× the median of its build's runs — e.g. a service was down during it) is excluded from all numbers and called out under the table.
  • The Overall line at the top only reacts to a metric that moved on two or more machines, or by 10% or more on one — a single modest 🟢/🔴 cell can still be a statistical fluke.

⚠️ Could not gather the results of apple-m1: no change_build run CSVs were found

Intel Core i5

Metric Baseline Change Δ Result
Samples 5415 (×3) 5522 (×3)
CPU average 16.6 ms (16.5–17.2) 16.3 ms (14.5–17.3) -0.3 ms ⚪ within noise
CPU 1% worst 39.9 ms (35.8–40.8) 37.0 ms (35.2–42.6) -2.9 ms ⚪ within noise
CPU 0.1% worst 100.3 ms (90.6–109.4) 98.9 ms (98.0–102.1) -1.3 ms — informational
GPU average 11.2 ms (11.2–11.6) 10.9 ms (9.0–11.8) -0.3 ms ⚪ within noise
GPU 1% worst 24.8 ms (23.4–26.1) 24.0 ms (20.9–25.5) -0.8 ms ⚪ within noise
GPU 0.1% worst 38.3 ms (34.8–43.7) 37.5 ms (30.2–42.4) -0.8 ms — informational
Exceptions per run 0 0 0 ⚪ no significant change

Automation

On demand — comment /visual-tests on this PR to run the visual regression suite against its build.

@decentraland-bot decentraland-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review — feat: add cinematic goto departure and landing animations


STEP 2 — Root-cause check

PASS. This is a new feature, not a bug fix. The PR adds cinematic departure and landing animations to the /goto chat command. No symptom-masking or workaround detected.


STEP 3 — Design & integration

PASS.

OWNER SEARCH — GotoTeleportAnimationSystem:

  • The animation wraps around the existing teleport flow (ChatTeleporterRealmNavigatorTeleportControllerPlayerTeleportIntent ECS component).
  • Existing owners searched: TeleportCharacterSystem (Explorer/Assets/DCL/Character/CharacterMotion/Systems/TeleportCharacterSystem.cs), TeleportPositionCalculationSystem (Explorer/Assets/DCL/Character/CharacterMotion/Systems/TeleportPositionCalculationSystem.cs), InterpolateCharacterSystem. These handle position changes, not cinematic transitions.
  • No existing lifecycle owner can host this work — the animation is a new presentation layer that coordinates departure visuals → teleport → landing visuals. A new system is justified.

OWNER SEARCH — GotoTeleportAnimation (coordination object):

  • Shared between async command (GoToChatCommand.ExecuteCommandAsync) and ECS system (GotoTeleportAnimationSystem.Update). Both run on the Unity main thread — no threading concern.
  • The completion-source coordination pattern (command awaits, system resolves) is a known pattern in the codebase.

OWNER SEARCH — GotoTeleportState (ECS component):

  • Stored on the player entity via World.Add(player, new GotoTeleportState(...)). Reference type is justified because it holds Unity object references (AvatarBase, AnimationClip) and a GotoTeleportTrails with GameObjects.

TEARDOWN / CONSUMPTION TRACE:

Opener Mirror Location
new UniTaskCompletionSource() (Departure) TrySetResult() / TrySetCanceled() in Finish GotoTeleportAnimation.cs:23→51, System.Update, GotoTeleportAnimation.Finish
new UniTaskCompletionSource() (Arrival) TrySetResult() / TrySetCanceled() in Finish GotoTeleportAnimation.cs:42→51, System.Update
AddCameraInputLock() RemoveCameraInputLock() in Restore System.cs:167→188
BlockInput(kind) UnblockInput(kind) in Restore System.cs:169→191
World.Add(player, StopCharacterMotion) World.Remove<StopCharacterMotion> in Restore (if owned) System.cs:146→194
new GameObject("Goto teleport trails") SafeDestroy(root) in Dispose GotoTeleportTrails.cs:21→47
new Material(shader) SafeDestroy(material) in Dispose GotoTeleportTrails.cs:22→48
Shader.SetGlobalVector(EFFECT_ID, ...) Reset to Vector4.zero in Restore + OnDispose System.cs:55,175

All openers have matching teardown. ✅

Structural change safety (CLAUDE.md §5):

  • Begin(): structural changes (World.Add<StopCharacterMotion>, AddOrGet<CharacterEmoteIntent>) on player happen before ref CameraComponent on camera entity — different entities, safe. ✅
  • Restore(): GotoTeleportEmote.Restore() does Remove<CharacterEmoteIntent> on player after writing through ref CharacterEmoteComponent (write completes before the Remove). Then ref CameraComponent / ref InputMapComponent on different entities. Final Remove<StopCharacterMotion> on player is last. ✅

STEP 4 — Member audit

GotoTeleportAnimation public API:

  • IsRequested — 3 consumers (BeginAsync, Finish, System.Update). Guards concurrent teleports. ✅
  • CancellationToken — 2 consumers (BeginAsync, System.Update). Lets system check cancellation. ✅
  • Departure / Arrival — 4+ consumers each. The completion sources the system resolves. Required by the coordination protocol. ✅
  • BeginAsync / ArriveAsync / Finish — the command-side API. Each has 1-3 consumers. ✅

No single-use intermediates, no absent-≠-false conflation, no redundant guards.


STEP 5 — Line-level findings

All findings are P2 (minor). See inline comments for suggestions.

# Sev File Description
1 P2 GotoTeleportTrails.cs:60 material.SetFloat("_Intensity", ...) uses string lookup per frame — cache PropertyToID
2 P2 GotoTeleportTrails.cs:32–34 Inline color/width literals should be named constants (code-style-guidelines § constants first)
3 P2 GotoTeleportPresentation.cs:30,85 Camera framing magic numbers should be named constants
4 P2 GotoTeleportAnimationSystem.cs:56 Resources.Load<Shader> result not null-checked — null shader → broken material

Security review: No issues. No secrets committed, no user-controlled input reaches asset loading (hardcoded shader name), global shader properties are gated by avatar buffer index (_lastAvatarVertCount == _DCLTeleportAvatar), input blocking is ref-counted and restored in all code paths (including exceptions and disposal).


STEP 6 — Complexity

COMPLEX — New ECS system with component lifecycle, shader pipeline integration across both avatar shader families (Toon + Facial Features), camera/input control, async coordination via completion sources, and assembly boundary changes across 37 files.


STEP 7 — QA assessment

QA_REQUIRED: YES — Changes affect user-visible behavior (new teleport animations), modify runtime code (avatar shaders, camera, input handling), and touch the rendering pipeline.


STEP 8 — Non-blocking warnings

None. Main scene not modified.


STEP 9 — Verdict

REVIEW_RESULT: PASS ✅
COMPLEXITY: COMPLEX
COMPLEXITY_REASON: New ECS system with component lifecycle, shader pipeline integration, camera/input control, async coordination, and assembly boundary changes across 37 files.
QA_REQUIRED: YES


Reviewed by Jarvis 🤖 · Requested by decentraland-bot via GitHub

root.transform.position = origin;
float launch = Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(1.35f, 2.6f, elapsed));
float alpha = Mathf.SmoothStep(0f, 1f, elapsed / 0.6f) * (1f - Mathf.InverseLerp(2.5f, 2.8f, elapsed));
material.SetFloat("_Intensity", alpha);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Per-frame string lookup. material.SetFloat("_Intensity", ...) performs a string-to-ID lookup every frame during the animation. Cache the property ID in a static field, matching the pattern already used in GotoTeleportPresentation (CLAUDE.md §4: allocation-free Update).

Add a static field at the top of the class and use it here:

Suggested change
material.SetFloat("_Intensity", alpha);
material.SetFloat(INTENSITY_ID, alpha);

Also add this field alongside the existing constants:

private static readonly int INTENSITY_ID = Shader.PropertyToID("_Intensity");

Comment on lines +32 to +34
line.widthMultiplier = i % 3 == 0 ? 0.045f : 0.018f;
line.startColor = new Color(0.05f, 0.6f, 1f, 0f);
line.endColor = new Color(0.3f, 1f, 1f, 1f);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Inline color/width literals. These visual constants should be named constants declared at the top of the type (code-style-guidelines § member ordering: consts first).

Suggested change
line.widthMultiplier = i % 3 == 0 ? 0.045f : 0.018f;
line.startColor = new Color(0.05f, 0.6f, 1f, 0f);
line.endColor = new Color(0.3f, 1f, 1f, 1f);
line.widthMultiplier = i % 3 == 0 ? WIDE_WIDTH : NARROW_WIDTH;
line.startColor = START_COLOR;
line.endColor = END_COLOR;

Add these constants at the top of the class:

private const float WIDE_WIDTH = 0.045f;
private const float NARROW_WIDTH = 0.018f;
private static readonly Color START_COLOR = new (0.05f, 0.6f, 1f, 0f);
private static readonly Color END_COLOR = new (0.3f, 1f, 1f, 1f);

Vector3 backward = state.CameraRotation * Vector3.back;
backward.y = 0f;
if (backward.sqrMagnitude < 0.01f) backward = Vector3.back;
return state.Origin + (Vector3.up * 1.2f) + (backward.normalized * 4.5f);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Camera framing magic numbers. The framing offset values (1.2f up, 4.5f back) define the cinematic camera setup and should be named constants at the top of the class (code-style-guidelines § constants first). Same for the -82f sky-look angle at lines 30 and 48.

Suggested change
return state.Origin + (Vector3.up * 1.2f) + (backward.normalized * 4.5f);
return state.Origin + (Vector3.up * FRAMING_HEIGHT) + (backward.normalized * FRAMING_DISTANCE);

Add these constants alongside EFFECT_TIMELINE_DURATION:

private const float FRAMING_HEIGHT = 1.2f;
private const float FRAMING_DISTANCE = 4.5f;
private const float SKY_LOOK_PITCH = -82f;

And update lines 30 and 48: Quaternion.Euler(SKY_LOOK_PITCH, ...)

Comment on lines +56 to +57
Shader shader = Resources.Load<Shader>("GotoTeleportTrail");
World.Add(player, new GotoTeleportState(new GotoTeleportTrails(shader)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Missing null-check on Resources.Load. If the shader fails to load (e.g. missing from the Resources folder after a refactor), shader is null and new Material(null) produces a broken material with a Unity error. Add a defensive check.

Suggested change
Shader shader = Resources.Load<Shader>("GotoTeleportTrail");
World.Add(player, new GotoTeleportState(new GotoTeleportTrails(shader)));
Shader shader = Resources.Load<Shader>("GotoTeleportTrail");
if (shader == null) { ReportHub.LogError(ReportCategory.CHAT, "GotoTeleportTrail shader not found in Resources"); return; }
World.Add(player, new GotoTeleportState(new GotoTeleportTrails(shader)));

Note: You'll need to add using DCL.Diagnostics; for ReportHub and choose an appropriate ReportCategoryCHAT or a new category if one fits better.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

badge

Avatar Preview Renderer — Vercel Preview is ready!

Field Value
Preview https://unity-explorer-8h36ddn90-decentraland1.vercel.app
Commit 6497285518ab511e50f87a41bb2f6ef6d213af92
Logs https://github.com/decentraland/unity-explorer/actions/runs/34147400051

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants