ペンの操作とハプティクス (触覚) のフィードバック

Windows には長期的にサポートされているデジタル ペンがあります。これにより、ユーザーは自然かつ直接的な方法でデバイスを操作し、デジタル インクを使用してリッチな書き込みと描画を行うことによって創造性を表現できます。

Windows 11 では、デジタル ペンのエクスペリエンスをより自然で説得力のあるものにする新しい機能が導入されています。"ハプティクス フィードバック" をサポートするペンを使用すると、ユーザーは、アプリのユーザー インターフェイス (UI) を使用して触覚的な方法でペンを操作することができます。

注意

この新機能に言及する場合、開発者 API および関連ドキュメント全体で "ハプティクス" が使用されます。"触覚" は、Windows の設定でフィードバックを設定するためにユーザーに表示されるフレンドリ名です。

Windows 11 でサポートされているハプティクス フィードバック エクスペリエンスには、"インク フィードバック" と "相互作用フィードバック" が含まれます。

  • インク フィードバックは、ペンが画面と接触している間に連続的な振動を使用して、さまざまな種類の手書きツールまたは描画ツール (ペン、マーカー、鉛筆、蛍光ペンなど) の感覚をシミュレートします。 既定では、Windows Ink プラットフォームは、すべての描画ツールでハプティクス フィードバックをサポートしています (このトピックでは、Windows Ink でサポートされているもの以外のカスタム インク ソリューションを提供する方法について説明します)。
  • 一方、相互作用フィードバックは、ボタンの上への移動やボタンのクリックなどの主要なユーザー操作、アクションの完了に対する応答に基づいているか、ユーザーの注意を引くための、直接的なフィードバックです。

通常、ハプティクス フィードバックを完全にサポートするには、次の 5 つの手順を実行する必要があります。

  • ペン入力を検出します。
  • 現在のペンとデバイスでハプティクス フィードバックがサポートされているかどうかを判断し、サポートされている場合はハプティクス フィードバック機能を確認します。
  • 送信するハプティクス フィードバック信号を決定します。
  • ハプティクス フィードバックを送信します。
  • ハプティクス フィードバックを停止します。

ペン入力を検出する

ペン入力を検出して分離するには、最初に PointerEntered イベントに登録してから、PointerDeviceTypepen であるかどうかを確認する必要があります。

次のコードは、PointerEntered イベント内でポインター デバイスの種類を確認する方法を示しています。 この例では、入力がペンからのものではない場合、単にイベント ハンドラーから戻ります。 それ以外の場合は、ペン機能を確認し、ハプティクス フィードバックを構成します。


private void InputObserver_PointerEntered(object sender, PointerRoutedEventArgs e)
{
    ...
    
    // If the current Pointer device is not a pen, exit.
    if (e.Pointer.PointerDeviceType != PointerDeviceType.Pen) 
    {
       return;
    }
    
    ...    
}

ハプティクス フィードバックのサポートを確認する

すべてのペンとデジタイザーでハプティクス フィードバックがサポートされているわけではありません。また、このトピックで説明されているハプティクス フィードバック機能をすべてサポートしているとは限りません。 そのため、アクティブなペンでサポートされている機能をプログラムによって確認することが重要です。

前の例の続きとして、アクティブなペンがハプティクス フィードバックをサポートしているかどうかを確認する方法を示します。

まず、現在の PointerId から、PenDevice オブジェクトの取得を試みます。 PenDevice を取得できない場合は、単にイベント ハンドラーから戻ります。

PenDevice を取得した場合は、SimpleHapticsController プロパティがサポートされているかどうかをテストします。 そうでない場合も、単にイベント ハンドラーから戻ります。

// Attempt to retrieve the PenDevice from the current PointerId.
penDevice = PenDevice.GetFromPointerId(e.Pointer.PointerId);

// If a PenDevice cannot be retrieved based on the PointerId, it does not support 
// advanced pen features, such as haptic feedback. 
if (penDevice == null)
{
    return;
}

