1. 问题描述
项目使用UGUI方案,以点击交互为主。
微信unity方案本身只能调PixelRatio,不能直接调整Unity的分辨率,(还没有测试过“自适应屏幕尺寸”会怎么样),不过看前段时间热门的unity小游戏项目《无尽冬日》也没有对分辨率进行适配,采用了分辨率拉伸的模式。
简单以我自己的手机举例,如果正常打开微信,再打开小游戏,分辨率是1080x2461,如果把小游戏的窗口单独拖到小窗口模式,WeChatWASM.WX.OnWindowResize()的回调给的分辨率是1080x1729,但因为不能调整Unity的分辨率,所以整个画面是拉伸的。反过来,如果先打开微信,把微信拉成小窗口,再打开小游戏,之后再把小游戏全屏,效果会反过来。
画面拉伸问题不算太大,但点击响应的问题比较严重,经过测试,以从左到右为x轴,从下到上为y轴,全屏启动小游戏再拉到小窗口模式后(1080x1729),左上角的输入坐标还是正确的(0,2461), 但左下角的坐标就是错误的(0,732)(2461-1729),导致的现象是越靠近上方的按钮越有可能点对,靠近下方的就点不到了。正确的坐标和输入坐标的关系是
Vector2 GetMousePosition() { var pos = Input.mousePosition; int originalHeight = 2461; // 初始化时存储 int modifiedHeight = 1729; // OnWindowResize() 回调获取 if (originalHeight == modifiedHeight) { return pos; } float resolutionScale = 1; // 分辨率缩放 float y = pos.y; pos.y = (y + (modifiedHeight - originalHeight) * resolutionScale) / modifedHeight * originalHeight; return pos; }
旧的输入系统(InputManager)也能达成效果,需要继承GraphicRaycaster类,修改UI的点击检测逻辑
另外业务逻辑中获取Input.mousePositon的地方也需要覆盖一下
public class CustomGraphicRaycaster : GraphicRaycaster { public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendLit) { var pos = eventData.position; // 修改位置 event.position = pos; base.Raycast(eventData, resultAppendList); } }
那其实直接改UGUI源码就行....
换用了新的输入系统InputSystem,是因为当时没试出来😂
2. 从UnityEngine.Input升级到UnityEngine.InputSystem
- 首先要更新UGUI源码(如果使用embbed模式), 虽然版本号一直是1.0.0, 但还是有多次修改的
- Window > PackageManager 安装 Input System (本文使用的是1.7.0版本)
- Editor > Project Settings > OtherSettings > Active Input Handling, 改为"Input System Package" (关闭旧的输入系统)
- UGUI的事件接入: 找到场景中的EventSystem, 把原本的StandaloneUIInputModule删掉, 挂一个InputSystemUIInputModule上去
3. 常用API的直接近似替换
需要注意判空
鼠标:
- Input.mousePosition -> Mouse.current.position.ReadValue()
- Input.GetMouseButton(index) -> Mouse.current.leftButton.isPressed | Mouse.current.rightButton.isPressed (下面省略)
- Input.GetMouseButtonDown(index) -> Mouse.current.leftButton.wasPressedThisFrame
- Input.GetMouseButtonUp(index) -> Mouse.current.leftButton.wasReleasedThisFrame
- Input.GetAxis("Mouse ScrollWheel") -> Mouse.current.scroll.ReadValue().y / 120f
键盘(实际只有编辑器在用):
- Input.GetKey(KeyCode.W) -> Keyboard.current.wKey.isPressed
- Input.GetKey(keyCode) -> switch case
- 键盘的DownUp也是wasPressedThisFramewasReleasedThisFrame
另外鼠标的逻辑替换需要注意判断触屏,以Input.mousePosition为例
public static class Input { public static Vector2 mousePosition { get { #if !UNITY_EDITOR && UNITY_WEBGL var pos = TouchScreen.current?.primaryTouch.position.ReadValue() ?? Vector2.zero; #else var pos = Mouse.current?.position.ReadValue() ?? Vector2.zero; #endif return pos; } } }
4. 添加作用到UGUI的坐标转换
为了方便修改,直接把InputSystem包改成embbed模式(包文件夹从Library/PackageCache移动到Packages文件夹)
新建Packages/com.unity.inputsystem@xxx/InputSystem/Controls/Processors/ResolutionVector2Processor.cs
namespace UnityEngine.InputSystem.Processors { public class ResolutionVector2Processor : InputProcessor<Vector2> { // 这些参数自行找时机传递过来即可 private static int s_OriginalHeight = 0; private static int s_ModifiedHeight = 0; private static float s_ResolutionScale = 1.0f; public override Vector2 Process(Vector2 value, InputControl control) { // 和第一节的逻辑相同 if (s_OriginalHeight == s_ModifiedHeight) return value; float y = value.y; value.y = (y + (s_ModifiedHeight - s_OriginalHeight) * s_ResolutionScale) / s_ModifiedHeight * s_OriginalHeight; return value; } public override string ToString() { return $"ResolutionVector2()"; } } }
到Packages/com.unity.inputsystem@xxx/InputSystem/InputManager.cs里面注册一下
internal void InitializeData() { // ... processors.AddTypeRegistration("ResolutionVector2", typeof()); // ... }
然后在unity中Project窗口选中Packages/com.unity.inputsystem@xxx/InputSystem/Plugins/PlayerInput/DefaultInputActions.inputactions
点击Inspector窗口的Edit asset
ActionMaps选择UI
Actions选择 Point > touch*/position [Touchscreen]
在Processors中添加刚才新建的ResolutionVector2,保存即可
5. Input.mousePosition的替换
回到第3节的代码,把转换逻辑加进去即可