凹みTips

C++、JavaScript、Unity、ガジェット等の Tips について雑多に書いています。

Unity でペンタブの入力を受け取れるアセットを作ってみた

はじめに

Cintiq をお借りしたので、Unityペンタブ情報を受け取れる uWintab というアセットを作成してみました。Windows 限定になります。

github.com

デモ

f:id:hecomi:20170611135725g:plain

解説

Windows でペンタブの情報を取得するためには Wintab という API 仕様を利用できます。Wacom 製品のドライバをインストールすると自動的に Wintab32.dll が System 以下に展開されます。uWintab ではこの DLL を C++ 側でよしなに処理する層を作成し、C# 層では最低限の情報のやり取りで済むように設計しています。もろもろの処理は別スレッドで行っているので、VR などの用途でも負荷は低いと思います。

C# だけでやる方法もありますが、メンテや上述の速度的な観点から C++ 側で実装することにしました。

使い方

セットアップ

Release ページから最新の .unitypackage をダウンロードし、プロジェクトにインポートして下さい。

uWintab > Script > Tablet コンポーネントを適当なオブジェクトにアタッチして下さい。後はここ経由でペンタブの情報が取得できるようになります。

メンバ

Tablet コンポーネントのメンバは以下のような感じです。

  • string deviceName
  • string version
    • Wintab のバージョン(最新は v1.4)
  • bool isPressureSupported
    • 筆圧をサポートしているか
  • bool isWheelSupported
    • ホイールをサポートしているか
  • bool isOrientationSupported
    • ペンの向きをサポートしているか
  • bool isExpKeysSupported
    • ファンクションキーをサポートしているか
  • int deviceNum
  • int expKeyNum
    • ファンクションキーの数
  • float x
    • 入力の X 座標
  • float y
    • 入力の Y 座標
  • float pressure
    • 筆圧
  • float wheel
    • ホイールの入力
  • float azimuth
    • ペンの傾き(方位)
  • float altitude
    • ペンの傾き(倒し具合)
  • float twist
    • 軸回転(私は使えてないですが 4D マウス用?)
  • int penId
    • ペンの ID(0〜)
  • CursorType cursor
    • カーソル種別(通常: Pen、裏側: TailSwitch など)
  • int time
    • 最後にイベントを取得した時間
  • bool proximity
  • bool GetButton(int id)
    • ペンのボタンが押されているか(0 は無し)
  • bool GetButtonDown(int id)
    • ペンのボタンが押されたタイミングか
  • bool GetButtonUp(int id)
    • ペンのボタンを離したタイミングか
  • bool GetExpKey(int id)
    • ファンクションキー(e.g. タブレット側のボタン)が押されたか
  • bool GetExpKeyDown(int id)
    • ファンクションキーが押されたタイミングか
  • bool GetExpKeyUp(int id)
    • ファンクションキーが離されたタイミングか

サンプルコード

例えばペンの位置や傾きをオブジェクトに反映させるコードはこんな感じです。スクリプトExamples ディレクトリ下にあります。

using UnityEngine;

namespace uWintab
{

public class Pen : MonoBehaviour 
{
    [SerializeField]
    Vector2 localScale = new Vector2(1.92f, 1.08f);

    [SerializeField]
    float hoverHeight = 0.1f;

    Tablet tablet_;
    float y_ = 0f;

    void Start()
    {
        tablet_ = FindObjectOfType<Tablet>();
    }

    void Update()
    {
        UpdatePosition();
        UpdateRotation();
    }

    void UpdatePosition()
    {
        var x = (tablet_.x - 0.5f) * localScale.x;
        var z = (tablet_.y - 0.5f) * localScale.y;
        y_ = Mathf.Lerp(y_, (tablet_.proximity ? 0f : hoverHeight), 0.2f);
        transform.localPosition = new Vector3(x, y_, z);
    }

    void UpdateRotation()
    {
        var yaw = tablet_.azimuth * 360f;
        var pitch = (0.5f - tablet_.altitude) * 180f;
        var rot = Quaternion.Euler(pitch, yaw, 0f);
        transform.localRotation = Quaternion.FromToRotation(Vector3.up, rot * Vector3.up);
    }
}

}

制約

現状、Unity エディタまたはビルド済みのアプリが最前面にないと入力は受け取れません。uDesktopDuplication と組み合わせて Photoshop でお絵描き…、とかしたかったのですが、Wintab の仕様により単一のコンテキストにしかイベントが送信されないためです。最前面にあるウィンドウのメッセージをフックすれば取れる可能性もあると思うので、別の機会に挑戦してみます。

おわりに

まだ機能は少ないですが、要望を貰えれば(ペンタブを返却するまでの間は)改善をしていきたいと思いますのでよろしくお願いします。