見出し画像

ゲームプランナーが『バグの少ないデータを効率的に作る』ための最低限の知識と考え方

ゲームって、「データ」をいっぱい作らなきゃいけないんですよ。
例えばドラクエのごとく「スライム」を登場させるにしても、

スライムの最大HPは?攻撃力・防御力・素早さは?
何のスキル持ってる?

とか。
でも頭の中で考えるだけじゃゲームは生まれないので、ゲームに反映するためにちゃんと「データ」として持たなければいけません。こういう感じに。

問題はですね。

表の作り方・各データのつなぎ方(データ構造)によって、
作業効率とバグの数が大きく変わる

って話があるんですよ。
データ数が少ないならまだしも、コンシューマのゲームみたいにがっつり作りこむゲームとか、ソシャゲみたいな長期間の運営するゲームみたいのはモロに影響を受けます。

私は制作中のタイトルに途中から参加して、ゲームデザインをしたり引き継いだりもちょくちょくするんですが……この時もっとも恐れるのが、

はい、このデータ構造でこれから頑張って作ってね!
 ↓
え?これ死ぬほど作りにくくてバグも出やすいんじゃ……

ってパターン。それはもう

は?ちょっと誰ヨこんなの作ったの、正気か??おん???
えー、ていうかコレでこれから私このゲームを数年間つく……
か、勘弁してくれー!!うぉわおわあああーーーーーー!!!!

ってくらいには泣きそうになります。
まともなデータ構造だったらもっと簡単に分かりやすく把握しやすいデータをバグも少ない形で作れるのに。何が悲しくてこんなバグが出て当然の作り方をしなければならぬのか。

この記事はですね。そんな悲劇を少しでも止められたらいいなー……と思って書いていきます。

プランナー(ゲームデザイナー)さんの中には

よく分かんないしプログラマーさんに任せときゃえぇやろ

という人もいるかもしれませんが……。
それで万事解決OKだったら私は苦労していません。残念ながら、他人に任せっきりにしていたら恐ろしく作りにくいデータ構造になってた、みたいなパターンはしばしば存在します。(もちろん、ちゃんとしたデータ構造を作ってくれるプログラマーさんはおられますし感謝感激雨あられですよ)

実際に大量のデータを設計・入力・運用していくのはプランナー側なので、少なくとも

①:明らかに問題のあるデータ構造に対して待ったをかけられる
②:①に対してどう考えて対処すればいいのか分かる

というのが出来た方がいいじゃないかと思うのですよ。
データ作るの自分達だし。

とはいえ小難しい話をして「よく分かんねぇ!めんど!!」ってなるのもアレなので、この記事では初歩の初歩的な話、つまり

正確な知識を身に着けたいなら別で勉強した方がいいけど、とりあえず上記に上げた①と②がなんとなく分かる

ってのを目標に、これから解説していこうかと思います。


◆前提:大量のデータを管理するには?

さて、しっかり説明する前にちょっと前提の話をします。

ゲームには大量のデータが必要になりますが、その大量のデータを管理したり、必要な時に必要な情報を取り出したりするのに便利なのが「データベース」です。

まぁこの記事では小難しい話をしないと誓っているので、正確な説明はヨソに任せるとして。ざっくり言うと、

こういうデータの表(テーブル)を目的毎にいっぱい作ってデータを管理しているってイメージを持ってもらえればそれでいいです。
(敵のデータ表、装備のデータ表とか)

ただ、各データの表(テーブル。家具じゃありませんよ?もう覚えましたね?)がバラバラにあっても機能しません。
例えばこういうデータのテーブルがあったとして、

「ゆうしゃが装備している武器の情報が欲しい」という場合は……

こんな感じで、「テーブル間で共通したデータ( ↑ の場合は「超強い剣」という名前)を基に必要な情報を引っ張ってくる」みたいなことをします。
まぁよくわかんなくても、何となく ↑ の画像がイメージできてればそれでいいです。

以上、前提の話オワリ。

さて、ここで重要なのが記事の冒頭に書いた

表の作り方・各データのつなぎ方(データ構造)によって、
作業効率とバグの数が大きく変わる

って話で、それをこれから解説していきます。

まぁ「俺はデータベース使わないから関係ねーや!!」って人もいるかもしれませんが、どういう方法をとるにしろ、これから話す考え方はデータ管理をする上で把握しておいて損はないと思いますよ、はい。


