はじめに
ゲームな皆さんこんにちは、Game Solutions Architect の Leng (@msian.in.japan) と Game Developer Relations の Taka (@takahiroishii_) です。
この投稿ではゲームのバックエンドを初めてクラウド化させようと考えていて、オンラインゲームを作成する上で Amazon DynamoDB のデータ設計の基礎を学んでみたい、もしくは DynamoDB をゲームから学びたい、そんな皆さんが読んですぐにゲームデザインに合ったテーブル設計ができる事を目指した魔法のような記事になります。
builders.flash メールメンバー登録
AWS for Games

Amazon DynamoDB とゲーム
Amazon DynamoDB はハイパフォーマンスなアプリケーションを様々な規模で実行できる、フルマネージドでありサーバーレスでもある Key-Value 型の NoSQL データベースです。そんな DynamoDB とゲームの相性を考えてる上でまずは DynamoDB の代表的な特徴をあげます
- Key-Value データベース : キーと値というシンプルな管理方法でデータを格納
- 高いスケーラビリティ : 一貫性のある高速パフォーマンスを自動でスケールしながら実現
- 低い管理負担 : バージョンアップやメンテナンスのような作業が不必要
次にそれぞれの特徴とゲームの相性を見ていきましょう
- Key-Value データベース : プレーヤー主体であるゲームのデータベースのキーはほとんどがプレーヤー ID
- 高いスケーラビリティ : 突然人気が出たり、時間帯で総プレーヤー数が著しい変化するのがゲーム
- 低い管理負担 : 管理負担を減らす事でゲーム本来の面白さを作る事に集中できる
どの DynamoDB の特徴もゲームと非常にマッチしていて高いシナジーがある事がわかります。
Amazon DynamoDB のコンポーネント
この記事では DynamoDB の基本的な機能にフォーカスし、テーブル設計をより簡単にイメージできる事を目指します。その中で出てくるいくつかの DynamoDB のコアコンポーネント があるので先に確認しておきましょう !
- テーブル (Table) : RDB や他のデータベース同様、データのコレクションをテーブルと呼びます
- 項目 (Item) : 各テーブルに入っている識別可能な属性のグループです。RDB でいう行にあたります
- 属性 (Attribute) : 各項目は1つ以上の属性で構成されます。RDB でいう列にあたります
次に Key-Value 型である DynamoDB の Key の部分に当てはまるコンポーネントです。各テーブルはキーを指定して作る必要があり、そのテーブル内の 2 つの項目が同じキーを持つことはありません。このキーをプライマリキー (Primary Key) と呼びます。
更に、DynamoDB は 2 種類の異なるプライマリキーをサポートします。
- パーティションキー (Partition Key) : パーティションキー、通称 PK という1つの属性で構成されたシンプルなプライマリキー
- パーティションキーとソートキー (Sort Key) の組み合わせ : PK とソートキー、通称 SK という2つの属性で構成された複合プライマリキー
シンプルな Key-Value として 1 つの属性でプライマリキーを作り管理する事も多いですが、2 つのキーである PK と SK を組み合わせる事で同じ PK に複数の SK を紐付け複数の項目を持たせるケースも様々場面で便利になります。
この他にも Amazon DynamoDB は沢山の機能を持ち合わせています。詳しくは 公式ドキュメント を参照してください!
ゲームインベントリの定義

