なおしのこれまで、これから

学んだこと・感じたこと・やりたいこと

Viveコントローラーでマウスを動かす (Unity)

f:id:vxd-naoshi-19961205-maro:20200705163234j:plain



始めに

前回の記事です。

shitakami.hatenablog.com


VTuberで勉強会をする際にDiscordをいじったりしたかったので、Viveコントローラーでマウスを動かす機能を作成しました。

あと、マウスの入力に関する記事はたくさんありますが逆にマウスを動かす内容の記事があまりなかったのでブログでまとめようと思います。



dllファイルをPluginsフォルダに入れる

マウスを動かすためにSystem.Windows.Forms.dllファイルとSystem.Drawing.dllファイルをUnityプロジェクトのAssets/Pluginsに入れる必要があります。


実はこれらのファイルはUnityのフォルダの中に入っております。

私の場合は\Unity\Hub\Editor\2019.2.6f1\Editor\Data\Mono\lib\mono\2.0 にありました。


これらのファイルを先程説明した通り、プロジェクトのAssets/Pluginsの中にコピーしてください。 dllファイルを入れることで、これらをスクリプトで使用することが出来ます。

f:id:vxd-naoshi-19961205-maro:20200705004003j:plain




キーボードでマウス操作する

始めに、キーボードでマウスを操作してみます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;

public class TestMouseController : MonoBehaviour
{

    [SerializeField]
    private int m_mouseSpeed;

    [DllImport("user32.dll")]
    static extern void mouse_event(int dwFlag, int dx, int dy, int cButtons, int dwExtraInfo);

    private const int m_mouse_leftDown = 0x2;
    private const int m_mouse_leftUp = 0x4;
    private const int m_mouse_rightDown = 0x8;
    private const int m_mouse_rightUp = 0x10;

    // Update is called once per frame
    void Update()
    {
        
        int x = System.Windows.Forms.Cursor.Position.X;
        int y = System.Windows.Forms.Cursor.Position.Y;


        if(Input.GetKey(KeyCode.RightArrow)) 
            x += m_mouseSpeed;
        if(Input.GetKey(KeyCode.LeftArrow))
            x -= m_mouseSpeed;

        if(Input.GetKey(KeyCode.UpArrow))
            y -= m_mouseSpeed;
        if(Input.GetKey(KeyCode.DownArrow))
            y += m_mouseSpeed;


        System.Windows.Forms.Cursor.Position = new System.Drawing.Point(x, y);
        
        

        if(Input.GetKeyDown(KeyCode.Space)) {
            mouse_event(m_mouse_leftDown, 0, 0, 0, 0);
            Debug.Log("左クリック、Down");

        }
        else if(Input.GetKeyUp(KeyCode.Space)) {
            mouse_event(m_mouse_leftUp, 0, 0, 0, 0);
            Debug.Log("左クリック、Up");
        }

        if(Input.GetKeyDown(KeyCode.R)) {
            mouse_event(m_mouse_rightDown, 0, 0, 0, 0);
            Debug.Log("右クリック、Down");
        }
        else if(Input.GetKeyUp(KeyCode.R)) {
            mouse_event(m_mouse_rightUp, 0, 0, 0, 0);
            Debug.Log("右クリック、Up");
        }

        

    }
}


もし、コンパイルエラーになっていた際はdllファイルがちゃんと入っているかを確認してください。



マウスの移動

初めにマウスの移動について解説します。

System.Windows.Forms.Cursor.Posiotionから現在のマウスの座標を取得できます。 Xは横方向、Yは縦方向となります。

int x = System.Windows.Forms.Cursor.Position.X;
int y = System.Windows.Forms.Cursor.Position.Y;



注意する点として、マウスの原点 (0, 0) は画面の左上になります。

f:id:vxd-naoshi-19961205-maro:20200705012536p:plain



