Windows Phoneからジョイスティックを作成する

少しの背景。


画像 ある晩、息子が私のところに来て、マリオをやりたいと言った。 夏には、祖母のコテージで、彼は雨天で「切り倒す」のが好きでした。 そして、窓のすぐ外です。 ためらうことなく、8ビットコンソールの最初のエミュレーターと彼に出会ったゲームをダウンロードしました。 しかし、キーボードを演奏する楽しさは完全に異なることが判明しました。 ジョイスティックを買いに行くのは遅すぎた。 そして、私はあなたがそれなしでできると思った。 手元には古いノキアLumiaがあり、そのサイズと形状はニーズにほぼ一致していました。 ジョイスティックを書くことにしました。 息子は箱の中の紙にデザインを描きに行き、父親はコーヒーをれに行って、このアイデアを最短時間で実装する方法を考えました。

私は(私の見地からは)抵抗が最も少ない道を辿ることにしました。 プレフィックスエミュレータは、設定で押されたボタンを指定する必要があります。つまり、アプリケーションはボタンを押す必要があります。 古き良きWINAPIを使用して、ボタンを押すことをエミュレートできます。

究極のアイデアは、クライアントサーバーアプリケーションでした。 クライアント(電話)は、ボタンが押されると、サーバーに要求を送信します。サーバーは、到着したものに応じて、キーボードボタンを押すまたは放すことをエミュレートします。 通信はソケット経由です。 すべてが単純なようです。 始めましょう。

サーバー側


同じ名前のtextBoxでテキストボックスフォームに配置します。 その中で、私たちは電話から来るものを示します。

画像

ソケットの使用を開始します。

最初にそれらを接続します:

using System.Net; using System.Net.Sockets; 

すべてが入るソケットとバッファを取得します。

 public partial class ServerForm : Form { private Socket _serverSocket, _clientSocket; private byte[] _buffer; 

サーバーを起動する関数を作成しています。

 private void StartServer() { try { _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _serverSocket.Bind(new IPEndPoint(IPAddress.Any, 3333)); _serverSocket.Listen(0); _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } 

それに応じて、最初から開始します。

  public ServerForm() { InitializeComponent(); StartServer(); } 

作品をキーボードに接続し、いくつかの機能を記述します。1つはキーを押し、もう1つはそれをリリースします。

 [ DllImport("user32.dll")] private static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); private const int KEYEVENTF_EXTENDEDKEY = 1; private const int KEYEVENTF_KEYUP = 2; public static void KeyDown(Keys vKey) { keybd_event((byte)vKey, 0, KEYEVENTF_EXTENDEDKEY, 0); } public static void KeyUp(Keys vKey) { keybd_event((byte)vKey, 0, KEYEVENTF_KEYUP, 0); } 

何かを取得しようとし、すべてが正常な場合は、受け取ったものをtextBoxに追加し、ボタンを押します(離します)。

 private void ReceiveCallback(IAsyncResult AR) { try { int received = _clientSocket.EndReceive(AR); Array.Resize(ref _buffer, received); string text = Encoding.ASCII.GetString(_buffer); //     AppendToTextBox(text); // ------------------ Array.Resize(ref _buffer, _clientSocket.ReceiveBufferSize); _clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null); } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } 

到着したものに応じて、ボタンの押し下げをエミュレートする機能:

 private void AppendToTextBox(string text) { MethodInvoker invoker = new MethodInvoker(delegate { string text_before = text; string exitW = text; // if (text == "a") { KeyUp(Keys.D); KeyDown(Keys.A); textBox.Text += text + " "; } // if (text == "a1" ) { KeyUp(Keys.A); textBox.Text += text + " "; } }); this.Invoke(invoker); } 

テスト中に、「進む」ボタンが不明な理由で固執することがあることがわかりました。 この場合、プレーヤーは「戻る」を反射的に押し、速度を落とそうとします。 このような機会を得るために、「戻る」を押すと、念のため、「進む」ボタンを上げます。

  if (text == "a") { KeyUp(Keys.D); KeyDown(Keys.A); textBox.Text += text + " "; } 

ボタンが貼り付いている理由は明らかではありません。

クライアント部


もっとマリオをプレイしたかったので、接続を設定して同じ画面で接続することにしました。
上部に、IPアドレスとポート、接続ボタン、ステータスを示すテキストブロックを入力するためのフィールドを配置しました。 標準のAppBarButtonはコントロールボタンとして使用され、息子自身が自分のデザインに従って配置しました。 彼自身の考えで、標準的な背景を放棄することが決定されました。 デザインが完成しました。 それをすべて機能させるためにやるべきことが残っています。

画像

ソケットを切断します。

 using Windows.Networking.Sockets; using Windows.Networking; using Windows.Storage.Streams; 

新しいソケットを開きます。

  StreamSocket clientSocket = new StreamSocket(); 

接続しようとしています:

 private async void btnConnect_Click(object sender, RoutedEventArgs e) { HostName host = new HostName(textBoxIP.Text); string port = textBoxPort.Text; if (connected) { StatusText.Text = " "; return; } try { StatusText.Text = "  ..."; await clientSocket.ConnectAsync(host, port); connected = true; StatusText.Text = " " + Environment.NewLine; } catch (Exception exception) { if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) { throw; } StatusText.Text = "   : "; closing = true; clientSocket.Dispose(); clientSocket = null; } } 

サーバーにデータを送信します。

 private async void sendkey(string key) { if (!connected) { StatusText.Text = " "; return; } try { StatusText.Text = "   ..."; DataWriter writer = new DataWriter(clientSocket.OutputStream); writer.WriteString(key); await writer.StoreAsync(); StatusText.Text = " " + Environment.NewLine; writer.DetachStream(); writer.Dispose(); } catch (Exception exception) { if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) { throw; } StatusText.Text = "    "; closing = true; clientSocket.Dispose(); clientSocket = null; connected = false; } } 

突然問題が発生しました。 ボタンのClickイベントは、押された時点では発生しませんが、クリック後にリリースされた時点で発生します。 msdnを少し掘り下げた後、クリックとしてGotFocusを使用し、リリースボタンとしてClickを使用することが決定されました。その後、同じ目的でPointerReleasedイベントが追加されました(これにより、一方のボタンを押しながらもう一方のボタンを離すことができました)。 ボタンを放しても、フォーカスはそれのままです。これを避けるために、他の要素に渡します。この場合、btnConnectという名前の管理に関与しないボタンです。

 //   // private void gotFocusUp(object sender, RoutedEventArgs e) { sendkey("w"); } private void lostFocusUp(object sender, RoutedEventArgs e) { sendkey("w1"); btnConnect.Focus(FocusState.Programmatic); } private void lostFocusUp(object sender, PointerRoutedEventArgs e) { sendkey("w1"); btnConnect.Focus(FocusState.Programmatic); } 

結果


画像

長所:マリオは走ってジャンプし、王女を助けます。 息子は喜んでいます。

さらなる計画:

1.電話でサーバー検索を行います。
2.スティックに対処します(仲間は、スティックはリアリズムを追加し、バグではなく機能を追加すると言います)。
3.デザインを再描画します。
4.一緒にプレイする機能を追加する
5.コードをコームします。 (サーバー側のifの量と、各ボタンがクライアントで独自のほぼ同一のイベントを持っているという事実が気に入らない)

gitHubに興味がある人のためのリンク。

Source: https://habr.com/ru/post/J274271/


All Articles