◆バグいっぱいになるデータ構造

さて、あなたはこれからとあるゲームのキャラクターデータを作ることになったとしましょう。RPGで考えてみましょうか。どんなデータが必要になりますかね?

まぁまず「名前」は必要そうですね。えぇ。

それから当然、「ステータス」も必要でしょう。

RPGなら属性もあるので、属性耐性も必要ですかね。キャラ毎に属性耐性をつけられるようにしていきます。

はい。これで、ゲームによっては既に地獄の一丁目に突入しています。このままデータをどんどん増やしていくと、死ぬほど作るのが大変でバグが出るようになってきます。
「何を言っているんだ?」と思われそうですが……ここはRPGにおける属性界の狂気、皆大好きポケモンのデータを例として扱ってみましょうか。


◇悪例をポケモンで例えると

えぇ~っと、ポケモンの属性(タイプ)ってどれくらいありましたっけ。
最新作のアルセウスで考えてみましょう。18タイプらしいです。

はい。だいぶ横に長くなりました。横長すぎてまともに画像が見えないですね。まぁ別に中身は重要じゃないので「なんかいっぱい入力するところが増えた」って雰囲気だけ感じ取ってください。

んでキャラクターデータなので、これを『全キャラクター分』作るのを考えてみましょうか。アルセウスのポケモン図鑑はNo.240までなので、240体分の属性耐性を設定する必要があります。
なので数字で考えると、単純に考えると属性耐性のデータだけで

240キャラ * 18属性分の耐性 = 4320

のデータ入力が必要になるわけですよ。いっぱいですね。大変ですね。でもやるっきゃねぇですね。私はやりたくないです。

データがいっぱいということは、デバッグもいっぱいになります。
1キャラずつ全属性(タイプ)の攻撃を当てて仕様通りのダメージの通り方(こうかはばつぐんだ!とか)になるかどうかをチェックしないといけません。4320パターンのチェックが必要です。

まぁでも根性で頑張ればやれなくもない。
ただ超がんばってデバッグまで終わらせても、こんな話が飛んでくることもあります。

やったぞ、終わったz……え?仕様変更??やっぱり「でんきタイプ」は「いわタイプ」の攻撃も弱点にしたい?

さぁて、まずは240種のキャラから「でんきタイプ」のキャラを探さないといけません。18キャラいるみたいなので彼らの属性耐性、つまり18データ分を変えます。
そして再度デバッグするわけですが、まぁ人間ミスをするものです。↓ みたいなこともちょくちょく起きます。

あれ、サンダースだけ「いわタイプ」が弱点になってなかった?
変えたと思ったんだけどな、ゴメンゴメン。修正したよ。
お、今度はちゃんと弱点になったって?よかった、これで修正は完了だ!!

はい。非常に危険です。「完了だ!!」じゃねぇんですよ。
「変えたと思ったけど変わってなかった」っていうのは、単に入力していなかっただけならまだマシなんですが、最悪なのは「変えるべきじゃない別の所を変えていた」というパターンです。

例えば「いわタイプ」の設定の隣にある、「むしタイプ」への耐性を変えてしまっていたとか。No.27のサンダースをいわ弱点にするつもりが、No.26のシャワーズがいわ弱点になってたとか。

特に、既にデバッグ完了済みのところのデータが変わっていると、そのバグを抱え込んだままリリースされてしまうこともあって危険です。デバッグ完了しているデータをわざわざチェックしないし。
この辺を気にし始めると、「18キャラに対する1つの属性耐性の修正」でもデバッグは18データじゃ済まなくなってくるので、けっこう面倒な話になってきますね。

こんな感じで、

入力するデータが多い ≒ 作るのが大変なだけでなく多くのバグを生み出す

ってことが言えます。こわーい!さいてい!!


◇データの入力箇所は極力少なく

入力するデータが多ければ多いほどバグは増えるのであれば、逆に言えば入力するデータが少なければ少ないほどバグも手間も減っていきます

今回のポケモンの例でいえば、

キャラクターに設定されたタイプ毎に属性耐性が決まるなら、キャラクター毎ではなくタイプ毎に属性耐性を設定する。

ってした方が効率的です。言い換えると

同じパターンのデータ設定をたくさんやるなら、1つ作ってあとはそれを使いまわせばいいじゃん!

って話です。例えばこんな感じ。

キャラクターデータと属性耐性データを分離させて、「でんきタイプ」のキャラは皆「でんきタイプの属性耐性」で管理するよ!って形。

