> ## 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.

# 從 XInput 移植到 GameInput

> 從 XInput 移植到 GameInput

<a id="introductionSection" />

在所有舊版 API 中，從 XInput 移植到 GameInput 是最不複雜的。這是因為 GameInput 深受 XInput 簡單 (且易於使用) 的程式設計模型影響，因此許多 XInput API 都能與 GameInput 中的對等函式 1:1 對應。

<a id="keyDifferencesSection" />

## 主要差異

下列各節將討論 XInput 與 GameInput 之間的主要差異。

<a id="cVsCppSection" />

### C 與 C++

XInput API 是一組一般 C 函式。另一方面，GameInput 是 C++，並使用介面 (就像圖形和音訊 API 一樣)。實際上，這不會讓使用 GameInput API 的程式碼變得複雜，也不會影響效能，而且一旦您更熟悉 GameInput 的運作方式，就會發現它具有一些優點。

請務必了解，雖然這些介面看起來可能像 COM，但它們並不是。使用這些介面只需要對參考計數有基本的了解。如需詳細資訊，請參閱 [GameInput 基本概念的介面一節](/zh-TW/build/core-features/common/input/overviews/input-fundamentals#interfacesSection)主題。

<a id="gettingInputSection" />

### 取得輸入

在 XInput 中，大多數遊戲會逐一查看使用者索引，直到找到具有已連線裝置的索引，然後從該裝置讀取狀態。遊戲通常會記住使用者索引，以便下次不需要再逐一查看。例如，下列程式碼是遊戲提示使用者在控制器上「按下 A」的典型程式碼：

```c++ theme={null}
// This function looks for a gamepad that currently has the "A" button pressed.
void FindActiveGamepad()
{
    for (DWORD index = 0; index < XUSER_MAX_COUNT; index++)
    {
        XINPUT_STATE state;
        if (XInputGetState(index, &state) == ERROR_SUCCESS)
        {
            if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A)
            {
                // Found the user's gamepad at this index.
            }
        }
    }
}
```

在 GameInput 中，您會先在不指定裝置的情況下取得輸入，然後可以視需要查詢輸入來自哪個裝置。程式碼看起來很類似，但不需要明確的裝置列舉，可以產生更簡單的演算法。

```c++ theme={null}
// This function looks for a gamepad that currently has the "A" button pressed.
void FindActiveGamepad(IGameInput * gameInput)
{
    // This checks for input from all gamepads simultaneously.
    IGameInputReading * reading;
    if (SUCCEEDED(gameInput->GetCurrentReading(GameInputKindGamepad, nullptr, &reading)))
    {
        GameInputGamepadState state;
        reading->GetGamepadState(&state);

        if (state.buttons & GameInputGamepadA)
        {
            // Found the user's gamepad.  At this point we can
            // get the device that generated this input, and then
            // pass that into future calls to the GetCurrentReading
            // method to receive input only from that gamepad.
        }

        reading->Release():
    }
}
```

此程式碼不像 XInput 那麼簡單，但非常相似。當您更熟悉 GameInput API 時，就會了解此模型如何提供 XInput 中所沒有的強大輸入處理選項。

另外值得注意的是，XInput 會以 **BYTE** 類型傳回扳機的類比值，並以 **SHORT** 類型傳回搖桿的類比值。使用 GameInput API 時，這些類比值會以 0 到 1 (扳機) 或 -1 到 1 (搖桿) 的 **float** 值傳回。

<a id="rumbleFeedbackSection" />

### 震動回饋

在 XInput 中，遊戲只需呼叫`XInputSetState` 即可將震動命令傳送至裝置。在 GameInput 中，遊戲需要取得裝置的 [IGameInputDevice](/zh-TW/reference/input/gameinput/interfaces/igameinputdevice/igameinputdevice) 執行個體，然後呼叫其 [SetRumbleState](/zh-TW/reference/input/gameinput/interfaces/igameinputdevice/methods/igameinputdevice_setrumblestate) 方法。這兩種方法的使用方式相似。這是您會呼叫裝置介面上的函式，而不只是將其作為裝置識別碼的一個範例。

<a id="appFocusSection" />

### 應用程式焦點

在主機上，GameInput 只會在應用程式取得焦點時提供輸入給該應用程式。否則，傳回的狀態會包含中性或「靜止」值，就像使用者完全沒有碰觸裝置一樣。這樣就不需要額外的輸入程式碼來處理焦點變更 (例如呼叫`XInputEnable`)。

在 PC 上，輸入預設會傳送至所有處理序。未來，您將可以使用 [SetFocusPolicy](/zh-TW/reference/input/gameinput/interfaces/igameinput/methods/igameinput_setfocuspolicy) 方法變更此行為。

<a id="xinputWrapperSection" />

## XInputOnGameInput 包裝函式

Microsoft Game Development Kit (GDK) 隨附一個名為`XInputOnGameInput.h` 的標頭檔，其中包含在 GameInput 之上實作的 XInput API。我們建議直接移植到 GameInput，尤其是當您需要鍵盤和滑鼠或其他輸入裝置時。不過，XInputOnGameInput 包裝函式可用來協助啟動初步的移植工作，而不需要對現有的 XInput 程式碼進行任何變更。

若要使用 XInputOnGameInput 包裝函式，只需將此程式碼：

```c++ theme={null}
#include <XInput.h>
```

取代為此程式碼：

```c++ theme={null}
#include <XInputOnGameInput.h>
using namespace XInputOnGameInput;
```

然後重新編譯您的程式碼。

XInput 包裝函式程式碼的實作完全位於標頭檔中，因此也可以將其作為使用 GameInput API 的範例來檢視，及/或視需要加以修改。

<a id="wrapperDifferencesSection" />

### XInput 與 XInputOnGameInput 之間的差異

一般而言，XInputOnGameInput 包裝函式可直接取代舊版 XInput API。不過，仍有一些細微差異：

* 為了簡化，包裝函式中只撰寫了遊戲台裝置支援。如果您需要支援其他裝置，例如賽車方向盤或大型機台搖桿，請直接使用 GameInput，或在 XInputOnGameInput 程式碼中新增對這些裝置的支援。

* 包裝函式只會在遊戲取得焦點時傳回遊戲台輸入。當遊戲未取得焦點時，傳回的任何遊戲台狀態都會設為中性或「靜止」值，就像使用者沒有碰觸遊戲台一樣。無論是否呼叫`XInputEnable`，都會如此處理。

* `XUSER_MAX_COUNT` 的值已從 4 增加為 8。對於大多數舊版 XInput 程式碼而言，這通常是透明的。不過，請務必仔細檢閱程式碼中對`XInputGetKeystroke` 函式的任何使用，以協助確保沒有任何程式碼寫死假設`XINPUT_KEYSTROKE` 結構的`UserIndex` 成員中傳回的最大值為 4。否則，可能會發生緩衝區溢位。

* 已新增一些新函式 (請參閱下文)，只有在您打算在生產程式碼中繼續使用 XInput 包裝函式時，這些函式才會有用。

<a id="productionWrapperUseSection" />

### 在生產程式碼中使用 XInputOnGameInput

XInputOnGameInput 包裝函式以高效能且無鎖定的方式撰寫，繼承了 GameInput API 的所有效能最佳化，因此適合在生產程式碼中使用。它也繼承了 GameInput 更廣泛的裝置支援 (例如熱門的 HID 遊戲台)，並在 API 中新增下列新函式：

* **`XInputSetStateEx`** 與`XInputSetState` 類似，但新增了對扳機馬達的支援。

* **`XInputGetStateWithToken`** 與`XInputGetState` 類似，但允許呼叫端提供 D3DX 畫面管線權杖，將特定輸入讀數與圖形畫面產生關聯，以便稍後在 PIX 中進行分析。

  > \[!NOTE]
  > 在 May Preview 版本中，`XInputGetStateWithToken` 目前的行為與`XInputGetState` 相同，因為基礎的 GameInput 程式碼尚未完全實作。

* **`XInputGetDeviceId`** 會傳回指定使用者索引之裝置的`APP_LOCAL_DEVICE_ID`。將此識別碼傳遞至 [IGameInput](/zh-TW/reference/input/gameinput/interfaces/igameinput/igameinput) 上的 [FindDeviceFromId](/zh-TW/reference/input/gameinput/interfaces/igameinput/methods/igameinput_finddevicefromid) 方法，會傳回該使用者索引對應的 [IGameInputDevice](/zh-TW/reference/input/gameinput/interfaces/igameinputdevice/igameinputdevice)。然後可以使用它來存取 GameInput API 中未透過 XInput 包裝函式公開的其他功能。

<a id="optimizingSection" />

#### 最佳化包裝函式程式碼

根據預設，XInputOnGameInput 包裝函式會設定為可直接相容於舊版 XInput API。不需要 100% 相容行為的遊戲，可以透過定義下列任一前置處理器巨集，微調包裝函式的行為和效能：

##### XINPUT\_ON\_GAMEINPUT\_EXPLICIT\_INITIALIZATION

根據預設，XInput 包裝函式會在第一次呼叫任何包裝函式時，自動延遲初始化基礎的 GameInput API。這可確保與現有 XInput 程式碼的直接相容性，但有幾個小缺點：

1. 第一次呼叫 XInput 包裝函式的執行時間會比平常長。

2. 每個 XInput 包裝函式在每次呼叫時，都必須檢查是否已執行延遲初始化。這只是對全域變數的簡單測試，因此分支預測器應該能降低成本，但仍是額外的負擔。

3. 雖然它應該永遠不會失敗，但無法得知基礎 GameInput API 的延遲初始化是否成功。

4. 在 XInput 包裝函式的全域變數清除之前 (由於模組卸載或處理序終止)，基礎的 [IGameInput](/zh-TW/reference/input/gameinput/interfaces/igameinput/igameinput) 執行個體不會釋放。

遊戲可以透過定義`XINPUT_ON_GAMEINPUT_EXPLICIT_INITIALIZATION` 巨集，手動控制包裝函式的初始化和關閉。這會新增兩個新函式`XInputOnGameInputInitialize` 和`XInputOnGameInputUninitialize`，可以呼叫它們來精確控制初始化和關閉的時機。

##### XINPUT\_ON\_GAMEINPUT\_NO\_XINPUTENABLE

實作`XInputEnable` 函式所需的程式碼，會為每次呼叫`XInputGetState`、`XInputGetStateWithToken`、`XInputSetState`、`XInputSetStateEx` 和`XInputGetKeystroke` 函式增加額外負擔。如果您的程式碼不會呼叫`XInputEnable`，或可以輕鬆從程式碼中移除，定義`XINPUT_ON_GAMEINPUT_NO_XINPUTENABLE` 巨集將會移除對`XInputEnable` 的支援及其帶來的相關負擔。基礎的 GameInput 程式碼無論如何都會在焦點變更時自動執行`XInputEnable` 的功能，因此大多數遊戲在可能的情況下都會想要定義此巨集。

##### XINPUT\_ON\_GAMEINPUT\_NO\_XINPUTGETKEYSTROKE

實作`XInputGetKeystroke` 函式所需的程式碼，會在包裝函式的實作中新增一些額外的函式和變數。它不會為任何其他 XInput API 函式增加負擔，但如果您的程式碼不會呼叫`XInputGetKeystroke`，您可以定義`XINPUT_ON_GAMEINPUT_NO_XINPUTGETKEYSTROKE` 巨集，稍微減少 XInput 包裝函式的程式碼/資料大小。

<a id="seeAlsoSection" />

## 參考 API 文件

* [GameInput (API 內容)](/zh-TW/reference/input/gameinput/gameinput_members)
* [XInputOnGameInput (API 內容)](/zh-TW/reference/input/xinputongameinput/xinputongameinput_members)

## 另請參閱

[GameInput 概觀](/zh-TW/build/core-features/common/input/overviews/input-overview)

[GameInput API 參考](/zh-TW/reference/input/gc-reference-input-toc)

[Microsoft Game Development Kit](/zh-TW/services/playfab/sdks/platforms/gdk)


## Related topics

- [移植到 XBOX GDK 的指南](/zh-TW/home/build-first-title/porting-guides.md)
- [从 XInput 移植到 GameInput](/zh-CN/build/core-features/common/input/porting/input-porting-xinput.md)
- [将现有输入代码移植到 GameInput](/zh-CN/build/core-features/common/input/porting/index.md)
- [GameInput 常见问题](/zh-CN/build/core-features/common/input/overviews/input-faq.md)
- [从 Windows.XBOX.Input 移植到 GameInput](/zh-CN/build/core-features/common/input/porting/input-porting-wxi.md)