あとは軸を合わせてマウスの座標を計算してSystem.Windows.Forms.Cursor.Positionに代入します。

        if(Input.GetKey(KeyCode.RightArrow)) 
            x += m_mouseSpeed;
        if(Input.GetKey(KeyCode.LeftArrow))
            x -= m_mouseSpeed;

        if(Input.GetKey(KeyCode.DownArrow))
            y += m_mouseSpeed;
        if(Input.GetKey(KeyCode.UpArrow))
            y -= m_mouseSpeed;


        System.Windows.Forms.Cursor.Position = new System.Drawing.Point(x, y);




クリック操作

マウスのクリックイベントを行うために、dllファイルの関数を呼び出す必要があります。 そこでDllImportを行います。

    [DllImport("user32.dll")]
    static extern void mouse_event(int dwFlag, int dx, int dy, int cButtons, int dwExtraInfo);

DllImportについてはUnity公式を参照してください。(ネイティブプラグイン - Unity マニュアル



あとはこの関数の第一引数にイベントの種類を表す値を入れて呼び出すことでマウスの左クリックや右クリックが出来ます。

    . . . . . 
    private const int m_mouse_leftDown = 0x2;
    private const int m_mouse_leftUp = 0x4;
    private const int m_mouse_rightDown = 0x8;
    private const int m_mouse_rightUp = 0x10;
    . . . . .

        if(Input.GetKeyDown(KeyCode.Space)) {
            mouse_event(m_mouse_leftDown, 0, 0, 0, 0);
            Debug.Log("左クリック、Down");

        }
        else if(Input.GetKeyUp(KeyCode.Space)) {
            mouse_event(m_mouse_leftUp, 0, 0, 0, 0);
            Debug.Log("左クリック、Up");
        }

        if(Input.GetKeyDown(KeyCode.R)) {
            mouse_event(m_mouse_rightDown, 0, 0, 0, 0);
            Debug.Log("右クリック、Down");
        }
        else if(Input.GetKeyUp(KeyCode.R)) {
            mouse_event(m_mouse_rightUp, 0, 0, 0, 0);
            Debug.Log("右クリック、Up");
        }



マウスイベントの種類についてはこちらの記事を参考にしています。

nonsoft.la.coocan.jp




Viveコントローラーでマウスを動かす

次にViveコントローラーでマウスを動かすプログラムです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;

public class MouseController : MonoBehaviour
{

    [SerializeField]
    private GameObject m_controller;

    [SerializeField]
    private float m_mouseSpeed;

    private IControllerInput m_controllerInput;

    private Vector2 m_lastMousePosition;

    [DllImport("user32.dll")]
    static extern void mouse_event(int dwFlag, int dx, int dy, int cButtons, int dwExtraInfo);

    private const int m_mouse_leftDown = 0x2;
    private const int m_mouse_leftUp = 0x4;
    private const int m_mouse_rightDown = 0x8;
    private const int m_mouse_rightUp = 0x10;

    // Start is called before the first frame update
    void Start()
    {
        m_controllerInput = m_controller.GetComponent<IControllerInput>();
    }

    // Update is called once per frame
    void Update()
    {
        
        CheckMouseLeftClick();

        CheckMouseRightClick();

        MouseCursorMove();

    }


    private void CheckMouseLeftClick() {

        if(m_controllerInput.IsTriggerDown())
            mouse_event(m_mouse_leftDown, 0, 0, 0, 0);
        else if(m_controllerInput.IsTriggerUp())
            mouse_event(m_mouse_leftUp, 0, 0, 0, 0);

    }

    private void CheckMouseRightClick() {

        if(m_controllerInput.IsOptionDown())
            mouse_event(m_mouse_rightDown, 0, 0, 0, 0);
        else if(m_controllerInput.IsOptionUp())
            mouse_event(m_mouse_rightUp, 0, 0, 0, 0);

    }


    private void MouseCursorMove() {

        if(m_controllerInput.IsTouchpadTouch()) {

            m_lastMousePosition = m_controllerInput.GetTouchPadAxis();

        }
        else if(m_controllerInput.IsTouchpadTouching()) {

            var newMousePosition = m_controllerInput.GetTouchPadAxis();
            var touchpadVector = newMousePosition - m_lastMousePosition;

            touchpadVector *= m_mouseSpeed;

            float x = System.Windows.Forms.Cursor.Position.X;
            float y = System.Windows.Forms.Cursor.Position.Y;

            x += touchpadVector.x;
            y -= touchpadVector.y;

            System.Windows.Forms.Cursor.Position = new System.Drawing.Point((int)x, (int)y);

            m_lastMousePosition = newMousePosition;

        }


    }

}




Viveコントローラーを使用すると言いながら、このプログラムではインターフェースを使ってコントローラーからの入力を受け取っています。

    . . . . . . .
    [SerializeField]
    private GameObject m_controller;
    . . . . . . .
    . . . . . . .

    // Start is called before the first frame update
    void Start()
    {
        m_controllerInput = m_controller.GetComponent<IControllerInput>();
    }


現在はViveコントローラーを使用していますが、後々Oculus Touch等に切り替わる可能性があるのでインターフェースでコントローラー入力をラッピングしました。

基本的にはこれら記事を参考にしてコントローラーの入力を受け取っています。

framesynthesis.jp

qiita.com



マウスのクリック処理

マウスの左クリックと右クリックはViveコントローラーのトリガーとメニューボタンを使用しています。ボタンを押し込んだときにマウスを押し込んだイベントを呼び出し、ボタンを離した際にマウスを離したイベントを呼び出しています。

    private void CheckMouseLeftClick() {

        if(m_controllerInput.IsTriggerDown())
            mouse_event(m_mouse_leftDown, 0, 0, 0, 0);
        else if(m_controllerInput.IsTriggerUp())
            mouse_event(m_mouse_leftUp, 0, 0, 0, 0);

    }

    private void CheckMouseRightClick() {

        if(m_controllerInput.IsOptionDown())
            mouse_event(m_mouse_rightDown, 0, 0, 0, 0);
        else if(m_controllerInput.IsOptionUp())
            mouse_event(m_mouse_rightUp, 0, 0, 0, 0);

    }




マウスの移動処理

マウスの移動は、タッチパッドに振れている座標の変化を使って実装しています。

タッチパッドに触れた瞬間はその触れた座標を保存しています。

    if(m_controllerInput.IsTouchpadTouch()) {

            m_lastMousePosition = m_controllerInput.GetTouchPadAxis();

    }



次にタッチパッドに触れている状態で現在の座標を取得して、前フレームの座標との差を求めます。

  else if(m_controllerInput.IsTouchpadTouching()) {

            var newMousePosition = m_controllerInput.GetTouchPadAxis();
            var touchpadVector = newMousePosition - m_lastMousePosition;



後は現在のマウスの座標にその差分を加えて移動させます。基本的にタッチパッドの座標は 0 ~ 1 の浮動小数になっており、逆にマウスの座標は整数になっているので計算する際はfloat型で行い代入する際はint型にキャストしています。

マウスの座標計算が終わったら現在のタッチパッドの座標を更新します。

            touchpadVector *= m_mouseSpeed;

            float x = System.Windows.Forms.Cursor.Position.X;
            float y = System.Windows.Forms.Cursor.Position.Y;

            x += touchpadVector.x;
            y -= touchpadVector.y;

            System.Windows.Forms.Cursor.Position = new System.Drawing.Point((int)x, (int)y);

            m_lastMousePosition = newMousePosition;




デモ

HMDを付けながらだと画面の確認が出来ないので、こちらを使用させて頂きました。

tips.hecomi.com

https://youtu.be/2G5FVXoSrxw


Viveコントローラーでマウスを動かす [Unity]



動画では行っていませんが、ドラッグアンドドロップも可能です。



まとめ

Unityの機能というより、C#の機能を使うことでマウス操作を行うことが出来ました。個人的に勉強会で使用してみて特に大きな不満はありませんが、いずれはレーザーポインターみたくマウスを操作したいなと思っております。