Unity Input System入门:从设备API到动作映射的实战指南
1. 项目概述为什么Unity开发者绕不开Input System如果你是从Unity的旧版Input Manager就是那个在Input类里写GetKeyDown和GetAxis的时代一路走过来的老鸟或者你是一个刚入门就被各种输入设备搞得晕头转向的新手那么“Input System”这个词对你来说绝对不是一个陌生的概念。它更像是一个摆在面前、迟早要翻越的山头。我刚开始接触时也犯过嘀咕旧系统用得好好的为什么要换但当你真正开始做一个需要支持手柄、触屏、键盘鼠标无缝切换或者想实现一套复杂的技能连招系统时旧Input Manager那种“硬编码”和“设备强绑定”的弊端就暴露无遗了。简单来说Unity的新Input System是一个现代化、事件驱动、设备无关的输入处理框架。它把“按下A键”或“移动右摇杆”这些底层硬件信号抽象成了更高层级的“动作”Actions比如“跳跃”、“移动”、“攻击”。你的游戏逻辑只关心这些“动作”是否被触发至于这个“跳跃”动作是由空格键、手柄A键还是屏幕上的一个虚拟按钮触发的Input System帮你搞定。这带来的好处是巨大的代码与设备解耦一套逻辑适配所有输入方式输入配置可动态修改甚至允许玩家自定义按键对新型输入设备如VR控制器、方向盘的支持更加优雅。所以这个“进阶”学习系列的第一篇我们就从最实在的地方开始如何把它导入到你的项目里以及如何用最基础的API去“摸到”那些连接着的设备。别看这是基础很多人在第一步配置上就卡住了或者因为不熟悉新的API而觉得无从下手。我会带你绕开这些坑用最直白的方式让你快速上手。2. 核心思路事件驱动与动作映射在深入代码之前我们必须先理解Input System的设计哲学这能帮你少走很多弯路。旧Input Manager是典型的“轮询”Polling模式你在Update()里不停地问“空格键按下了吗”。而新Input System的核心是“事件驱动”Event-driven。你可以把它想象成一个高效的邮局。各种输入设备键盘、鼠标、手柄是发信人它们产生事件“按键按下”、“摇杆移动”。Input System是邮局的分拣中心它根据你预先定义好的“动作映射表”Action Maps将这些原始事件分拣成有意义的“动作”包裹比如“跳跃请求”。最后你的游戏逻辑是收件人它只需要监听并处理这些“动作”包裹完全不用关心包裹是哪个发信人、通过什么路线送来的。这种设计的优势在于高效只在输入事件发生时执行代码避免了Update中的空轮询。清晰输入逻辑定义动作和游戏逻辑响应动作分离项目结构更干净。灵活动态切换输入方案例如从键鼠切换到手柄变得非常简单只需启用不同的“动作映射集”即可。我们本篇要学习的“基础设备调用API”实际上是在和“分拣中心”的基础设施打交道比如查询当前有哪些“发信人”设备在线或者直接监听某个“发信人”的原始事件。这在调试、创建自定义输入设备管理器或处理一些非常规输入时非常有用。3. 第一步导入Input System的三种方式与避坑指南这是所有事情的起点但Unity的包管理有时会给你设点小障碍。以下是三种主流方式我会详细说明每种方式的适用场景和可能遇到的坑。3.1 通过Package Manager安装推荐这是最标准、最推荐的方式能确保你获取到的是经过Unity验证的兼容版本。打开Unity进入顶部菜单Window Package Manager。在Package Manager窗口左上角点击“Packages”下拉菜单选择“Unity Registry”。这是关键一步如果你停留在“My Assets”或“In Project”可能找不到它。在列表中找到“Input System”。你可以使用右上角的搜索框直接搜索。点击它在右侧详情面板中点击“Install”按钮。注意点击安装后Unity会弹出一个警告对话框提示“Input System将禁用旧的Input Manager”。这是正常的也是必须的。两个系统可以共存但同一时间只能有一个处于激活状态。直接点击“Yes”确认。如果你后续需要切换回去可以在Edit Project Settings Player中找到“Active Input Handling”选项进行更改。可能遇到的坑找不到包请确保你的Unity版本是2019.1或更高并且Package Manager源切换到了“Unity Registry”。2019.1是Input System正式发布的最低版本。安装失败检查网络连接或者尝试重启Unity和Package Manager。3.2 通过Unity Registry直接安装命令行/快速对于喜欢终端或者需要自动化脚本的开发者可以使用Unity的命令行工具。打开系统命令行Windows的CMD/PowerShellMac的Terminal导航到你的项目根目录然后执行unity -batchmode -projectPath . -executeMethod UnityEditor.PackageManager.Client.Add -args com.unity.inputsystem这种方式适合CI/CD流水线但在日常开发中不如图形界面直观。3.3 手动修改manifest.json高级/特定版本有时你可能需要锁定一个特定的、非最新的Input System版本。这时可以手动修改项目。在你的Unity项目文件夹中找到Packages/manifest.json文件。用文本编辑器打开它。在dependencies区块中添加或修改这样一行com.unity.inputsystem: 1.7.0,保存文件返回Unity它会自动开始解析和导入这个指定版本的包。实操心得我强烈建议新手使用第一种方式Package Manager。它能最大程度避免版本冲突和依赖问题。只有在你知道自己需要某个特定版本来解决兼容性问题时才考虑第三种方式。安装完成后你会在Package Manager的 “In Project” 列表中看到它并且菜单栏会多出一个“Edit Project Settings Input System Package”的选项这说明导入成功了。4. 基础设备调用API详解与硬件直接对话导入成功后我们就可以开始写代码了。Input System的核心命名空间是UnityEngine.InputSystem。我们先从最基础的——获取和监听输入设备开始。这部分API让你能直接接触到“邮局”的“发信人列表”。4.1 枚举所有已连接的设备InputSystem.devices是一个静态列表包含了当前所有被Input System识别并连接的输入设备。using UnityEngine; using UnityEngine.InputSystem; // 关键引入命名空间 public class DeviceLister : MonoBehaviour { void Start() { // 遍历所有设备并打印信息 foreach (var device in InputSystem.devices) { Debug.Log($设备名称: {device.displayName}, 类型: {device.GetType()}, 设备ID: {device.deviceId}); // 你可以进一步判断设备具体类型 if (device is Gamepad) { Debug.Log( 这是一个手柄); Gamepad gamepad device as Gamepad; // 现在你可以访问gamepad特有的属性如 gamepad.aButton } else if (device is Keyboard) { Debug.Log( 这是一个键盘); } else if (device is Mouse) { Debug.Log( 这是一个鼠标); } // 还可以判断Touchscreen, Pen, Sensor等 } } }为什么需要这个在游戏开始时的输入设备检测、创建动态的键位设置界面显示当前连接了哪些手柄、或者为不同设备初始化不同的默认配置时这个API非常有用。4.2 监听设备的连接与断开事件游戏运行时玩家可能会插拔手柄。我们需要动态响应这些变化。public class DeviceMonitor : MonoBehaviour { private void OnEnable() { // 订阅设备连接事件 InputSystem.onDeviceChange OnDeviceChange; } private void OnDisable() { // 取消订阅防止内存泄漏 InputSystem.onDeviceChange - OnDeviceChange; } private void OnDeviceChange(InputDevice device, InputDeviceChange change) { switch (change) { case InputDeviceChange.Added: Debug.Log($设备已连接: {device.displayName}); // 例如自动将新连接的手柄设为玩家2 break; case InputDeviceChange.Removed: Debug.Log($设备已断开: {device.displayName}); // 例如处理玩家掉线逻辑 break; case InputDeviceChange.ConfigurationChanged: Debug.Log($设备配置已更改: {device.displayName}); // 例如手柄电量低或配置文件切换 break; // 还有其他枚举值如Enabled, Disabled, UsageChanged等 } } }注意事项一定要订阅和取消订阅配对出现。在OnEnable中订阅在OnDisable或OnDestroy中取消订阅这是Unity事件系统的良好实践避免脚本禁用或物体销毁后仍被调用导致空引用或逻辑错误。InputDeviceChange.ConfigurationChanged对于支持宏或配置切换的专业设备如一些高端手柄或方向盘很有用。4.3 获取特定类型的默认设备通常我们关心的是“玩家1用的手柄”或“主键盘”。Input System提供了便捷的获取方式。void CheckDefaultDevices() { // 获取当前平台默认的游戏手柄通常是最近连接或第一个识别的手柄 Gamepad currentGamepad Gamepad.current; if (currentGamepad ! null) { Debug.Log($当前默认手柄: {currentGamepad.displayName}); // 直接读取状态 if (currentGamepad.aButton.wasPressedThisFrame) { Debug.Log(默认手柄的A键被按下了); } } else { Debug.LogWarning(未检测到连接的手柄。); } // 同样可以获取键盘和鼠标 Keyboard keyboard Keyboard.current; Mouse mouse Mouse.current; if (keyboard ! null keyboard.spaceKey.wasPressedThisFrame) { Debug.Log(空格键被按下通过.current); } }核心要点.current属性是获取“主要”或“最相关”设备的最快方式。对于鼠标键盘在PC上基本总是有效的。对于手柄它通常指向第一个可用的手柄。在访问.current或任何设备属性前进行空值检查 (! null) 是一个好习惯因为设备可能在任何时候被拔掉。4.4 直接读取设备状态低层级API虽然Input System鼓励使用“动作”Action但有时你需要直接读取原始数据比如做一个设备测试工具。public class RawInputReader : MonoBehaviour { void Update() { var gamepad Gamepad.current; if (gamepad ! null) { // 读取左摇杆的二维向量值 Vector2 leftStickValue gamepad.leftStick.ReadValue(); if (leftStickValue.magnitude 0.1f) // 加入一个死区阈值 { Debug.Log($左摇杆: {leftStickValue}); } // 读取右扳机键的压力值0到1 float rightTriggerValue gamepad.rightTrigger.ReadValue(); if (rightTriggerValue 0.2f) { Debug.Log($右扳机压力: {rightTriggerValue}); } // 判断按钮是否在本帧被按下 if (gamepad.buttonSouth.wasPressedThisFrame) // 通常是A键Xbox布局或Cross键PlayStation布局 { Debug.Log(A/X键被按下); } } // 直接读取键盘事件 var keyboard Keyboard.current; if (keyboard ! null) { // 同时检测多个按键 if (keyboard.wKey.isPressed keyboard.leftShiftKey.isPressed) { Debug.Log(正在冲刺WShift); } } } }与旧Input Manager的对比这里gamepad.leftStick.ReadValue()类似于旧的Input.GetAxis(“Horizontal”)但它是直接从设备对象获取更面向对象。wasPressedThisFrame和isPressed则对应GetKeyDown和GetKey。重要区别Input System中的wasPressedThisFrame等状态其“帧”的判断是基于Input System自己的更新时机默认与MonoBehaviour.Update同步但也可以配置为固定更新或手动更新这比旧系统更灵活。5. 从设备API到动作映射的桥梁PlayerInput组件初探学完基础设备API你可能会觉得“这好像和旧系统差不多啊还是要手动查设备、读数值。” 别急这只是为了让你理解底层。Input System的威力在于上层的“动作映射”。Unity提供了一个非常方便的组件PlayerInput来帮你快速搭建这座桥梁。虽然本系列后续会深入讲解自己创建Input Action Asset但这里可以先感受一下PlayerInput的便捷创建动作资源在Project窗口右键Create Input Actions命名为“PlayerControls”。双击打开编辑器创建一个“Gameplay” Action Map在里面添加两个Action“Move”2D Vector类型绑定到Gamepad的左摇杆和键盘的WASD。“Jump”Button类型绑定到Gamepad的South按钮和键盘空格键。保存资源。在游戏对象上添加PlayerInput组件。将“PlayerControls”这个Asset拖拽到组件的“Actions”属性上。在“Behavior”属性中选择“Invoke Unity Events”。现在你不需要在Update里写任何检测代码。只需要在PlayerInput组件暴露出来的UnityEvent中关联你的处理函数public class PlayerController : MonoBehaviour { public void OnMove(InputAction.CallbackContext context) { // context.ReadValueVector2() 直接给你移动向量 Vector2 direction context.ReadValueVector2(); Debug.Log($移动输入: {direction}); // 这里可以驱动角色移动 } public void OnJump(InputAction.CallbackContext context) { // context.started 表示按钮按下开始的瞬间 if (context.started) { Debug.Log(跳跃); // 这里触发跳跃逻辑 } } }PlayerInput组件自动帮你处理了所有设备的绑定、事件监听和路由你的代码只需要关心高层的“移动”和“跳跃”动作。这才是Input System的正确打开方式。基础设备API是你需要知道的“后台知识”而动作映射才是你日常开发的“主力工具”。6. 常见问题与排查技巧实录在实际使用基础API时我踩过不少坑这里总结一下希望能帮你节省时间。问题1InputSystem.devices列表为空或者Gamepad.current为null。可能原因1项目设置中未启用Input System。去Edit Project Settings Player Other Settings查看“Active Input Handling”是否选择了“Input System Package (New)”或“Both”。必须选前两者之一。可能原因2脚本执行顺序问题。在Awake中访问设备可能太早Input System尚未初始化完毕。将设备访问代码移到Start或OnEnable中更安全。可能原因3真的没有设备连接或者设备驱动异常。尝试插拔设备或检查系统设备管理器。问题2手柄输入有延迟或不跟手。排查检查Input System的更新模式。在Edit Project Settings Input System Package中有一个“Update Mode”选项。Fixed Update与物理更新同步输入采样频率固定适合物理相关的输入但可能感觉延迟。Dynamic Update默认选项。尽可能快地处理输入通常感觉最跟手。Manual Update需要手动调用InputSystem.Update()适用于特定框架如ECS。新手不要选这个。建议对于大多数动作游戏保持“Dynamic Update”即可。问题3同时连接多个同型号手柄时如何区分玩家1和玩家2解决方案基础API中InputSystem.devices列表的顺序并不总是代表玩家顺序。更可靠的做法是利用InputSystem.onDeviceChange事件监听设备连接。当手柄连接时InputDeviceChange.Added将其加入一个你自己管理的“可用手柄”列表。在游戏逻辑中如角色选择界面让玩家按手柄上的某个键如Start键来“认领”这个手柄并将其与你游戏中的“玩家索引”绑定。之后通过你绑定的索引来访问特定的手柄对象而不是依赖.current。问题4在编辑器里运行正常打包后输入无效。排查这是最常见的问题之一。首先检查打包设置中是否包含了Input System需要的资源。确保你的Input Action Asset.inputactions文件在打包的Resources文件夹或Addressables加载路径中。最简单的方法是将它放在Resources文件夹下或者确保它在Project Settings Player Publishing Settings的Managed Stripping Level设为Low或Medium防止代码剥离过度。检查依赖Input System包本身已经包含在构建中但如果你用了任何自定义的输入设备插件需要确保它们也被正确打包。问题5如何模拟输入用于测试或录制回放技巧Input System提供了强大的模拟输入API。你可以创建一个InputDevice的模拟器。// 创建一个虚拟手柄 var virtualGamepad InputSystem.AddDeviceGamepad(); // 然后你可以通过代码“按下”它的按钮 InputSystem.QueueStateEvent(virtualGamepad, new GamepadState { .buttonSouth true }); // 这会在下一帧触发所有绑定到“South按钮”的动作非常适合自动化测试。掌握了这些基础设备API你就有了直接与硬件对话的能力。它们是构建更复杂输入逻辑的基石。但请记住在大多数实际游戏开发中你应该尽快过渡到使用Input Action Asset和PlayerInput组件或自己管理InputAction这种更高层、更声明式的模式这才是发挥Input System全部威力的地方。下一篇我们将深入那个强大的可视化配置工具——Input Action Editor学习如何优雅地定义和管理你所有的游戏输入。