ゲームインベントリ
一般的にゲームインベントリとは、そのゲーム内でにあるアイテムを管理する場所を指します。例えば、RPG のようなゲームの場合プレーヤーが操作できる各キャラクターが武器・アクセサリー・防具などを管理するインベントリを持っていたり、はたまたターン制の戦略対戦ゲームなどでは各プレーヤーが複数のキャラクターとそれぞれのキャラクターに紐付いた武器や防具を管理するインベントリを持っているといった場合があるでしょう。今回は後者のようなプレーヤーが 1 つだけ持っているインベントリを作成していきたいと思います。
オンライン対戦機能のついたゲームの場合クラウド上にサーバーを置く事は必然的になり、またオフラインメインのゲームでもインベントリをクラウド上に保存し管理する事でプレーヤーのデータが守られ、マルチプラットフォームな環境でも簡単にプレーヤーは遊ぶ媒体を変更する事ができます。このように様々な理由でゲームのクラウド化を考える方は少なくないでしょう。
管理の少ない DynamoDB だからこそ、普段は手をかける事なくプレーヤー体験を向上させる事ができるというのも大きな利点になります。そんな DynamoDB という武器を持った魔法使いになって、ゲームをあっという間に作っちゃいましょう !

ゲームスタート !
前置きが少し長くなりましたが、いよいよ作業を開始したいと思います !
DynamoDB のテーブル設計をしていく上で ”アクセスパターンからテーブルは設計する” といったアドバイスを聞いた事のある方も多いでしょう、実際に公式ドキュメントにも 2 つの重要な概念 の1つとして紹介されています。いまいちこの概念の意味が理解できない方や初めて聞いたという方もご安心ください ! ここからは皆さんが慣れているゲーム開発のフローに合わせて紹介させていただきます。
テーブル設計までの流れ

ゲームデザインを決める
インベントリで管理する物その 1 : キャラクター
インベントリの中に入れる物その1はキャラクターです。ターン制の戦略対戦ゲームをイメージしているので 1 人のプレーヤーが複数のキャラクターを保持できます。それでは各キャラクターに紐付いている情報を決めます
持ち主であるプレーヤーの ID
このキャラクターのユニークな ID (全空間でユニーク)
このキャラクターの名前 (ユニークである必要なし)
このキャラクターのデータ ID (例えばマスターデータの ID )
プレーヤー ID 及びプレーヤー情報はすでに何かしらの方法で管理されているものとします。
インベントリで管理する物その 2 : 武器
インベントリの中に入れる物その 2 は武器です。こちらもキャラクターと同じく 1 人のプレーヤーが複数の武器を保持できます。それでは各武器に紐付いている情報を決めます。
持ち主であるプレーヤーの ID
この武器のユニークな ID (全空間でユニーク)
この武器の名前 (ユニークである必要なし)
この武器のデータ ID (例えばマスターデータの ID )
繰り返しになりますが、プレーヤー ID 及びプレーヤー情報はすでに何かしらの方法で管理されているものとします。
キャラクターに関する API
ゲームの UI/UX を思い浮かべ、その UI/UX に必要な必要な API を決めます。
GET : /characters/playerId
ある1人のプレーヤーの全てのキャラクターを取得する API
GET : /characters/character/playerId/characterId
ある1人のプレーヤーのある 1 つのキャラクターを取得する API
POST : /characters/new/playerId/characterDataId
ある 1 人のプレーヤーにマスターデータに基づきある 1 つキャラクターを付与する API
武器に関する API
キャラクター同様に武器に関しても基本的な作業を UI/UX 上で行う上で必要な API を決めます。
GET : /weapons/playerId
ある 1 人のプレーヤーの全ての武器を取得する API
GET : /weapons/weapon/playerId/weaponId
ある 1 人のプレーヤーのある 1 つの武器を取得する API
POST : /weapons/new/playerId/weaponDataId
ある 1 人のプレーヤーにマスターデータに基づきある 1 つ武器を付与する API
テーブル設計
では早速この定義された内容からテーブルを設計していきたいと思います。ひとまず管理したい情報は 2 種類 (キャラクターと武器) あったのでそれぞれバラバラに設計していきます。設計の手順は同じで以下の順番で設計していきます。
- まずはテーブルの名前を決める
- PK を考える
- 誰/何に紐付いている情報でか考える
- SK が必要か考える
- PK だけで管理できそうか考える
- 最後に残りの情報を何も考えないで属性として全部入れてしまう !
キャラクターの情報を管理するテーブル
ゲームデザインを振り返りながら、そしてテーブル設計の手順に沿いながらキャラクターの情報を管理するテーブルを設計していきます。
まずはテーブルの名前を決める
テーブルの名前は、そうですね、キャラクターを管理するという事で、ズバリ Characters にしましょう !
PK を考える
キャラクターの情報は何に紐付いているでしょう ? 各キャラクターには持ち主であるプレーヤーいてそのプレーヤーにはプレーヤー ID があるはずです。なので PK は持ち主であるプレーヤーの ID、 playerId にします !
SK が必要か考える
PK だけで管理できるかゲームデザインを振り返ってみます。
1 つ目の API “ある 1 人のプレーヤーの全てのキャラクターを取得する API” これを言い換えると、”プレーヤー ID でそれに紐付いているキャラクター全部持ってくる” になります。すでに PK がプレーヤー ID なので、この API は SK がなくても実現が可能です。2 つ目の API は “ある 1 人のプレーヤーのある 1 つのキャラクターを取得する API” これを言い換えると “プレーヤー ID に紐付いている特定のキャラクターを持ってくる” になります。この API を完成させるには、プレーヤー ID だけじゃなくキャラクターを特定する何かを使う必要があります。結論に行く前に、先に 3 つ目の API も確認しましょう、3つ目の API はプレーヤーにキャラクターを付与する API なので送る情報さえ足りていれば特に要件は無さそうです。
SK が必要かどうかに戻ると、2 つ目の API を満たすためには SK にキャラクターの固有の情報を使い PK と SK の複合プライマリキーで取得できると分かります。更にゲームデザインを深堀りするとキャラクターの情報の中にはキャラクターのユニークな ID がありました、これが使えそうですね ! という事でズバリ SK を characterID にしましょう !
最後に残りの情報を何も考えないで属性として全部入れてしまう !
ここは NoSQL の強み ! 残りの情報であるキャラクターの名前とキャラクターのデータ ID はそのまま入れてしまいます ! それぞれ characterName と characterDataId と命名しましょう。
これらをまとめ呪文を唱えると以下のようなテーブル Characters が完成すると思います。例となるデータも入れてみました。