// Check to see if the current PenDevice supports haptic feedback by seeing if it 
// has a SimpleHapticsController.
hapticsController = penDevice.SimpleHapticsController;
if (hapticsController == null)
{
    return;
}

前の例で取得した SimpleHapticsController は、ハプティクス機能を照会し、ハプティクス フィードバックを送信/停止するために次の例で使用されます。

注意

Windows App SDK Preview 1.0 を使用してアプリをビルドしている場合は、PenDevice interop (PenDeviceInterop.FromPointerPoint(PointerPoint)) を使用してシステムの PenDevice にアクセスできます。

private void InputObserver_PointerEntered(PointerInputObserver sender, PointerEventArgs args)
{
    var penDevice = PenDeviceInterop.PenDeviceFromPointerPoint(args.CurrentPoint);
}

以下のセクションでは、ハプティクス ペンがサポートする必要があるフィードバック機能と、オプションのフィードバック機能について説明します。 必須のハプティクス フィードバックの種類は、通常、オプション機能ではなくフォールバックとして使用できます。

インクの波形

ペンが画面と接触している間は、インクの波形が連続して再生され、さまざまな手書きツールまたは描画ツールの感覚をシミュレートしようとします。

機能 説明 必須/省略可能
InkContinous 波形 物理的なボールペンを使用したインクの感覚をシミュレートします。 これは、ハプティクス ペンでインク波形がサポートされていない場合の既定のフォールバックです。 必須
BrushContinuous 波形 ユーザーがインク ツールとしてブラシを選択したときの連続的なハプティクス信号。 オプション
ChiselMarkerContinuous 波形 ユーザーがインク ツールとしてチゼル マーカー/蛍光ペンを選択したときの連続的なハプティクス信号。 オプション
EraserContinuous 波形 ユーザーがインク ツールとして消しゴムを選択したときの連続的なハプティクス信号。 オプション
GalaxyContinuous 波形
(HID ドキュメントと実装ガイドでは、この波形を スパークルContinuous と参照しています)
多色のブラシなど、特殊なインク ツールの連続的なハプティクス信号。 オプション
MarkerContinuous 波形 ユーザーがインク ツールとしてマーカーを選択したときの連続的なハプティクス信号。 オプション
PencilContinuous 波形 ユーザーがインク ツールとして鉛筆を選択したときの連続的なハプティクス信号。 オプション

相互作用波形

相互作用波形は通常は短い (例外については次の表で説明されています)、直接的なフィードバックの波形です。ボタンの上への移動やボタンのクリックなどの主要な操作、アクションの完了に対する応答を確認するか、ユーザーの注意を引くために、必要に応じて生成されます。

機能 説明 必須/省略可能
Click 波形 短い "クリック" フィードバック。 これは、アプリによって選択された相互作用波形がハプティクス ペンでサポートされていない場合の既定のフォールバックです。 必須
Error 波形 操作が失敗したこと、またはエラーが発生したことをユーザーに警告するための強い信号です。 オプション
Hover 波形 ユーザーが対話型 UI 要素の上に移動したことを示します。 オプション
Press 波形 ユーザーがインクリメンタル アクションで対話型 UI 要素を押したことを示します (Release を参照)。 オプション
Release 波形 ユーザーがインクリメンタル アクションで対話型 UI 要素を解放したことを示します (Press を参照)。 オプション
Success 波形 操作が成功したことをユーザーに警告するための強い信号です。 オプション
BuzzContinuous 波形 連続するブーンという音。 オプション
RumbleContinuous 波形 連続する重低音。 オプション

ハプティクス フィードバックのカスタマイズ

一部のハプティクス ペンは、次のカスタマイズをサポートしています。

機能 説明 必須/省略可能
強度 ハプティクス信号の強度を設定します。 オプション
再生カウント 指定した回数、ハプティクス信号を繰り返します。 オプション
再生の一時停止間隔 繰り返される各ハプティクス信号の再生間隔を設定します。 オプション
再生時間 ハプティクス信号が再生される間隔を設定します。 オプション