相关新闻

Unity TextMeshPro SDF字体资产创建与优化全解析

Unity TextMeshPro SDF字体资产创建与优化全解析

1. 项目概述:为什么我们要深挖TMP_SDF? 如果你在Unity里做过UI,尤其是做过需要多语言支持、动态字体或者对文字效果有较高要求的项目,那么TextMeshPro(TMP)这个组件你一定不陌生。它几乎已经成为了Unity UI…

2026/10/12 0:10:12 阅读更多 →
UVM sequence调试:starting_phase参数详解与实战避坑指南

UVM sequence调试:starting_phase参数详解与实战避坑指南

1. 项目概述:从一次调试失败说起 最近在验证环境中调试一个sequence时,遇到了一个让人有点摸不着头脑的问题。场景是这样的:我写了一个用于配置寄存器的sequence,在测试用例中通过 start() 方法启动它,期望它能顺利地…

2026/10/16 8:40:52 阅读更多 →
HarmonyOS应用实战-启示散页-72-多窗口编辑别互相覆盖草稿:给每个窗口分配 draftSessionId

HarmonyOS应用实战-启示散页-72-多窗口编辑别互相覆盖草稿:给每个窗口分配 draftSessionId

HarmonyOS 应用实战 72:多窗口编辑别互相覆盖草稿:给每个窗口分配 draftSessionId 分屏、多窗口和任务切换在 HarmonyOS 上很常见。题库编辑页如果只把“是否修改过”存在页面里,两个窗口同时打开同一副牌时就会出现一个很隐蔽的问题&#x…

