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

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

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

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

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

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

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

原生觸控是透過 [IGameInputReading::GetTouchState](/zh-TW/reference/input/gameinput/deprecated/interfaces/igameinputreading/methods/igameinputreading_gettouchstate) 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

- [XBOX Series X|S 總覽](/zh-TW/paths/series-xs/overview.md)
- [使用 IGameInput 构建原生触控界面](/zh-CN/build/core-features/common/game-streaming/game-streaming-native-touch-igameinput.md)
- [Windows 與 ROG XBOX Ally 總覽](/zh-TW/paths/windows/overview.md)
- [使用 GDKX 建置並執行你的第一個主機遊戲](/zh-TW/home/build-first-title/first-console-title-walkthrough.md)
- [触控入门](/zh-CN/build/core-features/common/game-streaming/game-streaming-getting-started-with-touch.md)