※ここでいう「キャラクターデータ」「属性耐性データ」を、「テーブル」って呼ばれています。「テーブル構造」って言われたら、この表の列構成のことですね。

これなら属性耐性のデータはタイプ毎の18パターンを設定するだけで済みます。数にすると

18 * 18 = 324

なので、キャラ毎に設定していた4320に比べて「7.5%」しかありません。大幅削減ができました。(いやそれでも324って多いけどさ。ポケモン……)

更に言えば

やっぱり「でんきタイプ」は「いわタイプ」の攻撃も弱点にしたい

みたいな仕様変更が起きた際も、変えるのは属性耐性データにある「でんきタイプのいわ耐性」1つで済みます。18個データを変更していたのが1つのデータ変更で済むのです。
バグの確認も全てのでんきタイプのキャラを試す必要はなく、1~2キャラを選んで確認すれば(ちゃんとそのキャラがでんきタイプって確認がとれているなら)十分です。

※念のため注釈(読まなくていいです)
今回のポケモンの例は説明を分かりやすくするために単タイプのみで説明してます。実際のポケモンでは1キャラ2タイプ持てるので属性耐性は重なりますが、そこは重なった時の処理(2タイプで重なったらダメージ4倍になるとか)をプログラミング側で設定するなりすれば問題ないです。
データ設定・管理の手間は変わりません。

このようにまったく同じ仕様であっても、どうやってデータを入力して管理してくのか……というデータ構造によってそのゲームの作りやすさとバグの多さは大きく変わります

ポケモンの属性耐性という極端な例を出しましたが、これはあらゆるデータに適用できる考え方です。
例えばRPGで「装備」を何十個、何百個と用意したけど、装備ごとに装備可能なキャラクターを毎回設定するべきなのか?とか。
属性耐性も「タイプ」みたいのが無くても、「(砂漠ステージの敵とかで)水属性のみ弱点の敵」が大量にいるなら、それは本当にキャラ毎に属性耐性を設定するべきなのか?とか。

要は

何度も同じようなデータを入力するようなところ(冗長なデータ構造)があれば、使いまわせる形にした方が遥かに楽ができるよね

って話ですね。
さて、次は別の問題があるデータ構造の話。こういうデータ構造だと、システム的な都合でアイディアが活かせなくなりがちだよって話です。


◆アイディアを邪魔するデータ構造

けっこう初歩的な話なんですが、かなりデータの柔軟性を失うのが

みたいな作り方です。
問題点といえば……単純にこれ、3つ目の「落とすアイテム」を設定したくなったらどうするんですかね?列を追加して「落とすアイテム3」を設定すればいい?

ははは、御冗談を(真顔)


◇データ列を増やして何が悪い!!?

「悪いわボケェ!!!」と叫びたくもなりますが。
ちゃんと説明するとですね。これ、

データ列を増やす = そのテーブルにある全てのデータが影響を受ける

と思ってもらっていいです。
200体分の敵データを作っていたなら、何も設定しないにしても200体に対して追加した分のデータを作らなければなりません

もちろん、「何もない場合は空白にしておいてもらえれば無視するよ」っていう作り方で誤魔化すこともできますが……。
「入力できる場所がある」というだけでバグの温床になったりはします。入力するべき場所じゃないのに変なデータが入っちゃってたとか。
列が増えれば増える程1行のデータを把握するのが難しくなってくるので、危険度は増していきます。

そして何より、データを設定すると新しく追加する「落とすアイテム3」のデータを取得して引っ張ってくる処理がプログラム側にも必要になってくるので、プログラマーさんの手を煩わせることにもなります。
ただ3つのアイテムを落とさせたいだけなのにね。

そんな感じなので、列を増やして対応する方針だと例えば

イベントのネタ枠で、アイテムを100種類落とす敵を登場させたいな!

ってのを成立させようと思うと全敵に対して「落とすアイテム1~100」まで設定しなければなりません。無駄に大変でデメリットが大きすぎるので、こういう話は絶対に成立しないです。
ちなみに長期間運営しているゲームなんかだとデータ数も千とか万とかいってもおかしくないですし、運営中にリスキーな行動で無駄バグを作るべきではないので、特にこの辺りの変更はかなり慎重になります。

つまり、

良い仕様を思いついても柔軟性のないデータ構造のせいで実行に移せない

