LoginSignup
4
3

More than 5 years have passed since last update.

RESTful RPG Battle

Posted at

REST RPG Battle

最近 REST な API 設計を全然していません.

なぜかといえばゲームの操作って総じて RPC 的であると思っているからなのですが, 本当にそうなのでしょうか?

よく考えてみると REST でゲームの API 設計をしたことがないので, 一般的なターン制 RPG のバトルを設計したらどうなるかをやってみたいと思います.

RESTful API

正しい定義については https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm とかを読んでもらうとして
設計の前に自分の雑な REST のポイントは以下の4点になります.

  • ステートレスである
  • 全てのリソースは一意な URI を持つ
  • 統一のインターフェイスで操作できる
  • リソースは他のリソースへのリンクを持つ

これらを意識しながら API の設計をしていきたいと思います.

バトル仕様

まずはバトルの仕様ですね.

とりあえず

  • プレイヤー2名の 1v1 の対戦
  • ターン制でプレイヤーは交互にアクションを行う
  • 各プレイヤー1ターンで1回のアクションを行うことができる
  • アクションでは以下のいずれかができる
    • 攻撃
    • 相手の HP を 10 減らす
    • 魔法攻撃
    • 相手の HP を 30 減らす
    • 防御
    • 次のターン, 相手の魔法攻撃で HP が減らなくなる
  • プレイヤーは HP 100 を開始時に持つ.
  • アクションの結果, HP が 0 になったプレイヤーの敗北

みたいな単純なバトルを考えてみます.

設計

  • 通信には HTTPS を使うこととします.
  • BODY のフォーマットには JSON を利用するので Content-type: application/ json を指定します.
  • 認証のことは忘れます.

バトル作成

REST ですからリソースを作らなければあらゆる操作ができません.

バトル作成は POST /battles201 Created で返ってきて, 作成されたリソースの URI が Location: /battles/2 みたいにヘッダー返ってくる感じで良さそうです.

結果の BODYや GET /battles/2 で取れる内容は

/battles/2
{
    "player": {"hp": 100},
}

ってところですか. とりあえず2つAPIを作成しました.

URI Method 用途
/battles POST バトル作成
/battles/{battle_id}/ GET バトル情報取得

プレイヤー情報

よく考えると, player はバトルとは別のリソースな気がしますし, このままでは対戦相手のHPが見えません.

となると別のURIに切りださなければなりませんが, 別のリソースはリンクで示すことになっているので GET /battles/2 の結果は

/battles/2
{
    "links": [
        {
            "rel": "players",
            "href": "/battles/2/players"
        }
    ]
}

みたいにして, GET /battles/2/players でプレイヤー一覧を取れるようにする必要がありそうです.

バトル作成でプレイヤーを作成するのは, URI で指定したリソース以外を触っていてよろしくありません.

POST /battles/2/players でバトルへの参加 = プレイヤーの作成にします. バトルと同じように Location: /battles/2/players/21 みたいにリソースの URI を返します.

となると以下のAPIを追加です.

URI Method 用途
/battles/{battle_id}/players GET プレイヤー一覧取得
/battles/{battle_id}/players POST プレイヤー作成 (= バトル参加)
/battles/{battle_id}/players/{player_id} GET プレイヤー情報取得

GET /battles/2/players

/battles/2/players
[{
    "id": 21,
    "hp": 100
},{
    "id": 31,
    "hp": 100
}]

GET /battles/2/players/21

/battles/2/players/21
{
    "hp": 100
}

でとりあえず大丈夫そうです.
REST っぽくなってきた.

ターン

バトルが作れて, プレイヤーも作れたので早速攻撃したいと思います.
ATTACK /battles/2 とかできればいいんですが ATTACK というHTTPメソッドは無いのでできません.

どうすれば良いでしょうか.
攻撃がなんらかのリソースの作成だと考えると, 攻撃した結果できるのはそのターンが作成されると思い至ったので POST /battles/2/turnsLocation: /battles/2/turns/1 が返ってくるとしてアクションとすれば良さそうです.

/battles/2/turns/1
{
    "player_id": 21,
    "action": "attack"
}

player_id は認証から取るのとどっちがいいんでしょう?
認証から取るのはステートレスで無い気がするので, きちんと POST に含めた上で, 認証している ID と違っていたら 403 返すとかでしょうか. アクションする順番じゃ無いとかも 403

URI Method 用途
/battles/{battle_id}/turns GET ターン一覧取得
/battles/{battle_id}/turns POST ターン作成 (= アクションを行う)
/battles/{battle_id}/turns/{turn_id} GET プレイヤー情報取得

GET /battles/2/turns/1 は POST で渡した BODY をそのまま返せば良さそうです.

/battles/2/turns/1
{
    "player_id": 21,
    "action": "attack"
}

そのターンの攻撃した結果のプレイヤーの HP とか入れたくなるのですが, それは別リソースですからクライアントが PUT /battles/2/players/21 して HP を減らすことにします.

URI Method 用途
/battles/{battle_id}/players/{player_id} PUT プレイヤーHP更新

PUT /battles/2/players/31 で相手のHPを 10 減らします.

/battles/2/players/31
{
    "hp": 90
}

これで攻撃して相手の HP を減らすことができました!
あとは繰り返していけば良さそうです.

まとめ

RPC風な API だったら /battle/create /battle/action /battle/info の3つも用意すれば完了でしたが, REST 風にすると以下の 9 API を作成することになりました.

URI Method 用途
/battles POST バトル作成
/battles/{battle_id}/ GET バトル情報取得
/battles/{battle_id}/players GET プレイヤー一覧取得
/battles/{battle_id}/players POST プレイヤー作成 (= バトル参加)
/battles/{battle_id}/players/{player_id} GET プレイヤー情報取得
/battles/{battle_id}/players/{player_id} PUT プレイヤーHP更新
/battles/{battle_id}/turns GET ターン一覧取得
/battles/{battle_id}/turns POST ターン作成 (= アクションを行う)
/battles/{battle_id}/turns/{turn_id} GET プレイヤー情報取得

今回はバトルの仕様もかなり単純なものにしているのでカードやデッキを使ったり, 場の効果やスキルといった概念が入ってきたりするとさらにたくさんのリソースを定義する必要がありそうです.

また, 自分のリソースの操作するときに, 他のリソースの操作をしないようにするため, クライアント側に複数のリソースの API を叩いてもらう必要があったりとかもポイントでしょうか.

なかなか楽しかったです.

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3