PR

Xbox コントローラーで FFXI マルチボックスのウィンドウを快適に切り替える方法

AutoHotkey

FF11 を複数アカウントで遊んでいると、ウィンドウの切り替えが地味に大変ですよね。キーボードで Alt+Tab するのも戦闘中は焦るし…。

今回は Xbox コントローラーの LB/RB ボタンだけで、複数の pol.exe ウィンドウをサクサク切り替えられる AutoHotkey v2 スクリプトを紹介します。起動時に順番も自分で決められるので、使いやすさ抜群です!


事前準備:FFXI のゲームパッド設定

スクリプトを使う前に、FFXI 側のゲームパッド設定を正しく行う必要があります。

FFXI のゲームパッド設定画面を開いて、以下のように設定してください。

ポイントは右上の**「XInput 有効」にチェックを入れること**です。これがオフになっていると、スクリプトがコントローラーを認識できません。

また、LB(左ショルダー)と RB(右ショルダー)は FFXI 側の機能に割り当てないようにしておくと、誤操作が防げます。


必要なもの

  • AutoHotkey v2.0公式サイト からダウンロード)
  • Xbox コントローラー(有線・無線どちらでも OK)
  • Windows 10 / 11

特徴

  • 🎮 LB で前のウィンドウ、RB で次のウィンドウに切り替え
  • 🔁 ループ対応:最後のウィンドウから RB を押すと最初に戻る
  • 📋 起動時に順番を自分で設定できる
  • 👥 何人でも対応:2キャラでも6キャラでも自動検出
  • 戦闘中でもサクサク動く
  • 🚫 二重入力なし:切り替え中に誤操作しない

スクリプトの使い方

1. ファイルを作成する

メモ帳などのテキストエディタを開いて、下のコードを貼り付けて ff11switch.ahk という名前で保存してください。

ahk

#Requires AutoHotkey v2.0
#SingleInstance Force

polWindows := []
currentIndex := 0
isSwitching := false
orderedWindows := []

ShowOrderGui() {
    global polWindows, orderedWindows
    
    orderedWindows := []
    tempList := []
    
    for hwnd in WinGetList("ahk_exe pol.exe") {
        tempList.Push(hwnd)
    }
    
    if (tempList.Length == 0) {
        MsgBox("pol.exe のウィンドウが見つかりませんでした。")
        ExitApp()
    }
    
    orderGui := Gui("+AlwaysOnTop", "ウィンドウの順番を設定")
    orderGui.Add("Text", "w300", "切り替えたい順番にボタンを押してください。")
    orderGui.Add("Text", "w300", "全部押したら「決定」を押してください。")
    
    statusText := orderGui.Add("Text", "w300", "選択済み: 0 / " tempList.Length)
    
    buttons := []
    for hwnd in tempList {
        title := WinGetTitle("ahk_id " hwnd)
        if (title == "") {
            title := "ウィンドウ " A_Index
        }
        btn := orderGui.Add("Button", "w300", title)
        btn.OnEvent("Click", MakeHandler(hwnd, btn, statusText, tempList.Length))
        buttons.Push(btn)
    }
    
    doneBtn := orderGui.Add("Button", "w300 Disabled", "決定")
    doneBtn.OnEvent("Click", (*) => orderGui.Destroy())
    
    orderGui.Show()
    
    WaitForGui(orderGui, tempList.Length, doneBtn, statusText)
}

MakeHandler(hwnd, btn, statusText, total) {
    return (ctrl, *) => HandleButtonClick(hwnd, btn, statusText, total)
}

HandleButtonClick(hwnd, btn, statusText, total) {
    global orderedWindows
    
    for item in orderedWindows {
        if (item == hwnd) {
            return
        }
    }
    
    orderedWindows.Push(hwnd)
    btn.Enabled := false
    statusText.Text := "選択済み: " orderedWindows.Length " / " total
}

WaitForGui(gui, total, doneBtn, statusText) {
    global orderedWindows
    
    Loop {
        Sleep(100)
        try {
            if (orderedWindows.Length == total) {
                doneBtn.Enabled := true
            }
            if (!gui.Hwnd) {
                break
            }
        } catch {
            break
        }
    }
}

SwitchWindow(direction) {
    global polWindows, currentIndex, isSwitching
    
    if (isSwitching || polWindows.Length == 0) {
        return
    }
    
    isSwitching := true
    
    if (direction == "next") {
        currentIndex := (currentIndex == polWindows.Length) ? 1 : currentIndex + 1
    } else if (direction == "prev") {
        currentIndex := (currentIndex == 1) ? polWindows.Length : currentIndex - 1
    }
    
    targetHwnd := polWindows[currentIndex]
    WinActivate("ahk_id " targetHwnd)
    
    Loop 10 {
        if (WinActive("ahk_id " targetHwnd)) {
            break
        }
        Sleep(10)
    }
    
    isSwitching := false
}

GetXInputState() {
    static buf := Buffer(16, 0)
    
    if (DllCall("xinput1_4.dll\XInputGetState", "uint", 0, "ptr", buf) != 0) {
        return false
    }
    
    return NumGet(buf, 4, "ushort")
}

prevButtons := 0

PollController() {
    global prevButtons
    
    buttons := GetXInputState()
    
    if (buttons == false) {
        prevButtons := 0
        return
    }
    
    if (buttons & 0x0100) && !(prevButtons & 0x0100) {
        SwitchWindow("prev")
    }
    
    if (buttons & 0x0200) && !(prevButtons & 0x0200) {
        SwitchWindow("next")
    }
    
    prevButtons := buttons
}

ShowOrderGui()

polWindows := orderedWindows
currentIndex := 1

if (polWindows.Length > 0) {
    WinActivate("ahk_id " polWindows[1])
}

SetTimer(PollController, 16)

2. FFXI を先に起動しておく

スクリプトを実行する前に、切り替えたいキャラクター分の pol.exe を全部起動しておいてください。

3. スクリプトを実行する

ff11switch.ahk をダブルクリックすると、起動時にこんなウィンドウが表示されます。

切り替えたい順番にキャラクター名のボタンを押していくと、押したボタンがグレーアウトされます。全部押すと「決定」ボタンが有効になるので、押してください。

4. あとはコントローラーで切り替えるだけ!

  • RB → 次のウィンドウ
  • LB → 前のウィンドウ
  • 最後まで行ったら自動でループします

よくある質問

Q. コントローラーがなくても使えますか? A. このスクリプトは Xbox コントローラー専用です。キーボードで切り替えたい場合は別の方法が必要です。

Q. 何キャラまで対応していますか? A. 上限はありません。起動している pol.exe の数だけ自動で検出されます。

Q. スクリプトを終了するにはどうすればいいですか? A. タスクトレイの AutoHotkey アイコンを右クリックして「Exit」を選んでください。


まとめ

戦闘中でもコントローラーの LB/RB でサクサクウィンドウを切り替えられるので、マルチボックスのプレイがかなり快適になります。ぜひ試してみてください!

何か不具合や質問があればコメントまでどうぞ。

コメント