カスタム設定のサポートを確認する

強度、再生カウント、再生の一時停止間隔、再生時間のサポートを確認するには、SimpleHapticsController の次のプロパティを使用します。

インクのハプティクス フィードバックの送信と停止

SimpleHapticsController オブジェクトの SendHapticFeedback メソッドを使用して、インクの波形をユーザーのペンに渡します。 このメソッドは、波形またはカスタマイズされた強度値を持つ波形の両方の受け渡しをサポートしています (「ハプティクス フィードバックをカスタマイズする」を参照してください)。

ペンの端が画面に触れたらすぐにその波形の再生を開始するようにペンを構成するには、SendHapticFeedback を呼び出し、インクの波形を渡します。 ペンが持ち上げられるか、StopFeedback が呼び出されるまで (いずれかが先に発生するまで)、波形の再生が継続されます。 これは、ハプティクスを再生する要素の PointerEntered イベント ハンドラーで実行することをお勧めします。 たとえば、カスタム インク実装を持つアプリでは、そのインク キャンバスの PointerEntered メソッドでこれを行います。

目的のインクの波形を取得するには、SimpleHapticsControllerSupportedFeedback コレクションを反復処理して、アクティブなペンでサポートされていることを確認する必要があります。

サポートされていない場合は、何も再生しないか、サポートが保証されている InkContinuous 波形に戻すかのいずれかを選択できます。

次の例では、BrushContinuous 波形を送信します (BrushContinuous がサポートされていない場合は InkContinuous に戻します)。

SimpleHapticsControllerFeedback currentWaveform;

// Attempt to set the currentWaveform to BrushContinuous.
foreach (var waveform in hapticsController.SupportedFeedback)
{
    if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.BrushContinuous)
    {
        currentWaveform = waveform;
    }
} 

// If currentWaveform is null, it was not in the SupportedFeedback collection, so instead set 
// the waveform to InkContinuous.
if (currentWaveform == null)
{
    foreach (var waveform in hapticsController.SupportedFeedback)
    {
        if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.InkContinuous)
        {
            currentWaveform = waveform;
        }
    }
}

// Send the currentWaveform 
hapticsController.SendHapticFeedback(currentWaveform);

関連付けられているポインターが、ハプティクス フィードバック用に登録した要素から出たときには、ハプティクス フィードバックも停止することが重要です。 それ以外の場合、波形はアクティブなペンで再生を試み続けます。

注意

ペンが画面の範囲を離れるときに、必要に応じて単独でハプティクスを停止するペンもあります。 ただし、すべてのペンでこれを行う必要はありません。そのため、ここで説明するように、アプリケーションでは常に明示的にハプティクス フィードバックを停止する必要があります。

要素に対するハプティクス フィードバックを停止するには、ハプティクス信号を送信した PointerEntered ハンドラーを登録したのと同じ要素で PointerExited イベントに登録します。 終了したイベント ハンドラーで、次に示すように StopFeedback を呼び出します。

hapticsController.StopFeedback();

相互作用フィードバックを送信および停止する

相互作用フィードバックの送信は、インク フィードバックの送信と非常に似ています。

SimpleHapticsController オブジェクトの SendHapticFeedback メソッドを使用して、相互作用波形をユーザーのペンに渡します。 このメソッドは、波形またはカスタマイズされた強度値を持つ波形の両方の受け渡しをサポートしています (「ハプティクス フィードバックをカスタマイズする」を参照してください)。

(インク フィードバックの場合のように、ペンの端が画面に触れたときではなく) アプリ内の何らかの操作に基づいて、すぐに波形を再生し始めるようにペンを構成するには、SendHapticFeedback を呼び出し、インクの波形を渡します。

連続しない相互作用波形を使用する場合は、対応する StopFeedback 呼び出しを行う必要はありません。 連続的な相互作用波形に対しては StopFeedback を呼び出す必要があります。