2026/9/26 22:53:24 阅读更多 →

最新新闻

云计算第三版习题答案:零基础入门必看的核心考点与做题方法

云计算第三版习题答案:零基础入门必看的核心考点与做题方法

简介:《云计算(第三版)》配套习题答案,是一份面向云计算课程学习者、备考学生及自学者整理的讲义型资料。内容围绕教材核心知识展开,覆盖云计算基础概念与特点、IaaS/PaaS/SaaS三种服务模型、公有云/私有云/混合云/社区…

2026/10/16 16:09:40 阅读更多 →
VLA安全对齐:约束学习方法让具身智能机器人更可靠

VLA安全对齐:约束学习方法让具身智能机器人更可靠

最近在整理具身智能方向的论文,连着读了好几篇视觉-语言-动作模型(Vision-Language-Action Model,VLA)的工作,越读越觉得一个矛盾特别突出:模型的能力涨得飞快,但让人放心程度完全没跟上。VLA 模…

2026/10/16 16:09:40 阅读更多 →
Octop 数据管道编排实战:核心模型、调度重试与踩坑指南

Octop 数据管道编排实战:核心模型、调度重试与踩坑指南

1. 从“Octop”这个名字说起:它到底指什么第一次看到“Octop”这个词,很多人会下意识联想到章鱼(Octopus),或者某个开源项目的缩写。我最初接触它的时候也走了弯路,以为是个冷门的命令行工具,翻…

