펜 상호 작용 및 햅틱(촉각) 피드백

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 파형 실제 볼펜으로 입력하는 느낌을 모방합니다. 햅틱 펜에서 수동 입력 파형을 지원하지 않을 때 기본 대안입니다. Required
BrushContinuous 파형 사용자가 브러시를 수동 입력 도구로 선택할 때 연속 햅틱 신호입니다. 선택 사항
ChiselMarkerContinuous 파형 사용자가 각진 선 마커/형광펜을 수동 입력 도구로 선택할 때 연속 햅틱 신호입니다. 선택 사항
EraserContinuous 파형 사용자가 지우개를 수동 입력 도구로 선택할 때 연속 햅틱 신호입니다. 선택 사항
GalaxyContinuous 파형
(HID 설명서 및 구현 가이드는 다음과 같이 이 파형을 SparkleContinuous라고 합니다.
다중 색 브러시와 같은 특수 잉크 도구의 연속 햅틱 신호입니다. 선택 사항
MarkerContinuous 파형 사용자가 마커를 수동 입력 도구로 선택할 때 연속 햅틱 신호입니다. 선택 사항
PencilContinuous 파형 사용자가 연필을 수동 입력 도구로 선택할 때 연속 햅틱 신호입니다. 선택 사항

상호 작용 파형

상호 작용 파형은 일반적으로 짧은(예외는 다음 표에 설명) 직접 피드백 파형이며 요청 시 마우스로 단추 가리키기 또는 클릭, 작업 완료에 응답, 사용자의 주의 끌기 등의 주요 작업을 확인하기 위해 생성됩니다.

기능 설명 필수 / 선택
Click 파형 짧은 "클릭" 피드백입니다. 앱에서 선택한 상호 작용 파형이 햅틱 펜에서 지원되지 않을 때 기본 대안입니다. Required
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); 

햅틱 피드백 사용자 지정

햅틱 피드백을 사용자 지정하는 방법에는 세 가지가 있습니다. 첫 번째는 수동 입력 및 상호 작용 피드백에서 모두 지원되고, 두 번째와 세 번째는 상호 작용 피드백에서만 지원됩니다.

  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);
            }
        }
    }
    

예제

다음 기능의 작업 예제는 펜 햅틱스 샘플을 참조하세요.