【ミドルウェア?】スマートフォンをゲームコントローラー化する
お久しぶりですSANAです。
突然ですが、
僕ってWiiとかSwitchのコントローラー振ったりするゲーム好きなんですよね!
まぁ、WiiもSwitchも持ってないですけど👐
ハードを買うのも起動するのも開発するのもハードルが高くてVeryHard(ハードだけに)
ハードや専用コントローラー買いたくないけど
コントローラー振ったりして遊びたいんだあああ
...
あ、スマホ振ればええやん
とまぁそんな訳で作りました
適当な設計
開発者向けにするかプレイヤー向けにするか
ちょっと悩んだんですけど、普通に世の中に出てるPCゲームでも使えたほうが絶対良い。
→中継アプリが必要
懐かしのWindowsForm
PC側の中継アプリはタスクトレイに入れて常駐させたかったので一番手軽なWindowsFormを選択
昔はよく使ってましたねw
スマホアプリ側
脳死でUnityを選択
JoyStickは有名なJoyStickPackを使用致しました。 assetstore.unity.com
スマホ → 中継アプリ
UDP通信を使用しました。
参考サイト
[Unity]非同期処理(Task)の利用例(UDP送受信,SerialPortのRead) - Qiita
送る値は大した量ないので
通信種別 / イベントの種類(ダウン、アップ)/ キーの種類
みたいな文字列送ってます
※キーコードはUnityとWindowsで値が異なる為、WindowsのKeyCode(System.Windows.Forms.Keys)に合わせてます
private void InputDown(InputData data) { StringBuilder sb = new StringBuilder(); switch(data.type){ case Type.KeyCode: sb.Append("k/"); sb.Append(WM_KEYDOWN).Append("/"); int keyInt = (int)data.keyCode; sb.Append(keyInt); break; case Type.Mouse: sb.Append("m/"); if(data.mouse == MouseType.LeftButton){ sb.Append(WM.WM_LBUTTONDOWN); }else if(data.mouse == MouseType.RightButton){ sb.Append(WM.WM_RBUTTONDOWN); } sb.Append("/"); sb.Append(0); break; } sendEvent.Invoke(sb.ToString()); }
中継アプリ → ゲーム
調査した結果SendKeysかSendMessage、PostMessageという選択肢がありました。
しかし、SendKeysは押した情報しか送らず、離したという情報を送る方法がなかったので却下 docs.microsoft.com
/// <summary> /// 受診時イベント /// </summary> /// <param name="text"></param> private void ReceiveEvent(string text) { if (window == IntPtr.Zero) { return; } Invoke(new Action(() => { AddLog(text); })); string[] args = text.Split('/'); switch (args[0]) { case "s": Invoke(new Action(() => { DeviceName.Text = args[1]; })); break; case "k": KeyEvent(args[1], args[2]); break; case "g": GyroEvent(args[1]); break; case "m": MouseEvent(args[1], args[2]); break; case "e": Invoke(new Action(() => { DeviceName.Text = "None"; })); break; } } private void KeyEvent(string typeStr, string keyStr) { int type = int.Parse(typeStr); int key = int.Parse(keyStr); uint repeatCount = 0; uint scanCode = 0x2D; uint extended = 0; uint context = 0; uint previousState = 0; uint transition = 0; uint lParam = 0; // combine the parameters above according to the bit // fields described in the MSDN page for WM_KEYDOWN if (type == 256) { lParam = repeatCount | (scanCode << 16) | (extended << 24) | (context << 29) | (previousState << 30) | (transition << 31); } if (type == 257) { previousState = 1; transition = 1; lParam = repeatCount | (scanCode << 16) | (extended << 24) | (context << 29) | (previousState << 30) | (transition << 31); } PostMessage(window, type, key, lParam); }
参考サイト キーストロークをウインドウに送信 | C#
このlParamってあるじゃないですか
キーボード入力の概要 - Win32 apps | Microsoft Docs
キーストロークメッセージフラグっていう奴らしいんですけど、押したばっかりかどうか?とか色々送ってるみたいなんですよね
ここが一番難所でしたがコピペで解決しました()
参考サイト c# - Setting WM_KEYDOWN lParam parameters - Stack Overflow
送信先のゲーム選択
最初はアクティブな画面に送っていたのですが、送信先が選べないのが不便なの選択式にしました。
private void LoadWindow() { window = GetForegroundWindow(); windowList = new List<IntPtr>(); target.Items.Clear(); foreach (Process p in Process.GetProcesses()) { if (p.MainWindowTitle.Length == 0) continue; if (p.MainWindowHandle == IntPtr.Zero) continue; windowList.Add(p.MainWindowHandle); target.Items.Add($"{p.MainWindowTitle}"); if (window == p.MainWindowHandle) { target.SelectedIndex = target.Items.Count - 1; } } }
マイクラでテスト
JoyStick1つしかないからまだマイクラできない() pic.twitter.com/SuINozTR7m
— SANA@幻想テクノロギア (@sanaftg) 2021年6月26日
案外あっさり動きました。作業時間は6時間弱?
サブのプロジェクトとしてちょっとづつ進めようかなと思ってます~!
アプリ側でキー設定や配置の変更ができるようになったらまだまだ夢広がりそうですね。
では今回は長くなってしまいましたが最後までお付き合い頂きありがとうございました~!