PCゲームをスマホで操作！　Siv3D でスマホと HTTP 通信するよ

# 忙しい人へ

完成したものがこちらです。

https://github.com/Raclamusi/ShurikenConnect



# はじめに

みなさん、PCゲームは何で操作しますか？
ほとんどの人がキーボードやマウス、コントローラ（ゲームパッド）と答えるでしょう。
こういった入力装置はゲームの操作感に大きく影響を与えます。

例えば、レースゲームでハンドルコントローラを使用すると、スティックでコントロールするよりも自分が運転していると感覚が増し、没入感が得られるでしょう。

ハンドルコントローラのようなデバイスは一部の人しか持っていないでしょうが、なにか多くの人が持っているものを使ってキーボードやマウスにはない操作をPCゲームに導入したいです（筆者は逆張りがお好き）。

スマホはその目的に最適です。
**ほぼ全員が持っている**うえ、キーボードやマウス、コントローラではできない**タッチ操作ができます**。

スマホゲームではなくわざわざPCゲームにスマホを使う利点として、**キーボードやマウスの操作と組み合わせられる**こと、ゲーム画面はPCにあるので**指で画面が隠れない**ことなどがあります。

この記事では Siv3D で接続したスマホのタッチ情報を取得する機能を作ります。
システムの概要とそれに使われる HTTP や TouchEvent について解説し、作ったものを紹介します。



# システム

## 概要

PC 上の Siv3D アプリとスマホ上のウェブブラウザで通信します。

PC 側は Siv3D の TCP サーバ機能を使ってポートを開き、スマホの接続を待ちます。
スマホ側は開かれたポートにウェブブラウザでアクセスし、 PC から index.html をダウンロードします。
後はこの index.html に書いておいた JavaScript プログラムが逐次タッチイベントの情報を送信し、 Siv3D アプリがそれら受け取ります。

処理の流れは下の図のようになります。

```mermaid
sequenceDiagram
  actor U as ユーザ
  participant SP as スマホ
  participant PC as Siv3D アプリ
  U->>+SP: QR コードスキャン
  SP->>+PC: GET /<br>index.html 要求
  PC-->>-SP: 200 OK<br>index.html 送信
  SP-->>U: 操作画面表示
  SP->>+PC: POST /<br>ロードイベント送信
  PC-->>-SP: 200 OK
  deactivate SP
  U-)+SP: タッチ
  SP->>+PC: POST /<br>タッチイベント送信
  PC-->>-SP: 200 OK
  deactivate SP
  U-)+SP: タッチ
  SP->>+PC: POST /<br>タッチイベント送信
  U-)+SP: タッチ
  U-)+SP: タッチ
  PC-->>-SP: 200 OK
  deactivate SP
  SP->>+PC: POST /<br>タッチイベント×2送信
  PC-->>-SP: 200 OK
  deactivate SP
  deactivate SP
```



## HTTP サーバ

ウェブブラウザで直接 TCP のデータをやりとりすることは（まだ[^tcp]）できません。
通信は HTTP で行う必要があります。

[^tcp]: 将来的にはブラウザで直接 TCP/UDP 通信できるようになるかも？ https://asnokaze.hatenablog.com/entry/2022/01/04/012601

Siv3D には TCP 通信の機能はありますが HTTP 通信の機能はありません。
HTTP サーバは自分で実装する必要があります。

今回のプログラムでは HTTP サーバの必要最低限の機能を実装します。
解説はざっくりなので、詳細な実装はコードを読んでください。

### HTTP リクエストの解析

スマホから index.html の要求やイベント情報などが HTTP リクエストとして送信されます。
これを解析する必要があります。

#### GET リクエスト

ページを読み込む際の GET リクエストの例です。

```http
GET / HTTP/1.1
Host: 192.168.10.11:50000
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: ja-JP,ja;q=0.9,zh-CN;q=0.8,zh;q=0.7,en-US;q=0.6,en;q=0.5

```

はじめの行がステータス行、それ以降は `キー: 値` の形のヘッダが続きます。
最後に空行が入りヘッダの終わりを示します。
ステータス行、ヘッダの改行コードは CRLF であることに注意が必要です。

いろいろなヘッダが送られてきますが、 GET リクエストはステータス行だけ読んでいれば最低限動きます。
ステータス行は HTTP メソッド、リクエスト対象、バージョンが空白区切りで並びます。

今回のプログラムではリクエスト対象が `/` なら index.html を、それ以外なら 404 Not Found を返すようにします。

