忙しい人へ
完成したものがこちらです。
はじめに
みなさん、PCゲームは何で操作しますか?
ほとんどの人がキーボードやマウス、コントローラ(ゲームパッド)と答えるでしょう。
こういった入力装置はゲームの操作感に大きく影響を与えます。
例えば、レースゲームでハンドルコントローラを使用すると、スティックでコントロールするよりも自分が運転していると感覚が増し、没入感が得られるでしょう。
ハンドルコントローラのようなデバイスは一部の人しか持っていないでしょうが、なにか多くの人が持っているものを使ってキーボードやマウスにはない操作をPCゲームに導入したいです(筆者は逆張りがお好き)。
スマホはその目的に最適です。
ほぼ全員が持っているうえ、キーボードやマウス、コントローラではできないタッチ操作ができます。
スマホゲームではなくわざわざPCゲームにスマホを使う利点として、キーボードやマウスの操作と組み合わせられること、ゲーム画面はPCにあるので指で画面が隠れないことなどがあります。
この記事では Siv3D で接続したスマホのタッチ情報を取得する機能を作ります。
システムの概要とそれに使われる HTTP や TouchEvent について解説し、作ったものを紹介します。
システム
概要
PC 上の Siv3D アプリとスマホ上のウェブブラウザで通信します。
PC 側は Siv3D の TCP サーバ機能を使ってポートを開き、スマホの接続を待ちます。
スマホ側は開かれたポートにウェブブラウザでアクセスし、 PC から index.html をダウンロードします。
後はこの index.html に書いておいた JavaScript プログラムが逐次タッチイベントの情報を送信し、 Siv3D アプリがそれら受け取ります。
処理の流れは下の図のようになります。
HTTP サーバ
ウェブブラウザで直接 TCP のデータをやりとりすることは(まだ1)できません。
通信は HTTP で行う必要があります。
Siv3D には TCP 通信の機能はありますが HTTP 通信の機能はありません。
HTTP サーバは自分で実装する必要があります。
今回のプログラムでは HTTP サーバの必要最低限の機能を実装します。
解説はざっくりなので、詳細な実装はコードを読んでください。
HTTP リクエストの解析
スマホから index.html の要求やイベント情報などが HTTP リクエストとして送信されます。
これを解析する必要があります。
GET リクエスト
ページを読み込む際の GET リクエストの例です。
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 リクエストの例です。
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
に MIME タイプで示されます。
この例では JSON 形式のデータです。
POST リクエストで見るべきはヘッダの Content-Length
の値で、これは本体の長さです。
本体の後にはそのまま次のリクエストが来るので、この長さだけ読む必要があります。
それ以外はたぶん読まなくて大丈夫です。
今回のプログラムでは JSON でやり取りするので、本体を Siv3D の JSON::Load()
で読み込んで解析します。
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
は魔法の呪文です。
これをつけて送信することで TCP コネクションがいちいち切断されなくなり、高速なやり取りができるようになります。
必須級のヘッダです。
タッチイベント
スマホ側の JavaScript では、タッチイベントを取得して POST リクエストで送信します。
リクエストの送信には時間がかかるので、送信中に発生したイベントは溜めておいて、前の送信の完了後に一気に送信します。
タッチイベントの種類
タッチイベントは4種類あり、それぞれ次のようなものです。
イベントの種類 | 説明 |
---|---|
touchstart |
新たにタッチされたときに発生 |
touchmove |
タッチの位置、範囲、角度、圧力が変化したときに発生 |
touchend |
タッチが離れたときに発生 |
touchcancel |
タッチが取り消されたとき(アラートが出る、ブラウザの UI を触る、タッチ上限を超える、など)に発生 |
タッチから取得できる情報
ウェブブラウザのタッチの情報は Touch
オブジェクトで扱われます。
これに含まれる情報は実はタッチの位置だけではありません。
位置のほかに、触れている範囲と圧力の情報が含まれています。
位置以外の情報はスマホの機種によって取得できないことがあります。
今回のプログラムではそれらの情報も送信するようにします。
完成品
完成したものがこちらです。
ちょっとしたサンプルゲームも付けているので、試してみてください。
Windows 向けのバイナリも置いています。
おわりに
コードは MIT License で公開してあるのでよかったら活用してください。
加速度データも取ろうとしましたが、 HTTPS サーバを実装する必要がありました……。
参考文献
-
将来的にはブラウザで直接 TCP/UDP 通信できるようになるかも? https://asnokaze.hatenablog.com/entry/2022/01/04/012601 ↩