マルチプレイゲームの作り方のメモ【Django, Vue】

499, 2022-06-21

目次

マルチプレイゲームの作り方のメモ

DjangoとVueを使ってブラウザでプレイできるマルチプレイゲームを開発している。
Djangoではchannelsを使ってソケット通信をしている。
開発3日ほどでほとんど出来上がった。

ゲームの内容は戦車が弾を撃ち合うものだ。
複数のプレイヤーが同時に参加できて、リアルタイムなマルチプレイを実現する。

この記事ではこのゲーム開発で得られたノウハウをメモすることにする。

関連記事



マルチプレイゲームの基本は何か?

マルチプレイゲームの基本はチャットにあると思う。
チャットが作れるならあとはゲームプログラミングが出来ればマルチプレイゲームは作れる。

つまりチャットについて理解していないとそれを発展させた物は作れないということになる。

私が考えるチャットの基本は↓になる。

  • ユーザーがメッセージをソケット通信でサーバーに投稿する

  • サーバーがソケットを使ってそのメッセージを他のユーザーに送信する

これがチャットの基本である。
ほとんどのチャットはこの基本ロジックで出来ているはずである。

このチャットの基本をマルチプレイゲームの基本に変換すると↓になる。

  • プレイヤーが自身の状態を更新する

  • プレイヤーがーサーバーに自身の更新したデータをソケット通信で送信する

  • サーバーがデータを受け取り他のプレイヤーにそのデータを送信する

これが今作っているマルチプレイゲームの基本ロジックになる。

ゲームプログラムのロジックは?

マルチプレイゲームでは他のプレイヤーの状態を通信で得る必要がある。
そのためにソケット通信を利用する。
今回作っているゲームではWebSocketというものを使っている。

ゲームプログラムが基本的にすることは↓になる。

  • プレイヤーの状態の更新とそのデータの送信

  • ソケットで受信したデータを現在のゲームに反映させる

プレイヤーの状態の更新というのはたとえばキーが押されたらプレイヤーを移動させるというものだ。
この操作ではプレイヤーの座標が更新される。

そうするとその更新した座標は他のプレイヤーは知らないから、その座標を他のプレイヤーに通知する必要がある。
そのためソケットでサーバーに座標を送信し、サーバーから各プレイヤーにその座標を通知してもらう必要がある。
各プレイヤーはその座標データを受信し、現在のゲーム内の該当するオブジェクトの座標をそれで更新する。

これが基本的なロジックになる。

このロジックで必要なものは↓になる。

  • ゲーム内オブジェクトの識別(どれがどのプレイヤーか)

賢明な読者諸君であればお気づきかもしれないがこのロジックではオブジェクトにユニークなIDが必要だ。
つまりプレイヤーがゲームに参加したときにそのプレイヤーのIDを生成し、サーバーからプレイヤーにそのIDを送信しておく必要がある。

プレイヤーはそのIDを元に自分のゲームオブジェクト、今回のゲームで言えば戦車を作成する。
この戦車にはサーバーから通知されたユニークなIDが入っている。

そしてプレイヤーは自身の状態を更新し他のプレイヤーに通知するときに、そのIDも一緒に送信する。

そうして各プレイヤーはそのID付きのデータを受け取る。
各プレイヤーはゲーム内のオブジェクトを調べてそのIDにマッチするオブジェクトを見つける。
マッチしたオブジェクトを見つけたら受信したデータでそのオブジェクトの状態を更新する。

マッチするオブジェクトが見つからなかったら新規にオブジェクトを作成し受信したIDをセットする。
こうすることでマルチプレイの同期が取れる。

connectとdisconnect

ソケットでは接続切断が肝心になる。

接続が発生したら新規プレイヤーがやってきたということになる。
そのためそのプレイヤーのためのデータ(初期化データ)をそのプレイヤーに最初に送信する。
そのプレイヤーは接続後に最初に受信するデータで自分のオブジェクトを初期化する。
このデータにはユニークなIDも含まれる。

そして接続より悩みの多い処理が切断処理だ。
これはソケットがクローズされたら呼ばれる。

なぜ切断が悩みの多い処理なのか。
これはブラウザゲームの性質によるものだ。

ブラウザは画面が更新されたりタブが閉じられたりする。
そうするとソケットは閉じられるわけだが、他のプレイヤーはそのことを知らないのだ。
だからソケットを閉じる前にそのプレイヤーは自分を破棄するように他のプレイヤーに命令を送る必要がある。

ID付きの破棄命令が送られてきたら各プレイヤーはそのIDに対応するオブジェクトをゲームから除去する。
そうすることでログアウトしたユーザーを画面から消すことができる。

つまり切断処理ではその前にこの破棄通知が必要になるわけである。
そうするとブラウザの更新やタブのクローズなどは検出し、それが行われる前に破棄通知をしなければいけない。
ブラウザの更新やタブのクローズの検出はbeforeunloadイベントで検出できる。

window.addEventListener('beforeunload', e => {
    e.preventDefault();
    e.returnValue = ''
    game.destroy()
    return ''
})

上記のようにbeforeunloadにハンドルを仕込み実行される前にゲームを破棄する。
ゲームの破棄にはプレイヤーからの破棄命令の送信も含まれる。

接続上限数

ゲームが盛況になって接続数が増えると同時接続数に上限を設けないといけない。
上限がないと無限にユーザーが参加できるようになってしまう。
そうするとゲームが重くなりゲームとして機能しなくなってしまう可能性がある。
あるいはサーバーに負荷がかかりサーバーが処理をさばけない可能性も出てくる。

そうなる前に接続上限数を設けておく。
connectされたら現在のグループの接続数を増やし、disconnectされたらその接続数を減らす。

こうしておいて接続数が上限に達していたらユーザーをサーバー側で強制的にcloseして切断する。

現在の自作ゲームはまだリリースしてないのでこの接続数上限の適当な値はまだわからない。
現在は32で調整している。

状態遷移

このゲーム開発で必要になるのが状態遷移だ。
状態遷移をマスターしておかないと開発はちょっと厳しいと思う。

具体的にはサーバーとのソケット通信でも状態遷移を使う。
サーバーはconnect後のプレイヤーに最初に初期化データを送信する。
プレイヤーはこの初期化データを受け取って自身を初期化する。

それ以降の通信では普通に他のプレイヤーの状態データを受け取る通信になる。
つまり「最初」と「それ以降」の状態があるわけだ。

この状態はソケット通信のデータに含めておく。
たとえばgame_stateのような整数を用意しておいて、その整数の値に応じてプレイヤーは通信後の処理を変える。

game_stateFIRSTだったら受信したデータで自身を初期化。
DESTROYだったら受信したデータでゲーム内オブジェクトを除去。
という具合に。

こうすると通信後の状態を細かく管理できる。
サーバーは最初はgame_stateFIRSTでプレイヤーに送信する。
プレイヤーはそのFIRSTを検知して初期化を行う。

ゲームから離れるユーザーは他のプレイヤーに自身の破棄命令を送信するが、このときgame_stateDESTROYで送信する。
サーバーは送られてきたデータをそのまま他のプレイヤーに通知する。
他のプレイヤーはそのDESTROYのデータを受け取りオブジェクトの破棄処理を行う。

オブジェクトの寿命

戦車ゲームでは砲弾を発射する。
この砲弾はずっと存在していると画面が砲弾だらけになってしまう。
だからこの砲弾は消さないといけない。

プレイヤーが砲弾を発射してその砲弾が画面外に行ったからその砲弾をゲームから除去した。
果たしてこれは正しい処理だろうか?

実はこれはうまくいかない。
プレイヤー1人のゲームから砲弾を除去しても他のプレイヤーの画面には砲弾が残るからだ。

これを解決するには砲弾に寿命をつける。
そしてゲーム更新でその寿命を削る。
砲弾が画面外に出たら寿命を0にしてもいい。

そしてソケット通信でその砲弾も一緒にほかのプレイヤーに送信する。
他のプレイヤーのゲームでも砲弾の寿命が0以下だったらその砲弾をゲームから除去するようにする。

このようなロジックにすると砲弾データをソケット送信するだけで砲弾はそのうち全てのプレイヤーのゲームから除去される。

砲弾は誰の所有物か?

砲弾は砲弾を発射したプレイヤーの所有物である。
このような概念にしておくと砲弾をマルチプレイで管理できるようになる。

仮に砲弾をプレイヤーから独立させたらどうなるか?
そうするとソケット通信でどの砲弾を送信したらいいのかわからなくなる。

プレイヤーに砲弾を所有させてその砲弾をソケット通信で送信することで砲弾をプレイヤーの一部としてみなすことができる。
こうすると管理が楽にできる。

おわりに

今回はマルチプレイゲーム、特にブラウザのシューティングゲームで重要な点をまとめてみた。
ソケット通信によるマルチプレイゲームの実装はコツさえわかればそれほどコストは高くない気がする。
もっとも多人数による実際の運用はまだ経験してないが。
この記事が参考になれば幸いである。

(^ _ ^)

マルチプレイゲームの扉を開こう

(・ v ・)

チャットが全ての基本



この記事のアンケートを送信する

スポンサーリンク