という何とも悲しい結末を、こういうデータの作り方は生み出してしまいます。必要な制限であれば良いのですが、大抵は窮屈なだけの無駄な制限です。
ではこれ、どうすればいいのでしょうか?

◇1 対 X の構造を作る

こうした

1つのデータ(この場合は敵)に対して何個も設定したい。
何個まで設定するべきかはアイディア次第。

という場合は、「テーブル構造を分けて何個も設定できるようにする」と良いです。今回のエネミードロップの話だとこういう感じ。
※真面目に ↓ の画像を見るとデータの繋ぎ方が変なんですがその辺は後で解説します。

「スライム」に対しては2つの設定があるから2行のデータでそれを作る。「だいまおう」は1行、「Hなお姉さん♡」は3行。
ちなみにドロップが無い敵ならこのドロップアイテムテーブルにデータを作らない。

こうした「 1 対 X 」の構造、つまりデータ数に対して「列」ではなく「行」で対応すると、例え100種の「ドロップアイテムがある」みたいなものを作っても他のデータには影響を与えません。100行分のデータを頑張って作れば良いだけなので、プログラマーさんの手を煩わせることもない

こうして余計なバグを増やさず、思いついたアイディアを邪魔せずデータが作れるわけです。


◇データ列を増やす対応でもいいやつ

さて、これまで「データ列を増やすんじゃねぇ!悪!!」的な論調で話しましたが。データ列を増やすでも問題ないかなーってパターンも、もちろんあります。

この辺は制作思想とかも入るので絶対の正解はないんですが、例えば状態異常耐性みたく

必ず全ての行で入力するデータかどうか

は1つのポイントですかね。例えば毒攻撃を受けた時にそのキャラの毒耐性の値を拾ってくるんだけど、その時に毒耐性が「存在していない」と判定できずにゲームの処理的にバグが出る……みたいなパターン。

ドロップアイテムのような構造で作れば「新しい状態異常を追加する時に列を増やさなくて済む」という話はありますが……。
長く運用するようなゲームだとIDがぐちゃぐちゃになりがちだったり、データを追加し忘れたキャラがいても気が付きにくかったりするので、こういう場合は列を追加していくような方針の方が良いかもしれません。

要は

データ列を増やすと全部のデータに影響が出ちゃう!
 ↓
データ(行)によって入力しなくていいデータもあるぞ!
⇒ 列で追加はちょっと不味い
全部のデータ(行)が存在していることが前提だぞ!
⇒ 列で追加で全データが対応するのはむしろ当然

みたいな話はあるのかなーって思います。


◆おまけ:どうやってテーブルを繋ぐ?

さて、これまでの説明で、個人的に「最低限は知っておいて欲しいこと」の説明は終わりです。

ただ「考え方」について分かりやすさ重視で説明していったので、例えばこの表だと敵の名前でテーブルを繋ぐような形になっていましたが……

実際には名前で繋ぐことはせず、基本的にテーブルとテーブルを繋ぐときは「ID」を使用することが多いです。

というのも名前で繋いじゃうとですね。例えば

やっぱ「だいまおう」の名前を「大魔王」に変えよう!

って思った時に、「だいまおう」の名前が入っていたテーブルを全部変えなきゃいけないんですよ。そんで1個でも修正が漏れたらバグります
更に言えば「大魔王」の名前をもつ敵が2データ分(例えば片方が影武者的な存在で、名前は同じにしているのが)いたら、片方のデータだけが欲しいのに2データ分引っ張っちゃいます

なので、データを繋ぐものは基本的に

・他の行(レコード)で同じデータのものが必ず存在しない
・仕様の都合で基本的に変更が入らない

っていう性質の物でテーブルを繋ぎます。
そして、それを満たしているのが「ID」なわけです。識別用に割り振られた番号ってのは「名前」みたいにちょっとした都合で変えられたりしないので。

なので、例えばドロップアイテムのこのテーブルも実際には

こういう感じで管理されていきます。

※データベースに入るのはこういった情報ですが、データベースにデータを入れる前の段階(エクセルとかスプレッドシートとか)では各IDが何を指しているのか(敵ID:1の名前とか)を表示するようにしておくと、データ管理はしやすくなりますね

※複数条件でテーブルを繋ぐ場合もありますが、その辺りは割愛します。詳しく説明するのが目的の記事じゃないですし。

また、IDは(私は)基本的に数字の連番(1~)で振ることが多いんですが、場合によっては数字に意味を持たせたりテキストも組み合わせたりします。