武器の情報を管理するテーブル

完成。。。?
いやー完成しましたね ! と思っているあなたもう一声です ! ゲームデザインからテーブルを設計する事は DynamoDB に置ける 2 つの重要な概念 の内の最初の 1 つでした。言わば、まだ敵の HP が半分残っているような状態です。この概念のもう 1 つが ”テーブルはできるだけ少なくする” です。今回管理している情報である、キャラクターそして武器、それぞれに 1 テーブルづつ作成している事は一見できるだけ少なくした状態に見えます。ですが改めてゲームデザインそして 2 つのテーブルを見てみると、アクセスパターンは類似していて、テーブルのスキーマも非常に類似しています。これを上手く 1 つに纏めることはできないでしょうか ?
一度そのまま合わせて見たいと思います。テーブルの名前は Inventory とし、キャラクターと武器両方に共通していた SK の名前はどちらでもないので思い切って sk という名前にします。実は DynamoDB ではこのように PK や SK を中身に影響されずに使いまわしがきくようにそのまま PK/SK という名前にするテクニックがあります。
上手く纏めれてスッキリしたように見えますね ! ですが、念の為ゲームデザインを振り返って見ましょう。例えば、キャラクター管理の API の1つ目 “ある 1 人のプレーヤーの全てのキャラクターを取得する API” を引き続き使う事はできるでしょうか ?
キャラクター専用のテーブルがあった場合は PK のプレーヤー ID だけでデータを取得する事で全てのキャラクターが取得可能でした。しかし、1 つのテーブルに纏めた事で同じ方法を取ると全てのキャラクターと全ての武器を取得してしまう事が分かります。いやーこまりましたね。

