From dcb1fc42a68fba94d589cc45636cd773d286681c Mon Sep 17 00:00:00 2001 From: MelekTaus Moribundus Date: Mon, 22 Jun 2026 15:13:46 +0200 Subject: [PATCH] Add game mode manager with player-control and paint modes Introduce an extensible game mode system toggled with Tab: - Player Control: WASD movement with always-on mouse camera orbit - Paint: player frozen, left-click paints, right-click holds to orbit Add GameMode base class, GameModeManager, and the two concrete modes. Refactor controlled scripts to expose clean state flags instead of handling input themselves: ThirdPersonOrbitInput.LookMode, PlayerCharacterController.ControlEnabled, MeshPainter.PaintingEnabled. Also fix camera zoom: remove the obsolete /120 wheel normalization so one scroll notch maps to one zoomSpeed step under the new Input System. Co-Authored-By: Claude Opus 4.8 --- Assets/Scenes/SampleScene.unity | 57 +++++++++- Assets/Scripts/GameMode.cs | 29 +++++ Assets/Scripts/GameMode.cs.meta | 2 + Assets/Scripts/GameModeManager.cs | 117 ++++++++++++++++++++ Assets/Scripts/GameModeManager.cs.meta | 2 + Assets/Scripts/MeshPainter.cs | 13 +++ Assets/Scripts/PaintMode.cs | 21 ++++ Assets/Scripts/PaintMode.cs.meta | 2 + Assets/Scripts/PlayerCharacterController.cs | 36 ++++-- Assets/Scripts/PlayerControlMode.cs | 20 ++++ Assets/Scripts/PlayerControlMode.cs.meta | 2 + Assets/Scripts/ThirdPersonOrbitInput.cs | 112 ++++++++++++------- 12 files changed, 358 insertions(+), 55 deletions(-) create mode 100644 Assets/Scripts/GameMode.cs create mode 100644 Assets/Scripts/GameMode.cs.meta create mode 100644 Assets/Scripts/GameModeManager.cs create mode 100644 Assets/Scripts/GameModeManager.cs.meta create mode 100644 Assets/Scripts/PaintMode.cs create mode 100644 Assets/Scripts/PaintMode.cs.meta create mode 100644 Assets/Scripts/PlayerControlMode.cs create mode 100644 Assets/Scripts/PlayerControlMode.cs.meta diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index c52489c..c5c1312 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -152,16 +152,15 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: df129be8fadab79498bd429a59bb98e4, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::ThirdPersonOrbitInput + lookMode: 0 horizontalSensitivity: 0.2 verticalSensitivity: 0.15 invertY: 0 - toggleKey: 3 - startInLookMode: 1 enableZoom: 1 zoomSpeed: 0.5 minRadius: 1.5 maxRadius: 8 - lockCursorInLookMode: 1 + manageCursor: 1 --- !u!114 &172071432 MonoBehaviour: m_ObjectHideFlags: 0 @@ -779,7 +778,7 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::MeshPainter useGpu: 1 brushColor: {r: 0, g: 0, b: 0, a: 1} - brushSize: 2 + brushSize: 10 textureSize: 1024 baseColor: {r: 0.8, g: 0.8, b: 0.8, a: 1} paddingTexels: 2 @@ -1205,6 +1204,55 @@ MonoBehaviour: slideAcceleration: 20 maxSlideSpeed: 8 slideControl: 0.3 +--- !u!1 &1516578026 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1516578028} + - component: {fileID: 1516578027} + m_Layer: 0 + m_Name: GameManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1516578027 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1516578026} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 730466b26610d95498d22ab748d855a7, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::GameModeManager + player: {fileID: 1141039642} + cameraInput: {fileID: 172071431} + painter: {fileID: 682237521} + switchKey: 3 + startModeIndex: 0 +--- !u!4 &1516578028 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1516578026} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1774267801 GameObject: m_ObjectHideFlags: 0 @@ -1302,3 +1350,4 @@ SceneRoots: - {fileID: 963088882} - {fileID: 200502122} - {fileID: 172071436} + - {fileID: 1516578028} diff --git a/Assets/Scripts/GameMode.cs b/Assets/Scripts/GameMode.cs new file mode 100644 index 0000000..ae8bf04 --- /dev/null +++ b/Assets/Scripts/GameMode.cs @@ -0,0 +1,29 @@ +/// +/// Base class for a high-level game mode (state) handled by +/// . A mode configures the relevant scene systems +/// when it becomes active (), can clean up when it is left +/// () and may run per-frame logic in . +/// +/// Modes are plain C# classes (not MonoBehaviours). To add a new mode, derive +/// from this class and register an instance in GameModeManager.BuildModes(). +/// +public abstract class GameMode +{ + /// The owning manager, providing access to the controlled systems. + protected GameModeManager Manager { get; private set; } + + /// Human-readable name (shown in logs / debug UI). + public abstract string DisplayName { get; } + + /// Wires the mode to its manager. Called once during registration. + internal void Bind(GameModeManager manager) => Manager = manager; + + /// Called once when this mode becomes the active mode. + public virtual void Enter() { } + + /// Called once when this mode stops being the active mode. + public virtual void Exit() { } + + /// Called every frame while this mode is the active mode. + public virtual void Tick() { } +} diff --git a/Assets/Scripts/GameMode.cs.meta b/Assets/Scripts/GameMode.cs.meta new file mode 100644 index 0000000..81ded8a --- /dev/null +++ b/Assets/Scripts/GameMode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1dbd23638c056cc4db7ce7c0ffffe53c \ No newline at end of file diff --git a/Assets/Scripts/GameModeManager.cs b/Assets/Scripts/GameModeManager.cs new file mode 100644 index 0000000..35cb003 --- /dev/null +++ b/Assets/Scripts/GameModeManager.cs @@ -0,0 +1,117 @@ +using System.Collections.Generic; +using UnityEngine; +#if ENABLE_INPUT_SYSTEM +using UnityEngine.InputSystem; +#endif + +/// +/// Central state manager that switches the game between high-level modes. +/// Currently two modes exist: +/// 0. Player Control - WASD movement, camera orbits freely with the mouse. +/// 1. Paint - player stands still, left mouse paints the mesh, the +/// camera orbits only while the right mouse button is held. +/// +/// Pressing the switch key (Tab) cycles to the next registered mode, so adding +/// more modes later automatically extends the cycle. +/// +/// The manager owns references to the systems a mode may toggle (player +/// controller, orbit camera input, mesh painter) and exposes them to the modes. +/// Modes are plain C# classes derived from ; register +/// additional ones in . +/// +/// Attach this to a single, persistent GameObject in the scene. +/// +[DisallowMultipleComponent] +public class GameModeManager : MonoBehaviour +{ + [Header("Controlled Systems")] + [Tooltip("Player movement controller. Empty = found automatically in the scene.")] + public PlayerCharacterController player; + + [Tooltip("Third-person orbit camera input. Empty = found automatically in the scene.")] + public ThirdPersonOrbitInput cameraInput; + + [Tooltip("Mesh painter. Empty = found automatically in the scene.")] + public MeshPainter painter; + + [Header("Switching")] + [Tooltip("Key that cycles to the next mode.")] +#if ENABLE_INPUT_SYSTEM + public Key switchKey = Key.Tab; +#else + public KeyCode switchKey = KeyCode.Tab; +#endif + + [Tooltip("Index of the mode that is active at startup (0 = Player Control).")] + public int startModeIndex = 0; + + // --- accessors used by the modes --- + public PlayerCharacterController Player => player; + public ThirdPersonOrbitInput CameraInput => cameraInput; + public MeshPainter Painter => painter; + + private readonly List _modes = new List(); + private int _activeIndex = -1; + + /// The currently active mode (null before startup). + public GameMode ActiveMode => + (_activeIndex >= 0 && _activeIndex < _modes.Count) ? _modes[_activeIndex] : null; + + private void Awake() + { + // Auto-resolve references so the manager works with minimal wiring. + if (player == null) player = FindFirstObjectByType(); + if (cameraInput == null) cameraInput = FindFirstObjectByType(); + if (painter == null) painter = FindFirstObjectByType(); + + BuildModes(); + } + + private void Start() + { + if (_modes.Count > 0) + SwitchTo(Mathf.Clamp(startModeIndex, 0, _modes.Count - 1)); + } + + /// Registers all available modes. Add new modes here. + private void BuildModes() + { + _modes.Clear(); + _modes.Add(new PlayerControlMode()); + _modes.Add(new PaintMode()); + + foreach (var m in _modes) + m.Bind(this); + } + + private void Update() + { + if (_modes.Count == 0) return; + + if (WasSwitchPressed()) + SwitchTo((_activeIndex + 1) % _modes.Count); + + ActiveMode?.Tick(); + } + + /// Activates the mode at (Exit old, Enter new). + public void SwitchTo(int index) + { + if (index < 0 || index >= _modes.Count || index == _activeIndex) return; + + ActiveMode?.Exit(); + _activeIndex = index; + ActiveMode.Enter(); + + Debug.Log($"[GameModeManager] Mode -> {ActiveMode.DisplayName}", this); + } + + private bool WasSwitchPressed() + { +#if ENABLE_INPUT_SYSTEM + return Keyboard.current != null && Keyboard.current[switchKey].wasPressedThisFrame; +#else + return Input.GetKeyDown(switchKey); +#endif + } +} diff --git a/Assets/Scripts/GameModeManager.cs.meta b/Assets/Scripts/GameModeManager.cs.meta new file mode 100644 index 0000000..0e59969 --- /dev/null +++ b/Assets/Scripts/GameModeManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 730466b26610d95498d22ab748d855a7 \ No newline at end of file diff --git a/Assets/Scripts/MeshPainter.cs b/Assets/Scripts/MeshPainter.cs index 2c24ae2..71eb500 100644 --- a/Assets/Scripts/MeshPainter.cs +++ b/Assets/Scripts/MeshPainter.cs @@ -29,6 +29,12 @@ using UnityEngine.InputSystem; [DisallowMultipleComponent] public class MeshPainter : MonoBehaviour { + /// + /// When false, mouse painting is ignored (the canvas/material stays as-is). + /// Toggled by the GameModeManager (painting is only enabled in paint mode). + /// + public bool PaintingEnabled { get; set; } = true; + [Header("Backend")] [Tooltip("ON = GPU painting (shader/RenderTexture, fast). OFF = CPU painting. " + "Can be toggled live in Play mode - the painted content is carried over.")] @@ -429,6 +435,13 @@ public class MeshPainter : MonoBehaviour // Live backend switching. if (useGpu != _gpuActive) SwitchBackend(useGpu); + // Painting disabled by the game mode (e.g. player-control mode): do nothing. + if (!PaintingEnabled) + { + _hasLastScreen = false; + return; + } + if (updateColliderEveryFrame && _skinned != null) { BakeColliderMesh(); diff --git a/Assets/Scripts/PaintMode.cs b/Assets/Scripts/PaintMode.cs new file mode 100644 index 0000000..2b1dfdf --- /dev/null +++ b/Assets/Scripts/PaintMode.cs @@ -0,0 +1,21 @@ +/// +/// Mode 2: the player stands still and the mesh is painted with the left mouse +/// button. The camera orbits only while the right mouse button is held, so +/// dragging with the left button paints instead of rotating the view. +/// +public class PaintMode : GameMode +{ + public override string DisplayName => "Paint"; + + public override void Enter() + { + if (Manager.Player != null) + Manager.Player.ControlEnabled = false; + + if (Manager.CameraInput != null) + Manager.CameraInput.LookMode = ThirdPersonOrbitInput.OrbitLookMode.HoldRightMouseButton; + + if (Manager.Painter != null) + Manager.Painter.PaintingEnabled = true; + } +} diff --git a/Assets/Scripts/PaintMode.cs.meta b/Assets/Scripts/PaintMode.cs.meta new file mode 100644 index 0000000..dba9deb --- /dev/null +++ b/Assets/Scripts/PaintMode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a4815d31a23d2b14fb3fd22d9e13630d \ No newline at end of file diff --git a/Assets/Scripts/PlayerCharacterController.cs b/Assets/Scripts/PlayerCharacterController.cs index 7e1c07b..8c3522b 100644 --- a/Assets/Scripts/PlayerCharacterController.cs +++ b/Assets/Scripts/PlayerCharacterController.cs @@ -71,6 +71,13 @@ public class PlayerCharacterController : MonoBehaviour [Range(0f, 1f)] public float slideControl = 0.3f; + /// + /// When false, movement/jump input is ignored (the player stands still), but + /// gravity and ground contact keep running so the character stays grounded. + /// Toggled by the GameModeManager (e.g. disabled in paint mode). + /// + public bool ControlEnabled { get; set; } = true; + private CharacterController _controller; private float _verticalVelocity; @@ -92,21 +99,26 @@ public class PlayerCharacterController : MonoBehaviour float h = 0f, v = 0f; bool jumpPressed = false; -#if ENABLE_INPUT_SYSTEM - var keyboard = Keyboard.current; - if (keyboard != null) + // Skip input entirely when control is disabled (e.g. paint mode); gravity + // and grounding below still run so the player stays planted on the ground. + if (ControlEnabled) { - if (keyboard.aKey.isPressed) h -= 1f; - if (keyboard.dKey.isPressed) h += 1f; - if (keyboard.sKey.isPressed) v -= 1f; - if (keyboard.wKey.isPressed) v += 1f; - jumpPressed = keyboard.spaceKey.wasPressedThisFrame; - } +#if ENABLE_INPUT_SYSTEM + var keyboard = Keyboard.current; + if (keyboard != null) + { + if (keyboard.aKey.isPressed) h -= 1f; + if (keyboard.dKey.isPressed) h += 1f; + if (keyboard.sKey.isPressed) v -= 1f; + if (keyboard.wKey.isPressed) v += 1f; + jumpPressed = keyboard.spaceKey.wasPressedThisFrame; + } #else - h = Input.GetAxisRaw("Horizontal"); - v = Input.GetAxisRaw("Vertical"); - jumpPressed = Input.GetButtonDown("Jump"); + h = Input.GetAxisRaw("Horizontal"); + v = Input.GetAxisRaw("Vertical"); + jumpPressed = Input.GetButtonDown("Jump"); #endif + } // --- Build the horizontal move direction ---------------------------- Vector3 inputDir = new Vector3(h, 0f, v); diff --git a/Assets/Scripts/PlayerControlMode.cs b/Assets/Scripts/PlayerControlMode.cs new file mode 100644 index 0000000..2998da4 --- /dev/null +++ b/Assets/Scripts/PlayerControlMode.cs @@ -0,0 +1,20 @@ +/// +/// Mode 1: the player walks with WASD/Space and the camera orbits freely with +/// the mouse (cursor locked/hidden). Mesh painting is disabled. +/// +public class PlayerControlMode : GameMode +{ + public override string DisplayName => "Player Control"; + + public override void Enter() + { + if (Manager.Player != null) + Manager.Player.ControlEnabled = true; + + if (Manager.CameraInput != null) + Manager.CameraInput.LookMode = ThirdPersonOrbitInput.OrbitLookMode.AlwaysActive; + + if (Manager.Painter != null) + Manager.Painter.PaintingEnabled = false; + } +} diff --git a/Assets/Scripts/PlayerControlMode.cs.meta b/Assets/Scripts/PlayerControlMode.cs.meta new file mode 100644 index 0000000..4dc72bc --- /dev/null +++ b/Assets/Scripts/PlayerControlMode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c2f4d0884ba29d849afcaf6121027023 \ No newline at end of file diff --git a/Assets/Scripts/ThirdPersonOrbitInput.cs b/Assets/Scripts/ThirdPersonOrbitInput.cs index 158983d..188773f 100644 --- a/Assets/Scripts/ThirdPersonOrbitInput.cs +++ b/Assets/Scripts/ThirdPersonOrbitInput.cs @@ -8,10 +8,14 @@ using UnityEngine.InputSystem; /// Feeds mouse input into a so the camera /// orbits around the follow target like a classic third-person camera. /// -/// Rotation is always on by default and the cursor is locked/hidden while looking. -/// Pressing the toggle key (Tab) unlocks the cursor and disables camera rotation, -/// so you can freely use the mouse (e.g. paint the mesh with the left button or -/// click UI). Pressing it again re-locks the cursor and re-enables rotation. +/// How the orbit is driven is controlled by : +/// * AlwaysActive - rotates with every mouse move, cursor locked/hidden. +/// * HoldRightMouseButton - rotates only while the right mouse button is held; +/// the cursor is free otherwise (so the left button can paint). +/// * Disabled - no rotation, cursor free. +/// +/// The look mode is normally set by the GameModeManager when switching modes, +/// but can also be configured directly in the Inspector for standalone use. /// /// Horizontal/vertical mouse movement drives the orbital axes (yaw/pitch); the /// mouse wheel zooms by changing the orbit radius. The script reads the new Input @@ -25,6 +29,24 @@ using UnityEngine.InputSystem; [DisallowMultipleComponent] public class ThirdPersonOrbitInput : MonoBehaviour { + /// How the camera orbit is driven. + public enum OrbitLookMode + { + /// Rotate with every mouse move; cursor locked/hidden. + AlwaysActive, + /// Rotate only while the right mouse button is held; cursor free otherwise. + HoldRightMouseButton, + /// No rotation; cursor free. + Disabled + } + + [Header("Look Mode")] + [Tooltip("How the camera orbit is driven. Usually set by GameModeManager:\n" + + "AlwaysActive = rotates with every mouse move (cursor locked).\n" + + "HoldRightMouseButton = rotates only while RMB is held (cursor free otherwise).\n" + + "Disabled = no rotation.")] + [SerializeField] private OrbitLookMode lookMode = OrbitLookMode.AlwaysActive; + [Header("Rotation")] [Tooltip("Horizontal look sensitivity in degrees of yaw per pixel of mouse movement.")] public float horizontalSensitivity = 0.2f; @@ -35,23 +57,11 @@ public class ThirdPersonOrbitInput : MonoBehaviour [Tooltip("If ON, moving the mouse up tilts the camera down (inverted pitch).")] public bool invertY = false; - [Header("Toggle")] - [Tooltip("Key that toggles between look mode (cursor locked, camera rotates) and " + - "free mode (cursor unlocked, camera rotation disabled).")] -#if ENABLE_INPUT_SYSTEM - public Key toggleKey = Key.Tab; -#else - public KeyCode toggleKey = KeyCode.Tab; -#endif - - [Tooltip("If ON, the camera starts in look mode (rotation enabled, cursor locked).")] - public bool startInLookMode = true; - [Header("Zoom")] [Tooltip("If ON, the mouse wheel zooms the camera in and out (changes orbit radius).")] public bool enableZoom = true; - [Tooltip("How much the orbit radius changes per mouse wheel notch.")] + [Tooltip("How many world units the orbit radius changes per mouse wheel notch.")] public float zoomSpeed = 0.5f; [Tooltip("Closest the camera can zoom in (orbit radius in units).")] @@ -61,14 +71,21 @@ public class ThirdPersonOrbitInput : MonoBehaviour public float maxRadius = 8f; [Header("Cursor")] - [Tooltip("If ON, the cursor is locked and hidden while in look mode (rotation " + - "enabled), and freed in free mode so you can use the mouse normally.")] - public bool lockCursorInLookMode = true; + [Tooltip("If ON, the cursor is locked and hidden while the camera is actively " + + "rotating, and freed otherwise so you can use the mouse normally.")] + public bool manageCursor = true; CinemachineOrbitalFollow _orbit; - // True = look mode (cursor locked, camera rotates). False = free mode. - bool _rotationEnabled = true; + /// + /// How the camera orbit is currently driven. Setting this also updates the + /// cursor state immediately (e.g. when GameModeManager switches modes). + /// + public OrbitLookMode LookMode + { + get => lookMode; + set { lookMode = value; ApplyCursorState(); } + } void Awake() { @@ -79,7 +96,6 @@ public class ThirdPersonOrbitInput : MonoBehaviour void OnEnable() { - _rotationEnabled = startInLookMode; ApplyCursorState(); } @@ -94,14 +110,14 @@ public class ThirdPersonOrbitInput : MonoBehaviour if (_orbit == null) return; - // Tab toggles between look mode and free mode. - if (WasTogglePressed()) - { - _rotationEnabled = !_rotationEnabled; - ApplyCursorState(); - } + bool rotating = ShouldRotate(); - if (_rotationEnabled) + // Keep the cursor in sync every frame: in HoldRightMouseButton mode the + // lock state follows the right mouse button. + if (manageCursor) + SetCursorLocked(rotating); + + if (rotating) { Vector2 delta = ReadMouseDelta(); if (delta.sqrMagnitude > 0f) @@ -116,14 +132,31 @@ public class ThirdPersonOrbitInput : MonoBehaviour { float scroll = ReadScroll(); if (!Mathf.Approximately(scroll, 0f)) - _orbit.Radius = Mathf.Clamp(_orbit.Radius - scroll * zoomSpeed, minRadius, maxRadius); + { + // One wheel notch = one zoom step. Use only the sign so the step + // size is exactly zoomSpeed regardless of the platform's raw wheel + // magnitude (the new Input System reports ~1 per notch, the legacy + // Windows path used ~120). Scrolling up (positive) zooms in. + _orbit.Radius = Mathf.Clamp(_orbit.Radius - Mathf.Sign(scroll) * zoomSpeed, minRadius, maxRadius); + } + } + } + + /// Whether the camera should orbit this frame, based on the look mode. + bool ShouldRotate() + { + switch (lookMode) + { + case OrbitLookMode.AlwaysActive: return true; + case OrbitLookMode.HoldRightMouseButton: return IsRightMouseHeld(); + default: return false; } } void ApplyCursorState() { - if (lockCursorInLookMode) - SetCursorLocked(_rotationEnabled); + if (manageCursor) + SetCursorLocked(ShouldRotate()); } /// @@ -155,12 +188,12 @@ public class ThirdPersonOrbitInput : MonoBehaviour Cursor.visible = !locked; } - bool WasTogglePressed() + bool IsRightMouseHeld() { #if ENABLE_INPUT_SYSTEM - return Keyboard.current != null && Keyboard.current[toggleKey].wasPressedThisFrame; + return Mouse.current != null && Mouse.current.rightButton.isPressed; #else - return Input.GetKeyDown(toggleKey); + return Input.GetMouseButton(1); #endif } @@ -177,10 +210,11 @@ public class ThirdPersonOrbitInput : MonoBehaviour float ReadScroll() { #if ENABLE_INPUT_SYSTEM - // Scroll y is reported in steps of ~120 per notch; normalize to ~1 per notch. - return Mouse.current != null ? Mouse.current.scroll.ReadValue().y / 120f : 0f; + // Raw wheel delta. Only its sign is used in Update (one notch = one zoom + // step), so the platform-dependent magnitude does not matter here. + return Mouse.current != null ? Mouse.current.scroll.ReadValue().y : 0f; #else - return Input.GetAxis("Mouse ScrollWheel") * 10f; + return Input.GetAxis("Mouse ScrollWheel"); #endif } }