例えば「その装備IDが武器・防具・装飾品のどれなのか」を一目で識別したいのなら、

てな感じですね。

数字に意味を持たせる場合は、↑の例なら「1~2桁は武器・3桁は防具・4桁は装飾品」でやってますけど、当然武器は100個以上は作らない前提の作りなので、その辺りはそのゲームの仕様と相談になります。

柔軟で分かりやすいのはテキストと組み合わせるパターンですが、「数字」ではなく「文字列」で扱う必要が出てくるので、この辺りはプログラマーさんと相談して方針を決めておいた方が良いです。


◆おまけ:「正規化」って何だ?

さて、ちゃんとデータ構造についてお勉強するぞ、と思った人がぶつかるのがこの「正規化」をしようねって話です。

なんかけっこう説明が分かりづらいんですよね。ただまぁ、ここまで記事を読んで内容を理解している人なら実用レベルでの把握は問題ないかなって思います。

なにせ今回の記事はこの「正規化」の話を分かりやすく分解して説明しているだけなので。正規化について超ざっくり説明すると

データ構造を整理して、無駄なデータを無くしつつ管理しやすいようにしようぜ!
・第1正規化:同じ意味の列があるならまとめようぜ!
・第2正規化:1つのテーブルでやるの無駄すぎるからテーブル分けようぜ!
・第3正規化:まだ無駄な所あるからテーブル分けようぜ!

てな感じです。正規化が進めば進むほどテーブルの分割が進み、無駄なデータが無くなっていきます。
※ハイパーざっくりしたことしか言ってないので興味ある人はリンク先とか見てみてください。

ただし正規化はやればやるほどいい物でも無くて、あんまり分割しすぎるとデータを取ってくるのが大変になったりはします。
なので正規化だけなら第5正規化とかもありますが……だいたい第3正規化くらいまでやるといい的な事はよく言われていますね。

プロジェクトによって最適な形は違うので、チーム制作とかならプログラマさんと相談して最終的に決めればええんでないかとは思います。


◆まとめ

はい、今回の記事は以上になります。
今回の話をまとめると、

①大量のデータは「データベース」で管理することが多い
 ┗ その時のデータ構造がアレだと作りにくいしバグもでやすい
 ┗ 同じ仕様でも、良いデータ構造でデータを作るのが大事

②バグを少なくするには極力、無駄なデータ入力を無くすこと
  ┗ 入力する場所が多いだけでバグは生まれる
  ┗ 仕様変更の時にも、最小限の修正で済むと良い
  ┗ ひとまとめにして使いまわせるデータは使いまわす

③柔軟性のないデータ構造はアイディアを殺す
 ┗ データの列を増やすと全データに対して影響する
 ┗ 同じ意味の列は無暗に増やさない
 ┗ アイディア次第で数が変わるなら「1 対X 」の形で作ると吉
 ┗ 全データに「影響するべき」なら列を増やしても良し

ですかね。

よろしくないデータ構造は、生産性も落とすしバグも増やすので

ゲーム制作における「癌」

とすら言えます。
責任もって自分で作りきるならいいんですけども。だいたい泣きを見るのはもう引き返せない段階で呼ばれた後任の人間っていう……。
まだ変更できる段階ならいいんですけども。

……とまぁ偉そうなことを書いてますが、ぶっちゃけ私もこの辺りに詳しいわけでもなくてですね。しがないゲームデザイナーですし。
見る人が見たら「いや、ここはそうじゃねぇだろ!」とか「こうした方がもっと効率がいいだろ!!」とかあると思うので、そういう人はぜひ記事を書いてください。情報がたらぬぇ。


◆宣伝

ちゃんとデータ構造を考えながらゲームを作っていて、そのゲームが現在Steamでアーリーアクセス版をリリースしています。

データ的なバグは少ない方だと思いますよ。誤字はあるけども!!!

あと面白いゲームを作るためのゲームデザインの話を色々書いてたり、

今回の記事と似たタイプのものだと、こういう記事とか書いてます。

興味ある人は読んでみてくださいなー。

サポート(投げ銭、金銭支援)歓迎です。 頂いたお金はゲームの開発費に使用させていただきます。  ↓ 支援が多いと私のゲームのイラスト枚数が増えたりオリジナルの曲が組み込まれたりします。美味しいお肉を食べに行ったりはしません。