⭐ Unity 实现UI视差滚动效果(Parallax)鼠标控制、可拓展陀螺仪与脚本控制

发布于:2025-08-03 ⋅ 阅读:(7) ⋅ 点赞:(0)

✨ 效果如下

在许多游戏、APP 或动效页面中,我们常见的一种视觉效果是 视差滚动(Parallax Scrolling):前景、中景、背景在鼠标或设备移动时以不同速率轻微移动,从而营造出一种空间感和深度感。

目前遇到这样一个需求 所以在 Unity 中实现一个支持鼠标控制、陀螺仪控制和脚本控制的 UI 视差脚本,并提供完整源码、注释及使用方法,适合用于启动页、主菜单、信息卡片界面等多种场景。


🧠 实现原理

核心思想是:

  • 每个 UI 图层记录起始位置;

  • 根据输入设备(鼠标或陀螺仪),获取目标参考位置(通常是屏幕坐标归一化到 [0,1]);

  • Lerp 缓动插值方式移动 UI 图层,使其偏移方向和输入方向一致;

  • 每个图层的移动速度和最大移动范围可以独立配置。


📦 脚本结构概述

我们将视差系统封装为一个 MonoBehaviour 脚本,名为 MMParallaxUI,结构清晰、易于扩展:

名称 说明
ParallaxLayer 子类,表示每一层的参数设置(RectTransform、速度、幅度、是否启用等)
Modes 视差控制模式(鼠标 / 陀螺仪 / 脚本)
ParallaxLayers 图层列表,可在 Inspector 中直接配置
AmplitudeMultiplier 所有图层的幅度乘数
SpeedMultiplier 所有图层的速度乘数


🧩 完整源码(含中文注释)

using System;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 用于实现UI视差滚动效果的组件(Parallax)
/// 支持鼠标控制、陀螺仪控制(移动端)、或通过代码控制
/// </summary>
public class MMParallaxUI : MonoBehaviour
{
    /// <summary>
    /// 用于存储每个视差图层的设置
    /// </summary>
    [Serializable]
    public class ParallaxLayer
    {
        [Tooltip("该图层的 RectTransform 组件")]
        public RectTransform Rect;

        [Tooltip("该图层移动的速度")]
        public float Speed = 2f;

        [Tooltip("该图层相对初始位置最大移动距离(幅度)")]
        public float Amplitude = 50f;

        [HideInInspector]
        public Vector2 StartPosition;  // 图层的初始位置(运行时记录)

        [Tooltip("是否启用该图层的视差效果")]
        public bool Active = true;
    }

    /// <summary>
    /// 控制视差输入的模式类型
    /// </summary>
    public enum Modes
    {
        Mouse,      // 使用鼠标位置控制(适用于PC)
        Gyroscope,  // 使用陀螺仪控制(适用于移动设备)
        Script      // 外部脚本通过 SetReferencePosition 控制
    }

    [Header("基础设置")]
    [Tooltip("当前使用的控制模式")]
    public Modes Mode = Modes.Mouse;

    [Tooltip("控制所有图层振幅的倍率")]
    public float AmplitudeMultiplier = 1f;

    [Tooltip("控制所有图层速度的倍率")]
    public float SpeedMultiplier = 1f;

    [Tooltip("参与视差移动的图层列表")]
    public List<ParallaxLayer> ParallaxLayers;

    // 内部变量
    protected Vector2 _referencePosition;   // 当前输入参考位置(归一化)
    protected Vector3 _newPosition;         // 图层的新位置
    protected Vector2 _mousePosition;       // 鼠标当前位置(屏幕坐标)

    /// <summary>
    /// Start 时初始化所有图层的起始位置
    /// </summary>
    protected virtual void Start()
    {
        Initialization();
    }

    /// <summary>
    /// 初始化:记录每个图层的起始位置
    /// </summary>
    public virtual void Initialization()
    {
        foreach (ParallaxLayer layer in ParallaxLayers)
        {
            if (layer.Rect != null)
            {
                layer.StartPosition = layer.Rect.position;
            }
        }
    }

    /// <summary>
    /// 每帧更新:移动所有图层
    /// </summary>
    protected virtual void Update()
    {
        MoveLayers();
    }

    /// <summary>
    /// 根据控制模式更新 _referencePosition 并移动图层
    /// </summary>
    protected virtual void MoveLayers()
    {
        // 根据控制模式获取输入
        switch (Mode)
        {
            case Modes.Gyroscope:
                // 示例:你可以接入 Input.gyro.rotationRate 或 attitude(仅限移动设备)
                // _referencePosition = new Vector2(Input.gyro.rotationRate.x, Input.gyro.rotationRate.y);
                break;

            case Modes.Mouse:
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
                _mousePosition = UnityEngine.InputSystem.Mouse.current.position.ReadValue(); // 新输入系统
#else
                _mousePosition = Input.mousePosition; // 旧输入系统
#endif
                // 将鼠标屏幕坐标转为归一化视口坐标(0~1)
                _referencePosition = Camera.main.ScreenToViewportPoint(_mousePosition);
                break;

            case Modes.Script:
                // 由外部通过 SetReferencePosition() 设置
                break;
        }

        // 遍历每个图层并移动位置
        foreach (ParallaxLayer layer in ParallaxLayers)
        {
            if (layer.Active && layer.Rect != null)
            {
                // X轴移动(缓动)
                _newPosition.x = Mathf.Lerp(
                    layer.Rect.position.x,
                    layer.StartPosition.x + _referencePosition.x * layer.Amplitude * AmplitudeMultiplier,
                    layer.Speed * SpeedMultiplier * Time.deltaTime
                );

                // Y轴移动(缓动)
                _newPosition.y = Mathf.Lerp(
                    layer.Rect.position.y,
                    layer.StartPosition.y + _referencePosition.y * layer.Amplitude * AmplitudeMultiplier,
                    layer.Speed * SpeedMultiplier * Time.deltaTime
                );

                _newPosition.z = 0f;

                // 更新图层位置
                layer.Rect.position = _newPosition;
            }
        }
    }

    /// <summary>
    /// 设置一个新的输入参考位置(仅在 Script 模式下使用)
    /// 值通常在 (0,0) 到 (1,1) 之间
    /// </summary>
    public virtual void SetReferencePosition(Vector3 newReferencePosition)
    {
        _referencePosition = newReferencePosition;
    }
}


🛠️ 使用方法

1️⃣ 添加组件

  1. 在 Canvas 下创建一个空 GameObject,命名为 UIParallaxRoot

  2. 挂载 MMParallaxUI 脚本。

  3. 在 Inspector 中配置 ParallaxLayers 列表,添加你希望参与视差效果的图层(Image/Text 等 UI 元素)。

  4. 配置每层的 SpeedAmplitude

2️⃣ 设置控制模式

  • Mouse(默认):使用鼠标位置控制(适用于PC)。

  • Gyroscope:适用于移动端,可扩展为接入 Input.gyro

  • Script:通过代码调用 SetReferencePosition() 控制。

// 示例:手动控制参考位置
parallaxUI.SetReferencePosition(new Vector2(0.5f, 0.5f)); // 回中


🎮 目前我使用的阈值如下

👇 均为透明png实现
     按我这个阈值去配置  可以得到首图的效果


这个脚本目前直接挂载使用即可,陀螺仪方面还未拖拽完毕,也可以增加“自动回中”逻辑,在鼠标或输入松开后回归中心位置。如需图片素材做参考,或者你有新的思路和实现方式 可以私信我或在评论区留言。


网站公告

今日签到

点亮在社区的每一天
去签到