Skip to content

Commit 97f438b

Browse files
eordanoclaude
andcommitted
fix: fall back to raw pointer device when Camera action map is disabled
Opening the chat puts it into the focused state, which disables the entire `Camera` input action map (intended to stop camera-look/zoom while typing). `PrimaryPointerInfoSystem` reads the raw pointer position through that same map's `Point` action; a disabled `InputAction` returns `default(Vector2)`, so the client writes `PBPrimaryPointerInfo.ScreenCoordinates = (0,0)` to the scene on every tick while chat is focused. The fishing-pond scene normalizes that value against the canvas size and renders the tooltip at `left:14, bottom:10` — exactly the bottom-left corner, where the chat input sits. Fixes #9496 Includes a regression test that fails without this fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 68731a1 commit 97f438b

3 files changed

Lines changed: 133 additions & 1 deletion

File tree

Explorer/Assets/DCL/SDKComponents/PrimaryPointerInfo/Systems/PrimaryPointerInfoSystem.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,12 @@ protected override void Update(float t)
6565

6666
private void UpdatePointerInfo()
6767
{
68-
Vector2 rawPosition = inputPoint.ReadValue<Vector2>();
68+
// The Camera action map is disabled while explorer UI holds input focus (chat, passport,
69+
// explore panel), and a disabled action reads default(Vector2); the scene-facing pointer
70+
// feed must keep tracking the device, so fall back to it (or the last known position).
71+
Vector2 rawPosition = inputPoint.enabled
72+
? inputPoint.ReadValue<Vector2>()
73+
: UnityEngine.InputSystem.Pointer.current?.position.ReadValue() ?? previousPosition;
6974
CumulativePointerDelta accumulatedDelta = exposedCameraData.AccumulatedPointerDelta;
7075
Vector2 pointerPos;
7176
Vector2 deltaPos;
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
using Arch.Core;
2+
using CRDT;
3+
using CrdtEcsBridge.ECSToCRDTWriter;
4+
using DCL.CharacterCamera;
5+
using DCL.ECSComponents;
6+
using DCL.SDKComponents.PrimaryPointerInfo.Systems;
7+
using NSubstitute;
8+
using NUnit.Framework;
9+
using SceneRunner.Scene;
10+
using System;
11+
using System.Collections.Generic;
12+
using UnityEngine;
13+
using UnityEngine.InputSystem;
14+
using Utility;
15+
using ProtoVector3 = Decentraland.Common.Vector3;
16+
17+
namespace DCL.SDKComponents.PrimaryPointerInfo.Tests
18+
{
19+
// Regression coverage for https://github.com/decentraland/unity-explorer/issues/9496:
20+
// while the explorer chat is focused, ApplyInputMapsSystem disables the whole `Camera`
21+
// action map (ChatInputBlockingService.Block() -> Kind.Camera), and PrimaryPointerInfoSystem
22+
// sources its raw pointer position from `DCLInput.Instance.Camera.Point`, which is part of
23+
// that map. A disabled InputAction.ReadValue<Vector2>() returns default(Vector2), so the
24+
// system used to feed PBPrimaryPointerInfo.ScreenCoordinates = (0,0) to every scene for as
25+
// long as chat stayed focused, pinning scene-side UI (e.g. the Genesis Plaza fishing pond's
26+
// "Toggle Hints" tooltip) to the bottom-left corner instead of the real cursor.
27+
[TestFixture]
28+
public class PrimaryPointerInfoSystemCameraMapDisabledShould : InputTestFixture
29+
{
30+
private const float TOLERANCE = 1e-4f;
31+
32+
private World sceneWorld;
33+
private World globalWorld;
34+
private Mouse mouse;
35+
private GameObject cameraGameObject;
36+
private Camera camera;
37+
private IECSToCRDTWriter ecsToCRDTWriter;
38+
private ISceneStateProvider sceneStateProvider;
39+
private IExposedCameraData exposedCameraData;
40+
private PrimaryPointerInfoSystem system;
41+
private List<(Vector2 pos, Vector2 delta, ProtoVector3 rayDir)> putCalls;
42+
43+
[SetUp]
44+
public void SetUp()
45+
{
46+
base.Setup();
47+
48+
mouse = InputSystem.AddDevice<Mouse>();
49+
DCLInput.Instance.Enable();
50+
51+
sceneWorld = World.Create();
52+
globalWorld = World.Create();
53+
54+
cameraGameObject = new GameObject("PrimaryPointerInfoCameraMapDisabledTestCamera");
55+
camera = cameraGameObject.AddComponent<Camera>();
56+
globalWorld.Create(new CameraComponent(camera));
57+
58+
putCalls = new List<(Vector2 pos, Vector2 delta, ProtoVector3 rayDir)>();
59+
60+
ecsToCRDTWriter = Substitute.For<IECSToCRDTWriter>();
61+
62+
ecsToCRDTWriter.PutMessage(
63+
Arg.Any<Action<PBPrimaryPointerInfo, (Vector2 pos, Vector2 delta, ProtoVector3 rayDir)>>(),
64+
Arg.Any<CRDTEntity>(),
65+
Arg.Do<(Vector2 pos, Vector2 delta, ProtoVector3 rayDir)>(data => putCalls.Add(data)));
66+
67+
sceneStateProvider = Substitute.For<ISceneStateProvider>();
68+
sceneStateProvider.IsCurrent.Returns(true);
69+
70+
exposedCameraData = Substitute.For<IExposedCameraData>();
71+
exposedCameraData.PointerIsLocked.Returns(new CanBeDirty<bool>(false));
72+
exposedCameraData.AccumulatedPointerDelta.Returns(default(CumulativePointerDelta));
73+
74+
system = new PrimaryPointerInfoSystem(sceneWorld, globalWorld, sceneStateProvider, ecsToCRDTWriter, exposedCameraData);
75+
system.Initialize();
76+
77+
// Initialize() performs one PUT; discard it so the test only sees the Update()-driven write.
78+
putCalls.Clear();
79+
}
80+
81+
[TearDown]
82+
public void Cleanup()
83+
{
84+
system.Dispose();
85+
UnityEngine.Object.DestroyImmediate(cameraGameObject);
86+
sceneWorld.Dispose();
87+
globalWorld.Dispose();
88+
}
89+
90+
[Test]
91+
public void NotReportZeroScreenCoordinatesWhenCameraMapDisabledByChatFocus()
92+
{
93+
// Arrange: position the simulated pointer device, then disable the whole `Camera`
94+
// action map exactly as ApplyInputMapsSystem.cs does when chat gains focus
95+
// (DCLInput.Instance.Camera.Disable()) - this puts every action in that map,
96+
// including Point, into the Disabled phase.
97+
var simulatedPosition = new Vector2(456f, 234f);
98+
Set(mouse.position, simulatedPosition);
99+
DCLInput.Instance.Camera.Disable();
100+
101+
// Act
102+
system.Update(0);
103+
104+
// Assert: the scene-facing pointer feed must keep tracking the real cursor instead
105+
// of collapsing to (0,0) - the exact value a disabled InputAction.ReadValue<Vector2>()
106+
// returns, and the value that pins the fishing-pond "Toggle Hints" tooltip to the
107+
// bottom-left corner while chat is focused.
108+
Assert.IsNotEmpty(putCalls);
109+
(Vector2 pos, Vector2 _, ProtoVector3 _) = putCalls[putCalls.Count - 1];
110+
111+
Assert.AreNotEqual(Vector2.zero, pos, "screenCoordinates must not collapse to (0,0) while the Camera map is disabled");
112+
Assert.AreEqual(simulatedPosition.x, pos.x, TOLERANCE);
113+
Assert.AreEqual(simulatedPosition.y, pos.y, TOLERANCE);
114+
}
115+
}
116+
}

Explorer/Assets/DCL/SDKComponents/PrimaryPointerInfo/Tests/PrimaryPointerInfoSystemCameraMapDisabledShould.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)