Unity输入法相关(IME)

Posted tiancaiwrk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity输入法相关(IME)相关的知识,希望对你有一定的参考价值。

  在UI上的InputField中, 中文输入法的备选框不会跟随在光标旁边, 造成输入不方便.

  看到有一个相似的, 可是是WebGL的 : https://blog.csdn.net/Rowley123456/article/details/103726927/

  它通过添加html的Input控件的方式来修改备选框位置, 直接跟平台相关了, 不具有泛用性.

  按照这个思路, 直接找Windows的输入控制模块:

    [DllImport("imm32.dll")]
    public static extern IntPtr ImmGetContext(IntPtr hWnd);
    [DllImport("imm32.dll")]
    public static extern int ImmReleaseContext(IntPtr hWnd, IntPtr hIMC);
    [DllImport("imm32.dll")]
    public static extern bool ImmSetCompositionWindow(IntPtr hIMC, ref COMPOSITIONFORM lpCompForm);
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern System.IntPtr GetActiveWindow();

  然后获取窗口句柄, 设置位置的返回都是正确的, 可是结果并没有改变备选框位置:

    void SetInputPos()
    {
        IntPtr hImc = ImmGetContext(GetWindowHandle());
        COMPOSITIONFORM cf = new COMPOSITIONFORM();
        cf.dwStyle = 2;
        cf.ptCurrentPos.X = 500;
        cf.ptCurrentPos.Y = 500;
        bool setcom = ImmSetCompositionWindow(hImc, ref cf);    // setcom == true
        ImmReleaseContext(GetWindowHandle(), hImc);
    }// 结构体略

  这就比较尴尬了, 设置没有反应没有报错......

  考虑到Unity应该有各个平台的底层接口的, 以实现标准化的输入(IME接口), 所以在BaseInputModule里面去找一找, 发现它下面有个BaseInput组件:

//StandaloneInputModule : PointerInputModule
//PointerInputModule : BaseInputModule
public abstract class BaseInputModule : UIBehaviour
{
    protected BaseInput m_InputOverride;
    //
    // 摘要:
    //     The current BaseInput being used by the input module.
    public BaseInput input { get; }
    
    ......
}

  这个跟输入貌似有关系, 看到里面的变量跟Windows的API有点像:

public class BaseInput : UIBehaviour
{
    public BaseInput();

    //
    // 摘要:
    //     Interface to Input.imeCompositionMode. Can be overridden to provide custom input
    //     instead of using the Input class.
    public virtual IMECompositionMode imeCompositionMode { get; set; }
    //
    // 摘要:
    //     Interface to Input.compositionCursorPos. Can be overridden to provide custom
    //     input instead of using the Input class.
    public virtual Vector2 compositionCursorPos { get; set; }
    
    ......
}

  估计只要继承它自己设置compositionCursorPos就能达到效果了, 直接创建一个继承类型, 然后通过反射的方式给StandaloneInputModule设定BaseInput:

    [RequireComponent(typeof(InputField))]
    public class IME_InputFollower : BaseInput
    {
        public InputField inputField;
        public override Vector2 compositionCursorPos
        {
            get
            {
                return base.compositionCursorPos;
            }
            set
            {
                base.compositionCursorPos = new Vector2(200,200);
            }
        }
        
        // 在inputField focus/unfocus的时候调用即可
        private static void SetCurrentInputFollower(IME_InputFollower target)
        {
            var inputModule = EventSystem.current.currentInputModule;
            if(inputModule)
            {
                var field = inputModule.GetType().GetField("m_InputOverride", BindingFlags.Instance | BindingFlags.NonPublic);
                if(field != null)
                {
                    field.SetValue(inputModule, target);
                    if(target)
                    {
                        target.inputField.OnPointerDown(new PointerEventData(EventSystem.current));
                        if(string.IsNullOrEmpty(target.inputField.text) == false)
                        {
                            target.inputField.caretPosition = target.inputField.text.Length;
                        }
                    }
                }
            }
        }
    }

  当InputField被focus的时候, SetCurrentInputFollower使用反射的方式设定BaseInput到当前的InputModule中, 然后手动触发一下OnPointerDown和设定光标位置, 这样就能刷新输入法窗口了, 不会因为切换InputField而窗口不跟随.

  这样这个逻辑应该就是可以在全部平台中跑了, 只要添加compositionCursorPos的set逻辑就行了, 在测试时发现在Windows下compositionCursorPos的计算方法是窗口坐标, 并且起始坐标为窗口坐上角(0, 0), 不知道是不是DX平台的特点. 而平台的差异只要在计算坐标中注意即可(不过除了Windows也没其他需要的平台了).

  还有就是在编辑器下窗口的大小为Game窗口的大小, 而不是渲染部分的大小, 所以在编辑器下窗口大小与渲染不同的时候计算位置是不对的.

 技术图片

 

以上是关于Unity输入法相关(IME)的主要内容,如果未能解决你的问题,请参考以下文章

检测后退按钮但不关闭对话框片段

文本和输入:创建一个IME:简介

将 IME 放入自定义文本框中,从 Control 派生

浅谈Android输入法(IME)架构

unity中怎样用代码获取物体组件中的相关属性

C# 和 IME - 获取当前输入文本