注意

インクの波形が再生されているときに相互作用波形を送信すると、インクの波形は一時的に中断されます。 相互作用波形が停止すると、インクの波形が再開されます。

目的の相互作用波形を取得するには、SimpleHapticsControllerSupportedFeedback コレクションを反復処理して、アクティブなペンでサポートされていることを確認する必要があります。

サポートされていない場合は、何も再生しないか、サポートが保証されている Click 波形に戻すかのいずれかを選択できます。

次の例では、Error 波形を送信します (Error がサポートされていない場合は Click に戻します)。

SimpleHapticsControllerFeedback currentWaveform;  

// Attempt to set the currentWaveform to BrushContinuous.
foreach (var waveform in hapticsController.SupportedFeedback)
{
    if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.Error)
    {
        currentWaveform = waveform;
    }
} 

// If currentWaveform is null, it was not in the SupportedFeedback collection, so instead set 
// the waveform to Click.
if (currentWaveform == null)
{
    foreach (var waveform in hapticsController.SupportedFeedback)
    {
        if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.Click)
        {
            currentWaveform = waveform;
        }
    }
} 

// Send the currentWaveform.
hapticsController.SendHapticFeedback(currentWaveform); 

ハプティクス フィードバックをカスタマイズする

ハプティクス フィードバックをカスタマイズするには、3 つの方法があります。 1 つ目はインク フィードバックと相互作用フィードバックの両方でサポートされていますが、2 つ目と 3 つ目は相互作用フィードバックでのみサポートされています。

  1. システムの最大強度設定を基準に、フィードバックの強度を調整します。 これを行うには、まず SimpleHapticsController が強度の設定をサポートしていることを確認してから、目的の Intensity の値で SendHapticFeedback を呼び出す必要があります。

    if (hapticsController.IsIntensitySupported) 
    {
        foreach (var waveform in hapticsController.SupportedFeedback)
        {
            if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.Click)
            {
                double intensity = 0.75;
                hapticsController.SendHapticFeedback(waveform, intensity);
            }
        }
    }
    
  2. 指定した回数、ハプティクス信号を繰り返します。 これを行うには、まず SimpleHapticsController が強度の設定をサポートしていることを確認してから、目的のカウントの値で SendHapticFeedbackForPlayCount を呼び出す必要があります。 強度と再生の一時停止間隔の両方を設定できます。

    注意

    SimpleHapticsController が強度または再生の一時停止間隔の設定をサポートしていない場合、指定された値は無視されます。

    if (hapticsController.IsPlayCountSupported && hapticsController.IsIntensitySupported && hapticsController.IsReplayPauseIntervalSupported)
    {
        foreach (var waveform in hapticsController.SupportedFeedback)
        {
            if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.Click)
            {
                double intensity = 0.75;
                int playCount = 3;
                System.TimeSpan pauseDuration = new System.TimeSpan(1000000);
                hapticsController.SendHapticFeedbackForPlayCount(currentWaveform, intensity, playCount, pauseDuration);
            }
        }
    }
    
  3. ハプティクス信号の継続時間を設定します。 これを行うには、まず SimpleHapticsController が再生時間の設定をサポートしていることを確認してから、目的の時間間隔の値で SendHapticFeedbackForDuration を呼び出す必要があります。 強度を設定することもできます。

    注意

    SimpleHapticsController が強度の設定をサポートしていない場合、指定された値は無視されます。

    if (hapticsController.IsPlayDurationSupported && hapticsController.IsIntensitySupported)
    {
        foreach (var waveform in hapticsController.SupportedFeedback)
        {
            if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.RumbleContinuous)
            {
                double intensity = 0.75;
                System.TimeSpan playDuration = new System.TimeSpan(5000000);
                hapticsController.SendHapticFeedbackForDuration(currentWaveform, intensity, playDuration);
            }
        }
    }
    

次の機能の動作例については、ペン ハプティクス サンプルを参照してください。