> ## 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](/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 游戏串流后端服务需要进行配置，以便您的游戏在部署到零售环境时能够接收触控事件。</Note>

原生触控通过 [IGameInputReading::GetTouchState](/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

- [触控入门](/zh-CN/build/core-features/common/game-streaming/game-streaming-getting-started-with-touch.md)
- [构建触控的设计师指南](/zh-CN/build/core-features/common/game-streaming/building-touch-layouts/game-streaming-tak-designers-guide.md)
- [XBOX Cloud Gaming 触控与流式传输支持](/zh-CN/build/core-features/common/game-streaming/index.md)
- [使用 GDKX 构建并运行你的第一款主机游戏](/zh-CN/home/build-first-title/first-console-title-walkthrough.md)
- [内容测试应用 (CTA) 串流配置概述](/zh-CN/build/core-features/common/game-streaming/game-streaming-content-test-application-stream-config.md)