begins_with
ここで使える魔法が DynamoDB のクエリのキー条件式 の 1 つである begins_with という条件式です。begins_with は SK がある特定のサブ文字列から始まる事を条件に絞り込みができるとても便利な条件式です。この条件式を使ってゲームデザインの要件であった “ある 1 人のプレーヤーの全てのキャラクターを取得する API” を満たすために、全てのキャラクターの ID を character_ というサブ文字列で始め、武器の ID を weapon_ というサブ文字列で始めたいと思います。それを踏まえたテーブルとテーブルの中身の例がこちらになります。

拡張性
テーブルをできるだけ少なくした事で管理が用意になっただけでなく、拡張性も追加されました。1 つの拡張性がインベントリの中身であるキャラクターと武器を 1 人のプレーヤーに対して全てを取得する必要が出てきた際に、PK であるプレーヤー ID を指定するだけでキャラクターも武器も同時に取得可能になりました。
さらに、今回は紹介しなかった DynamoDB の他のコンポーネントであるセカンダリインデックスを利用する事で、例えばキャラクターと武器それぞれに闇耐性のような特性 (passive) がある場合、それらもテーブルの属性として追加し、passive をグローバルセカンダリインデックスの SK として登録し、playerId を引き続きグローバルセカンダリインデックスでも PK とすることで “ある1人のプレーヤーの全闇耐性のキャラクターと武器を取得する” といった新しいゲームデザインの要件を満たす事ができます。
こうする事で、プレーヤー ID に紐付いた全キャラクターを取得する時は playerId = player1 and begins_with(sk, "character_") という条件を、はたまた全武器の取得する時は playerId = player1 and begins_with(sk, "weapon_") という条件を使う事でゲームデザインの要件を満たすことができます。
これでゲームデザインに含まれる全ての情報を管理し、全ての API をサポートする 1 つの単一のテーブルが完成しました !

拡張性
テーブルをできるだけ少なくした事で管理が用意になっただけでなく、拡張性も追加されました。1 つの拡張性がインベントリの中身であるキャラクターと武器を 1 人のプレーヤーに対して全てを取得する必要が出てきた際に、PK であるプレーヤー ID を指定するだけでキャラクターも武器も同時に取得可能になりました。
さらに、今回は紹介しなかった DynamoDB の他のコンポーネントであるセカンダリインデックスを利用する事で、例えばキャラクターと武器それぞれに闇耐性のような特性 (passive) がある場合、それらもテーブルの属性として追加し、passive をグローバルセカンダリインデックスの SK として登録し、playerId を引き続きグローバルセカンダリインデックスでも PK とすることで “ある1人のプレーヤーの全闇耐性のキャラクターと武器を取得する” といった新しいゲームデザインの要件を満たす事ができます。

まとめ
ゲームの裏側として採用が増えている Amazon DynamoDB、その難しいと思われがちがテーブル設計をゲームデザインを元にゼロからやってみました。
今回ご紹介した DynamoDB のゲームでの活用例やその他のデータベースに関した様々な情報がゲーム開発者の皆さん向けに AWS for Games のゲーム向けデータベース のページで纏めて確認できるようになっています。是非皆さんのゲームにあったデータベースを選んで楽しいゲーム開発に役立てて下さい !
筆者プロフィール

Sheng Hsia Leng
アマゾン ウェブ サービス ジャパン合同会社
ソリューションアーキテクト
ゲーム業界に特化したソリューションアーキテクトとしてお客様を支援しております。
RPG とドット絵ゲームが好きです。オフモードの時はインスタでバイリンガル漫画を投稿しています。

石井 宇大
アマゾン ウェブ サービス ジャパン合同会社
ゲームデベロッパーリレーションズ
カナダ🇨🇦 で 10 年以上生活していたゲームで遊ぶのも作るのも好きな元ゲームエンジニア。趣味は奥さんと季節を感じながら美味しい物を食べ大好きな赤🍷 を嗜む事。
普段は楽しいゲーム作りに繋がるよう、様々なソリューションを提供するお仕事をしています。
Did you find what you were looking for today?
Let us know so we can improve the quality of the content on our pages