微信小游戏这一套环境,做过的朋友都清楚,它跟标准的WebGL发布完全是两码事。Unity里跑得好好的InputField,打包成小游戏丢进微信,点上去一点反应没有,键盘就是不弹——这个问题几乎每个第一次把Unity项目搬到微信小游戏的团队都会撞一次。我自己头一回遇到的时候,也以为是InputField的Raycast被挡了,折腾了半天UI层,最后才发现根本不是那么回事。这篇文章就把"Unity微信小游戏无法调起输入框"从头到尾拆一遍:为什么会调不起来、微信小游戏自己提供了哪些输入接口、Unity的InputField/ TMP_InputField该怎么改造成能用的状态、键盘高度变化怎么让界面避让、真机和开发者工具行为差异怎么排查,以及多行输入、仿输入框槽位这类进阶需求该怎么落地。适合正在做Unity微信小游戏、被输入框卡住的开发,也适合还没踩坑、想提前避坑的同学参考。
1. 先别急着改代码:把"调不起输入框"这件事分层看
很多人一上来就去翻InputField的Inspector,怀疑是readOnly勾了、interactable被关了、或者有透明Image挡在前面。这些当然要排查,但如果你是在微信小游戏里遇到的,99%的概率问题不在这些地方。要搞清楚它,得先明白"点击UI后弹键盘"这条链路,在Unity原生、标准WebGL、微信小游戏这三种环境里,走的是完全不同的路径。
1.1 三种典型报错现场
我见过的情况大体分三类,先对号入座一下,能省掉不少瞎试的时间。
第一类,点击InputField,UI上有高亮或者选中态,但键盘死活不弹。光标可能都不出现,或者出现了但一闪就没。这种情况基本可以判定是输入法调起链路断了。
第二类,键盘弹出来了,但输入的内容不进InputField,或者输入一个字就断了、光标乱跳。这类通常是事件绑定和回填逻辑的问题,键盘本身没问题。
第三类,键盘弹出来、内容也进去了,但是键盘把整个界面顶飞了,或者收起键盘后画面错位、黑屏、UI偏移。这属于布局和渲染层的问题,是另一个维度。
提示:在动手改之前,先确认你遇到的是哪一类。很多所谓"调不起输入框"其实是第二类或第三类,改错了方向会越改越乱。
1.2 WebGL的输入链路为什么在小游戏里断了
要理解这个问题,得先知道标准WebGL是怎么处理输入的。Unity导出成WebGL之后,页面里其实会动态插入一个隐藏的HTML<input>或<textarea>元素。当你点击InputField,Unity的运行时会把浏览器焦点切到这个隐藏元素上,浏览器检测到焦点变化,就弹出系统软键盘,用户敲的字符通过这个元素的input事件回传给Unity,再由Unity写进InputField的text里。整套流程依赖的是真实DOM和浏览器的焦点管理机制。
而微信小游戏不是浏览器页面。它运行在微信客户端内部的JavaScript虚拟环境里,没有DOM,没有HTML元素,没有浏览器焦点这套东西。你导出的小游戏代码里,Unity那套"插入隐藏input、切换焦点"的逻辑自然就完全失效了。点击事件能收到,因为触摸事件是小游戏自己模拟的;但"把焦点交给一个不存在的DOM元素"这一步,就断在这里了。
这就是为什么很多人发现:InputField的onValueChanged能触发、点击也响应,就是键盘不弹。因为断点根本不在UI层,而在Unity的输入模块和浏览器DOM之间的那层桥。微信小游戏官方也清楚这一点,所以单独提供了一套自己的键盘调用接口,把"弹键盘"这件事从浏览器手里接管过来。你要做的,就是把InputField和这套接口对接上,用微信的键盘状态去驱动Unity的文本显示。
理解了这个本质,后面的方案就顺理成章了:不要指望InputField自己弹键盘,而是拦截点击事件,主动调用微信的键盘接口,再把键盘返回的内容手动写回InputField。
2. 微信小游戏的输入接口到底怎么用
微信小游戏提供了一套键盘相关的接口,Unity侧通过官方的小游戏适配SDK(也就是常见的WX-WASM-SDK这类转换工具,不同版本命名略有差异,下面的接口名以主流SDK为准)来调用。核心就几个:WX.ShowKeyboard、WX.HideKeyboard、WX.UpdateKeyboard,以及三个回调WX.OnKeyboardInput、WX.OnKeyboardConfirm、WX.OnKeyboardComplete。另外还有一个高度变化回调WX.OnKeyboardHeightChange,做避让必须要用。
2.1 ShowKeyboard 系列的参数逐个说清
WX.ShowKeyboard的参数不多,但每个都影响实际体验,必须弄清楚。
- defaultValue:键盘弹起时预填的内容。一般传当前InputField里已有的text,用户接着改,不然会出现"点进去内容空了"的诡异现象。
- maxLength:允许输入的最大长度。注意这里传的是整数,不是InputField的characterLimit字符串。传小了用户打不进去,传大了不生效,最好跟InputField的设置保持一致。
- multiple:是否是多行输入。false是单行,true是多行。多行的行为在不同基础库版本上有差异,后面单独讲。
- confirmType:键盘右下角那个键显示什么。常见值有
done、send、search、next、go。做搜索框就传search,做聊天输入就传send,别小看这个细节,它直接影响用户对输入框功能的心智预期。 - confirmHold:点确认键之后键盘是否保持不收起。默认false,也就是点一下确认就收键盘。做连续输入或者聊天框,一般也希望收起,除非做多标签连续输入。
举个实际调用长这样,先感受一下:
var option = new ShowKeyboardOption { defaultValue = inputField.text, maxLength = 30, multiple = false, confirmType = "done", confirmHold = false }; WX.ShowKeyboard(option);2.2 CreateInput / CreateTextarea 的另一条路
除了ShowKeyboard这套,部分SDK版本还提供了WX.CreateInput和WX.CreateTextarea,也就是在小游戏上层创建一个原生输入组件,返回一个对象,你可以调用它的show、hide,还能监听它的输入事件。这条路的好处是输入框是"真实的原生控件",输入法行为更接近系统原生,中文拼音候选、光标移动、选择文本这些体验都会更好。
但它的代价也明显:它是一个浮在游戏画面之上的原生层,位置、大小、样式都要通过参数或后续调用来控制,跟Unity的UI对不齐是常有的事,而且它在不同机型上位置偏移、层叠顺序的问题比ShowKeyboard更多。我个人建议,简单输入(登录、改昵称、搜索关键词)优先用ShowKeyboard,因为它可控、问题少;只有当用户需要长文本、多行、复杂编辑体验时,才考虑CreateTextarea这种原生控件的方案。
注意:这两套接口不要同时用。我见过有项目既在点击时调ShowKeyboard,又用CreateInput创建了一个隐藏输入框,结果两个输入源打架,输入内容重复、光标乱跳,排查了半天。选定一条路走到底。
3. 手把手改造 InputField:从点击到回填的完整链路
光知道接口不够,关键是怎么把它和Unity的InputField、TMP_InputField接起来。核心思路一句话:接管InputField的交互,让微信键盘成为唯一的输入来源,Unity端只负责显示和存储文本。下面按顺序把整条链路写出来。
3.1 组件挂载与SDK初始化时机
第一步,别再用InputField默认的交互逻辑去处理。最稳妥的做法是给需要输入的地方单独挂一个适配脚本,脚本里持有对InputField的引用,同时处理SDK的初始化。
SDK初始化有个坑要提前说:WX.InitSDK是异步的,回调没回来之前调用键盘接口可能无效或者报错。所以一定要把键盘回调的注册放在InitSDK的回调里,而不是Awake里直接注册。我见过不少"回调不触发"的问题,根源就是注册提前了。
using UnityEngine; using UnityEngine.UI; using WeChatWASM; public class WXInputAdapter : MonoBehaviour { [SerializeField] private InputField inputField; private bool _keyboardReady; void Awake() { if (inputField == null) inputField = GetComponent<InputField>(); } void Start() { WX.InitSDK((code) => { WX.OnKeyboardInput(OnKeyboardInput); WX.OnKeyboardConfirm(OnKeyboardConfirm); WX.OnKeyboardComplete(OnKeyboardComplete); WX.OnKeyboardHeightChange(OnKeyboardHeightChange); _keyboardReady = true; Debug.Log("WX SDK 初始化完成, code = " + code); }); } }这里把回调都挂上,后续三个回调分别处理:输入中、点确认、键盘收起。三个都要处理,缺一个都会留下状态残留的隐患。
3.2 点击唤起、输入回填、确认收尾
点击唤起很简单,给InputField加一个点击区域,或者直接监听它的onPointerClick。注意不要依赖InputField自己的选中逻辑去弹键盘,那个在微信小游戏里是不生效的。你可以用一个透明的Button盖在InputField上,点击时调ShowKeyboard。
public void OnInputClicked() { if (!_keyboardReady) return; var option = new ShowKeyboardOption { defaultValue = inputField.text, maxLength = 20, multiple = false, confirmType = "done", confirmHold = false }; WX.ShowKeyboard(option); }回填逻辑是整个改造里最关键的。用户每敲一个字符,微信会通过OnKeyboardInput推给你一个结果对象,里面带着当前整个输入框的完整文本(注意是完整值,不是增量)。你把它写回InputField的text就行:
private void OnKeyboardInput(OnKeyboardInputListenerResult res) { inputField.text = res.value; // 主动触发一次校验/联动逻辑 inputField.onValueChanged.Invoke(res.value); } private void OnKeyboardConfirm(OnKeyboardConfirmListenerResult res) { inputField.text = res.value; inputField.onEndEdit.Invoke(res.value); WX.HideKeyboard(new HideKeyboardOption()); } private void OnKeyboardComplete(OnKeyboardCompleteListenerResult res) { inputField.text = res.value; }有一点要注意:回填的时候会触发InputField自身的onValueChanged,如果你的业务在onValueChanged里做了文字过滤、字数统计,可能会被触发两次(一次是手动Invoke,一次是赋text触发)。稳妥的做法是别手动Invoke,改在赋值之后自己调业务方法,或者干脆只用一个入口。这个坑很隐蔽,表现为"统计数字翻倍",不仔细看很难发现。
3.3 键盘高度变化与界面避让
移动端最烦人的就是键盘弹起来把输入框挡住了。微信给了WX.OnKeyboardHeightChange,会告诉你键盘高度,你可以据此把输入框往上顶。
private void OnKeyboardHeightChange(OnKeyboardHeightChangeListenerResult res) { float heightInPixel = res.height; // 把像素高度换算成Unity的UI单位 float uiHeight = heightInPixel / 2f; // 假设缩放系数为2 inputRootRect.anchoredPosition = new Vector2(0, uiHeight); }这里有两个细节。第一,微信返回的高度单位是物理像素,你换算成Unity的UI单位时要考虑Canvas scaler的缩放系数,不能直接拿来用,否则顶起的距离会偏大或偏小。第二,键盘高度变化是动态的——安卓上键盘可能带候选词条,高度会变;用户切换输入法,高度也会变。所以不要只在弹起时算一次,要在回调里每次都更新位置,并且做好边界处理,别把UI顶出屏幕。如果做的是聊天框这种贴在底部的输入条,建议直接根据键盘高度做lerp过渡,避免位置突跳。
4. 踩坑实录:那些工具里好好的、真机上翻车的场景
改造完之后,本地跑通不代表真机没问题。这个环节几乎是我们踩坑最密集的地方,单独拎出来说。
4.1 常见问题速查表
下面这张表是我陆陆续续记下来的,遇到问题先对照着查,能省掉很多重复劳动。
| 现象 | 大概率原因 | 处理方式 |
|---|---|---|
| 点击无任何反应 | SDK未初始化完成、键盘接口提前调用 | 把调用放进InitSDK回调后 |
| 键盘弹出但内容不进 | 没监听OnKeyboardInput或没回填text | 补上回填逻辑 |
| 内容进了一半就断 | 回调被多次注册,事件重复 | 初始化只做一次,做好去重 |
| 字数统计翻倍 | 赋值触发onValueChanged + 手动Invoke | 只保留一个触发入口 |
| 键盘遮挡输入框 | 没做高度避让 | 监听高度变化并顶起UI |
| 真机无键盘、工具正常 | 开发者工具模拟不了原生键盘 | 一律以真机为准 |
| iOS顶部/底部错位 | 安全区未适配 | 结合安全区调整布局 |
| 多行输入光标错位 | 用了showKeyboard多行模式 | 改用CreateTextarea方案 |
| 键盘收起画面残留 | 没有监听Complete做清理 | 在Complete里复位状态 |
| 输入法状态残留 | 频繁Show/Hide冲突 | 加节流,避免短时间反复调用 |
这张表里,第一行和第六行是最容易耽误时间的。SDK初始化那个坑,本质上是个时序问题;开发者工具模拟不了键盘,是因为工具里根本没有原生的系统键盘,它只是模拟了接口调用,不能反映真实输入法的行为。所以我的习惯是,输入相关的功能,一律真机验证,开发者工具只用来检查逻辑通不通。
4.2 几个容易被忽略的细节
除了表里那些,还有几个我印象比较深的细节。
一个是被动收起键盘的场景。用户按了物理返回键、或者切到后台再回来,键盘可能已经收了,但你本地还记着"键盘是弹起状态",导致下次点击调不起键盘。解决方式是在OnKeyboardComplete里统一把状态复位,不管是什么原因收的键盘,都走同一个清理入口。
另一个是多个输入框共用一个键盘。如果你的页面有两个以上输入框,一定要注意当前聚焦的是哪个,回填的时候别写错对象。我们的做法是维护一个"当前活跃输入框"的引用,点击谁就把谁设成活跃,回调统一往这个引用里写。这个模式很干净,也避免了多个适配脚本各自注册回调造成的混乱。
还有中文输入法的问题。安卓上某些输入法在拼拼音的过程中,OnKeyboardInput给的是拼音,选字之后才是最终汉字。如果你在InputField的onValueChanged里实时做了敏感词过滤或者搜索联想,很可能在拼音阶段就触发了,体验会很怪。这种情况建议在confirm之后再做重逻辑,输入过程中只做轻量的显示。
5. 更进一步:自建输入面板与多行输入的处理
单行输入用上面这套基本能覆盖。但实际项目里,多行输入、自定义样式的输入框需求很常见,值得单独聊聊。
5.1 单行够用,多行怎么办
前面提过,WX.ShowKeyboard的multiple参数在多行场景下,不同基础库版本表现不一致。有的版本多行了但回车换行不生效,有的版本光标跑到别处,还有的干脆把内容截断。我踩过的坑是:在某个版本上,多行输入时光标总是在开头,用户打字全插在最前面。
遇到多行需求,我的经验是优先考虑CreateTextarea,它本质上是一个原生textarea组件,多行编辑、换行、光标移动都接近系统原生,稳定性明显好于showKeyboard的多行模式。代价就是它浮在游戏画面之上,样式受限,你需要接受它不能完全跟着Unity的UI走。如果要做的输入界面本身就不复杂(比如一个评论区、一个反馈框),其实可以用全屏遮罩+原生textarea的方式来掩盖层叠问题,用户根本感知不到它在Unity之外。
如果坚持要用showKeyboard做多行,那至少要准备一个降级逻辑:检测输入内容是否包含换行符、渲染时是否正确换行,不通过就走原生方案兜底。
5.2 仿输入框槽位样式的一些思路
有些产品会想要类似那种一个词一个槽位的输入框样式,看起来像是分开的格子,其实是整体输入。这种在小游戏里做的话,有个取巧的思路:视觉上用多个格子显示,逻辑上还是单个输入。用户点击任意格子,都触发同一个ShowKeyboard,回填的时候把文本按字符拆开,分别塞进对应格子的Text里,光标定位在你手动算出来的当前字符位置。
好处是实现简单,不用真正做多输入框;坏处是要自己处理复制粘贴、退格删除这些边界情况,字符数超过槽位数量时也要有策略(截断或者滚动)。做这类样式的时候,我一般会把"显示层"和"数据层"彻底分开:数据层就一个完整的字符串,显示层负责把它切成格子渲染。这样无论输入法怎么输入,只要保证数据层是对的,显示层的渲染逻辑就是纯函数,测试起来也方便。
做这类自定义输入时,字号、字色、行高在微信原生键盘上是不生效的——因为它用的是系统键盘,样式你改不了。真正需要自定义键盘样式的场景,得等小游戏开放对应的能力,或者接受用系统键盘这个前提。
我个人在折腾Unity微信小游戏的输入框这件事上,最大的体会就是:不要用Unity原生的思路去想象它的行为,它本质上是一个"WebGL被剪掉了输入模块、改由平台接口接管"的环境。你只要把心智模型从"InputField自己弹键盘"切换成"我主动调平台接口、手动回填文本",剩下的坑就都是有迹可循的工程问题。另外一个建议是,输入这类强交互的功能,越早真机测越好,别等到功能做完了才发现键盘调不起来,那时候改动的面就大了。如果项目里输入场景比较多,值得在早期就抽一个统一的输入适配层出来,把所有输入框都走同一个入口,后面对齐和排查都会轻松很多。还有个小技巧:给每个输入框在真机上都点一遍,特别是模拟弱网和切换输入法的情况,能提前发现不少只在特定机型上冒出来的怪问题。