> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 使用 IGameInput 建置原生觸控介面

> 使用 IGameInput 為您的遊戲建置原生觸控介面

當玩家將您的遊戲串流到其行動裝置時，提升遊戲樂趣的最佳方式之一，就是讓他們能夠使用觸控控制項與您的遊戲互動。XBOX 遊戲串流支援使用 [Touch Adaptation Kit](https://docs.xbox.com/zh-TW/build/core-features/common/game-streaming/game-streaming-touch-touch-adaptation-kit-overview.md) 在遊戲上方疊加觸控控制項，對於遊戲中許多以虛擬遊戲手把遊玩是自然方式的畫面而言，這會是很好的選擇。不過，對於遊戲中的選單、地圖、物品欄畫面等部分，直接使用觸控輸入與遊戲互動可能更為自然。

例如，玩家很自然地會想點選選單選項來與其互動，或想使用手機手勢來放大和捲動地圖。Microsoft Game Development Kit (GDK) 提供的 API 可讓您在遊戲中建置這種原生觸控介面，使其感覺像是真正的行動體驗。

Touch Adaptation Kit 與原生觸控並非互斥：您可以在遊戲中同時使用兩者，在最適合您遊戲的地方使用各種形式的觸控輸入。例如，使用原生觸控選單，同時在主要遊戲迴圈中使用 Touch Adaptation Kit，可能是建構遊戲觸控輸入策略的合理方式。

> [!NOTE]
>
> 如果您正在建置使用原生觸控的遊戲，請通知您的客戶經理。必須設定 XBOX Game Streaming 後端服務，您的遊戲部署到零售環境時才能接收觸控事件。

原生觸控是透過 [IGameInputReading::GetTouchState](https://docs.xbox.com/zh-TW/reference/input/gameinput/deprecated/interfaces/igameinputreading/methods/igameinputreading_gettouchstate.md) API 提供給您的遊戲。此 API 會提供目前的觸控點集合，代表玩家目前用來觸碰螢幕的手指集合。

若要將這些觸控點轉換為按下、移動和放開事件，在每個畫面之間追蹤其狀態會很有幫助。

* 任何在前一個畫面中不存在、但在目前畫面中存在的觸控點，都是新的觸控按下。
* 任何在前一個畫面中存在、且在目前畫面中仍然存在的觸控點，若觸控的座標已變更即為移動，否則為無作業。
* 在前一個畫面中存在、但已不再存在的觸控點，則為觸控放開。

建置這些按下、移動和放開事件的非常基本輸入迴圈，可能如下所示：

```cpp theme={null}
struct TouchPoint
{
    // GameInputTouchState.touchId corresponding to this point
    uint64_t Id = 0;    

    // X pixel being touched
    uint32_t X = 0;

    // Y pixel being touched
    uint32_t Y = 0;

    // Is this entry in the array of touch points currently being used to track a touch
    bool IsInUse = false;

    // Is this TouchPoint tracking a finger which is currently touching the screen
    bool IsTouchActive = false;
};

// Touch points currently being tracked
std::array<TouchPoint, 10> g_touchPoints = {};

extern IGameInput* g_gameInput;
extern uint32_t g_frameWidth;
extern uint32_t g_frameHeight;

void TouchPointPressed(const TouchPoint& touchPoint);
void TouchPointMoved(const TouchPoint& touchPoint);
void TouchPointReleased(const TouchPoint& touchPoint);


void ProcessTouchInput()
{
    // Reset the active touch state from the previous frame
    for (TouchPoint& touchPoint : g_touchPoints)
    {
        touchPoint.IsTouchActive = false;
    }

    // Process any new touch points for this frame
    Microsoft::WRL::ComPtr<IGameInputReading> reading = nullptr;
    if (SUCCEEDED(g_gameInput->GetCurrentReading(GameInputKindTouch, nullptr, &reading)))
    {
        uint32_t touchCount = reading->GetTouchCount();
        if (touchCount > 0)
        {
            std::vector<GameInputTouchState> touchStates(touchCount);
            touchCount = reading->GetTouchState(touchCount, touchStates.data());

            for (const GameInputTouchState& touchState : touchStates)
            {
                const uint32_t x = static_cast<uint32_t>(touchState.positionX * g_frameWidth);
                const uint32_t y = static_cast<uint32_t>(touchState.positionY * g_frameHeight);

                // Check to see if we are already tracking this touch point
                auto existingPoint = std::find_if(std::begin(g_touchPoints), std::end(g_touchPoints), [id = touchState.touchId](const TouchPoint& point)
                {
                    return point.IsInUse && point.Id == id;
                });

                if (existingPoint != g_touchPoints.end())
                {
                    // We were already tracking the point - it is still alive this frame, but it may have
                    // also moved position.
                    existingPoint->IsTouchActive = true;
                    if (existingPoint->X != x || existingPoint->Y != y)
                    {
                        existingPoint->X = x;
                        existingPoint->Y = y;
                        TouchPointMoved(*existingPoint);
                    }
                }
                else
                {
                    // This is a new touch point. Start tracking it and treat it as a press
                    auto insertPoint = std::find_if(std::begin(g_touchPoints), std::end(g_touchPoints), [](const TouchPoint& point)
                    {
                        return !point.IsInUse;
                    });

                    if (insertPoint != std::end(g_touchPoints))
                    {
                        insertPoint->Id = touchState.touchId;
                        insertPoint->X = x;
                        insertPoint->Y = y;
                        insertPoint->IsInUse = true;
                        insertPoint->IsTouchActive = true;

                        TouchPointPressed(*insertPoint);
                    }
                }
            }
        }

        // Look for any points which were pressed last frame but are no longer pressed this frame
        // and treat those as touch releases
        for (TouchPoint& touchPoint : g_touchPoints)
        {
            if (touchPoint.IsInUse && !touchPoint.IsTouchActive)
            {
                TouchPointReleased(touchPoint);
                touchPoint = TouchPoint{};
            }
        }
    }
}
```


## Related topics

- [開始使用觸控](https://docs.xbox.com/zh-TW/build/core-features/common/game-streaming/game-streaming-getting-started-with-touch.md)
- [建置觸控控制項的設計師指南](https://docs.xbox.com/zh-TW/build/core-features/common/game-streaming/building-touch-layouts/game-streaming-tak-designers-guide.md)
- [XBOX Cloud Gaming 觸控與串流支援](https://docs.xbox.com/zh-TW/build/core-features/common/game-streaming/index.md)
- [IGameInputDevice (介面內容)](https://docs.xbox.com/zh-TW/reference/input/gameinput/interfaces/igameinputdevice/igameinputdevice.md)
- [IGameInputReading (介面內容)](https://docs.xbox.com/zh-TW/reference/input/gameinput/interfaces/igameinputreading/igameinputreading.md)