2026/10/16 16:09:40 阅读更多 →
279个开箱即用Shell脚本合集:运维自动化与Linux实用工具详解

279个开箱即用Shell脚本合集:运维自动化与Linux实用工具详解

简介:这套资料汇集279个可直接使用的Shell脚本,覆盖DDoS攻击IP自动屏蔽、Linux邮件告警、MySQL单/多循环备份、Nginx访问日志按天切割与分析、网卡实时流量查看等常见运维场景,适合Linux/Unix系统管理员、运维工程师及Shell入门学习者参考使用…

2026/10/16 16:09:40 阅读更多 →
小程序集成百度云人脸识别的避坑指南与架构实践

小程序集成百度云人脸识别的避坑指南与架构实践

1. 项目概述:为什么小程序里要嵌人脸识别,而不是直接跳转H5或App?“小程序调用百度云接口实现人脸识别”——这十个字背后,其实是一整套轻量级身份核验场景的落地闭环。我做过不下二十个政务、金融、教育类的小程序项目&#xff0…

2026/10/16 16:09:40 阅读更多 →
计算机网络实验复习:冲突域、广播域与ARP协议核心考点梳理

计算机网络实验复习:冲突域、广播域与ARP协议核心考点梳理

简介:这份文档面向计算机网络课程的初学者与实验课学生,聚焦交换机与集线器的工作原理这一基础而关键的知识点,帮助读者通过 Cisco Packet Tracer 仿真实验理解两种设备在数据转发上的本质差异。资源包内含 1 个 doc 文件,大小约 …

2026/10/16 16:08:39 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →