はじめまして。ピグブレイブでサーバーサイドの開発を担当しております、中塚と申します。
ピグブレイブは、アメーバピグと同じように自分そっくりのアバターを作成することができ、仲間と一緒に冒険するオンラインRPGです。他のユーザーやゲーム内で手に入る仲間キャラとパーティを組み、モンスターとのバトルをすることができます。
先日、ピグの部署内での勉強会にて、7月15日にリリースされたピグブレイブ大型アップデートについての話をさせていただきました。ピグ事業部では職種問わず勉強会が定期的に行われています。
今回はその際にお話した、レイドバトル機能開発についての話を主に書かせていただきます。
ピグブレイブのアーキテクチャ
まず先に、そもそものピグブレイブの構成を簡単に紹介したいと思います。
ピグブレイブのクライアント側はFlashで動作しており、クライアントとサーバー間はWebSocketでコネクションが張られており通信を行っています。
サーバー側ではnode.jsを採用しており、データの保存にはmongoDBとredisを使用しています。各アプリサーバーは、general, matching, battleの3タイプに分類されてクラスタを組んでおり、各タイプごとに処理内容が分担されています。
- general: 最初にユーザーが接続するサーバー。自分の部屋の表示等
- matching: ユーザー同士がパーティを組む際のマッチング処理等
- battle: バトル全般を担当。攻撃の当たり判定等
各アプリサーバーではnode.jsのプロセスがそれぞれ4つずつ動いています。他にも管理画面用サーバーやAPIサーバー等がありますが今回の話とは直接関係ないので割愛します。
大型アップデート概要
大型アップデートでリリースされた主な機能は次の2つです。
- レイドバトル
- バトルダンジョン
それまでバトルといえば、
- パーティを組む(ソロバトルのパーティ編成 or プレイヤー同士の共闘バトル)
- キャンプエリアに移動する
- バトルエリアに移動する
- バトル開始
という流れしかなかったのですが、今回のアップデートにより
- バトルダンジョンエリアでモンスターにぶつかる
- バトルエリアに移動
- レイドバトル開始
という新たな遊び方が追加されました。
レイドバトル
レイドバトルとは、複数のプレイヤーで共通のボスに挑み、一緒にボスの討伐を目指す方式のバトルを指します。(細かい遊び方についてはこちら → http://ameblo.jp/piggbrave/entry-12179599350.html)
では、ピグブレイブにおけるレイドバトルはどのように実現したのかをご紹介したいと思います。
先に述べた通り、各サーバー上では4つのnode.jsプロセスが動いており、各プロセス上でバトルエリアが管理されています。
ソロバトルや共闘バトルの場合、1つのバトルは1つのエリア内だけで完結するので、バトルに関するデータはメモリ上にあるareaオブジェクトに集約されていました。バトルに関するデータとは
- ユーザーサイドのデータ
– パーティメンバーの情報
– 各ユーザーや仲間キャラのHP
– 装備やレベル - 敵サイドのデータ
– ボスのHP
– バーストモードゲージ
– 敵が取る行動のリスト
などなど…バトルに必要なデータ諸々を指します。
ソロバトル・共闘バトルでは1つのエリア内でバトルが完結していましたが、レイドバトルは複数人で同時に戦うため、図で示すと以下のような構図になります。
1つのレイドバトルにつき、参加者分のエリアオブジェクトが生成されます。そして各参加者の状況や、共通のレイドボスの情報など、該当のバトルに参加者しているユーザー全員で共有すべきパラメーターは、RaidBattleというオブジェクトに切り出して管理し、必要な値をバトル参加者で共有しています。
バトル中の同期について
ピグブレイブでは複数のユーザーが同時にバトルをすることができ、リアルタイムでバトルが進行します。そのため、他のユーザーが攻撃した情報を、該当のバトルに参加している他の人にも同期する必要があります。
以下が実際のレイドバトル中の画面になります。赤枠で囲った部分は、全ユーザーが共通で見えている部分、すなわちユーザー間で同期している部分になります。
- バトル中のユーザーのレイドバトルポイント
(そのユーザーがボスに与えたダメージ量から算出したポイント数) - ボスの残りHP
- ボスのバーストモードゲージ
(ボスは一定量ダメージを受けると、モードが変わって暴走したりするので、その現在のモードを示すゲージ)
本来なら、各プレイヤーが攻撃をして変化がある度に同期をすると、一番正しい値をユーザーに通知することができるのですが、変化がある度に同期をしていると得点のソート処理や通知が走りすぎてしまうので、しきい値を設けて対応しました。
{
"syncThreshold": { // 同期間隔の設定値(ミリ秒)
"updateBurst": 3000, // バーストモードゲージ
"updateHp": 3000, // ボスのHP
"updateRaidStatus": 1000, // ランキング
}
}
こちらは管理画面から設定できる値になっています。最後に同期した時刻をメモリ上で保持しておき、次にユーザーがスキルを発動した時にこの設定値と比較して、最後に同期した時刻からしきい値秒以上経過している場合のみ、同期を行うようにしました。
このようにリリース後に負荷を監視しながら調整できるようにしたい値については、コード上で直接定義せず、管理画面からエンジニアが設定して運用できるようにしています。
バトルダンジョン
複数のプレイヤーとエネミーが存在し、自由に動き回れるエリアのことをバトルダンジョンと名付けています。バトルダンジョン上にいるエネミーにぶつかることで、そのエネミーとのバトルを開始することができます。
誰かが敵にぶつかったら、すぐにエネミーオブジェクトが持つフラグを変更します。
fieldEnemy.status = BATTLE_STATUS;
このフラグ操作によって、同一エネミーに対して複数のユーザーが同じ操作をしてしまうことを防いでいます。
エネミーの種類
バトルダンジョン上には、ソロバトル専用の雑魚エネミーと、レイドバトル専用のレイドボスの2種類のエネミーが存在します。
この2種類の敵はそれぞれ定義されているパラメーターが違いますが、レイドバトル用のエネミーは、ソロバトル用のエネミーを継承して定義されています。
バトルダンジョン上への同期について
レイドバトルのステータスを、そのバトルが開催されているバトルダンジョン上のエネミーの表示と同期させるために、バトルダンジョンエリアとそれに紐づくバトルエリアは同じプロセス上で管理するようにしています。同一サーバー内で完結させることにより、サーバー間で通信する手間を省きました。
バトルダンジョン上では、そのバトルに参加しているピグが他人から見えるようになっているため、バトル中のユーザーのピグは、バトルエリアとバトルダンジョン上の二カ所で表示されていることになります。どちらか一カ所でピグが残り続けてしまうことのないように、クライアントとサーバー間では常に死活監視を行っており、一定期間応答のなかったピグはエリアから削除するようなcronを走らせています。
最後に
今回の開発では、リリース前に一般の方にテスターとして体験していただき、ご意見をいただいた上でさらにブラッシュアップを行うなど、今までにない取り組みも行いました。今後もユーザーの皆様に楽しんでいただけるサービスを目指して、開発陣一同日々改善に取り組んでまいります。
最後まで読んでいただきありがとうございました。