#### POST リクエスト

イベント情報の送信に用いる POST リクエストの例です。

```http
POST / HTTP/1.1
Host: 192.168.10.11:50000
Connection: keep-alive
Content-Length: 194
User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://192.168.10.11:50000
Referer: http://192.168.10.11:50000/
Accept-Encoding: gzip, deflate
Accept-Language: ja-JP,ja;q=0.9,zh-CN;q=0.8,zh;q=0.7,en-US;q=0.6,en;q=0.5

{"width":412,"height":960,"events":[{"type":"touchstart","touches":[{"id":0,"x":200,"y":436.952392578125,"a":2.095238208770752,"b":2.095238208770752,"angle":0,"force":1}],"changedTouches":[0]}]}
```

ステータス行、ヘッダは GET リクエストと同じ構造ですが、そのあとに本体 (body) が続きます。
本体の形式はヘッダの [`Content-Type`](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Type) に [MIME タイプ](https://developer.mozilla.org/ja/docs/Web/HTTP/MIME_types)で示されます。
この例では JSON 形式のデータです。

POST リクエストで見るべきはヘッダの [`Content-Length`](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Length) の値で、これは本体の長さです。
本体の後にはそのまま次のリクエストが来るので、この長さだけ読む必要があります。
それ以外はたぶん読まなくて大丈夫です。

今回のプログラムでは JSON でやり取りするので、本体を Siv3D の `JSON::Load()` で読み込んで解析します。

### HTTP レスポンスの送信

HTTP リクエストを受け取ったら、 HTTP レスポンスを返す必要があります。
HTTP レスポンスは HTTP リクエストとおおむね同じ構造です。

```http
HTTP/1.0 200 OK
Content-Length: 49
Content-Type: text/html; charset=utf-8
Connection: keep-alive

<html><body><h1>Hello, World!</h1></body></html>
```

ステータス行は HTTP リクエストと異なる形をしていて、バージョン、ステータスコード、ステータスメッセージがこの順に並びます。

ステータスコードは 200 と 400 、404 あたりを用意しておけば大丈夫です。

[`Connection: keep-alive`](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Keep-Alive) は魔法の呪文です。
これをつけて送信することで TCP コネクションがいちいち切断されなくなり、高速なやり取りができるようになります。
必須級のヘッダです。

## タッチイベント

スマホ側の JavaScript では、タッチイベントを取得して POST リクエストで送信します。
リクエストの送信には時間がかかるので、送信中に発生したイベントは溜めておいて、前の送信の完了後に一気に送信します。

### タッチイベントの種類

タッチイベントは4種類あり、それぞれ次のようなものです。

|イベントの種類|説明|
|:------------|:--|
|`touchstart` |新たにタッチされたときに発生|
|`touchmove`  |タッチの位置、範囲、角度、圧力が変化したときに発生|
|`touchend`   |タッチが離れたときに発生|
|`touchcancel`|タッチが取り消されたとき（アラートが出る、ブラウザの UI を触る、タッチ上限を超える、など）に発生|

### タッチから取得できる情報

ウェブブラウザのタッチの情報は [`Touch`](https://developer.mozilla.org/ja/docs/Web/API/Touch) オブジェクトで扱われます。
これに含まれる情報は実はタッチの位置だけではありません。
位置のほかに、**触れている範囲**と**圧力**の情報が含まれています。

位置以外の情報はスマホの機種によって取得できないことがあります。

今回のプログラムではそれらの情報も送信するようにします。

https://x.com/RickByers/status/524692309112131584




# 完成品

完成したものがこちらです。

https://github.com/Raclamusi/ShurikenConnect

ちょっとしたサンプルゲームも付けているので、試してみてください。
Windows 向けのバイナリも置いています。

https://x.com/raclamusi/status/1864791866153300221

![QRコード画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643544/f59aaabb-7834-eaee-08c4-4c70354c2727.png)
![タッチ確認画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643544/23b8eb30-875a-887c-2444-0c99abd9362e.png)
![サンプルゲーム画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643544/21a4cc4a-a4db-70c2-d5af-8f4529d9f9b0.png)


# おわりに

コードは MIT License で公開してあるのでよかったら活用してください。

加速度データも取ろうとしましたが、 HTTP**S** サーバを実装する必要がありました……。



# 参考文献

https://developer.mozilla.org/ja/docs/Web

https://developer.chrome.com/blog/rotationangle-and-touchradius?hl=ja