ゲームプログラミング/RPG
本書の注意点
[編集]本書の目的
[編集]本ページ『ゲームプログラミング/RPG』では原則、あまり(あるいは「まったく」)、いわゆるドラクエ的な意味での「RPG」の難易度デザインやそのためのパラメータ調整などのゲームデザインに関する話題は扱いません。
なぜなら、本書『ゲームプログラミング』は、題名にもあるとおりプログラミングの教科書だからです。つまり本書は、RPGのゲームデザイン論の教科書ではないです。
難易度デザインについては、RPG限定ではないですが本wikiでは別途、『ゲームプログラミング/バランス調整』という難易度デザインに特化した目的のページが切り離されて存在しているので、そちらをご利用ください。
同様に、ゲームシナリオやイラスト素材や音楽素材などの制作など自前のRPG制作に必要なその他の知識も説明しません。もしかしたら必要に応じて「ドットエディタ」などのキャラチップ制作ツールの存在や最低限のIT的な説明はするかもしれませんが、しかしデッサン的な話題は一切しません。
出典などの有無について
[編集]市販のゲームクリエイター教育書には、一切、RPGのプログラミング解説書はありません。
一応、アクションゲームについては書籍『ゲームプログラマになる前に覚えておきたい技術』や『ゲームプログラミングC++ 』などがありますが、その書籍は別にRPGには限定しておらず、どちらかというとアクションゲーム寄りの内容かもしれません(たとえば『ゲームプログラミングC++』では「衝突検知」などの話題がある)。
そういった出版事情もあり、本ページでは、出典のないRPGゲームプログラミング技法を多く紹介しています。
出典がなくても、プログラミングについては実際に Visual Studio などのコンパイラでその技法を確認してみれば真偽の確認ができますので、読者ご自身で真偽をご確認ください。
本ページに限らず『C言語』や『JavaScript』などの本wikiのプログラミング言語の教科書も同様に、プログラミング技法については特に出典はないスタイルです(出典があるのは、主に用語や歴史などの説明に関する出典だったりする)。
想定読者として、そういう御自身でコンパイラでコードをビルドして確認できるレベルの人を本ページでは想定していますので、もし読者がそれに到達していないなら、このページからは決して学ばないでください。
コード全部は紹介しきれない
[編集]実際のゲームプログラミングでは、たとえ無音声の2Dゲームですら、計算処理の他にグラフィックやキーボード入力など多くの機能が連動するので、コードはかなり長くなります。無音2Dのシンプルな超短編ゲームですら、初心者でもコードが何千行やあるいは1万行以上にもなります。
そのため、本wikiでは動作ソースコードの全部は到底、紹介できないです。だから本wikiで紹介されているコードをコピーペーストしても、それだけでは動かないのが普通です。
本ページにあるC言語のコードは、プログラミングでRPGを作る際に、「たぶん多くの人はこういった点が気になって悩むと思うから、こういうふうにコード記述すれば解決できると思います」といった方針だけを提供するものに過ぎません。
あるいは、もし本書で紹介したコードで動作したとしても、そのコードの結果はコマンドプロンプト(DOSプロンプト)のような真っ黒い画面で、RPG的な計算内容による計算結果だけを表示するプログラムだったりするので、そのままでは他人に遊んでもらえるような実際のゲームではないので、したがって実際のゲームプログラミング時には映像の表示などのためのコード追加がかなり多く必要になります。
はじめに
[編集]本ページのRPGは、ターン制の戦闘システムを採用した[注 1]RPG(Turn-based RPG)を中心にとり扱う。アクションRPGやシミュレーションRPG、アクティブバトルなどを語る場合には、注記する。
以下のものは取り扱わない。
- テーブルトークRPG。TRPGや、テーブルトップRPGとも呼ばれる。
なお、本ページでは説明のためにC言語を用いている。C言語の理解を前提としている箇所が存在するため、もし読者が知識をお持ちでない場合は、適切な媒体で調べながら読み進めていただきたい。 本ページではC言語を説明のために用いているが、開発環境がそれ以外の言語である場合は、適宜読み替えていただきたい。
これからこの単元では、ゲームプログラミングとそれに関連する理論を解説していきます。
ですが、ゲームやマンガやアニメなどの娯楽は、理論にもとづく分野ではなく、観客を楽しませてナンボの業界です。
また、そもそも伝えられている理論自体が間違っている場合もあります。
たとえば漫画家の江川達也(えがわ たつや)の体験談なのですが、彼は漫画家デビュー当初、マンガ中での男性キャラの履いているズボンをかっこよく書くのに時間が掛かっていて困っていました。
そうなった理由は、彼・江川の崇拝するマンガの巨匠、手塚治虫が、「人体に直線の部分は無いので、人の絵を書く際に定規などを使うべきではない」という理論を、江川も信用していたからです。
しかし、その手塚理論のせいで江川はズボンを書く際に定規を使わなかったので、ズボンのラインがうまく真っ直ぐに書けず、困っていました。
ある時、江川が試しに定規を使ってズボンを書いて見たら、短時間でズボンをかっこよく描けたので「手塚先生、ウソを教えやがった」と愚痴を何かの書籍で述べていました。
マンガなど創作の分野の理論とはこういものです。偉い人が言ったから正しい、というものでもないし、当然、偉くない人が言ったから正しいというわけでもありません。
そうでなくて、実際に理論を手段として使ってみた結果、観客から見ても「かっこいい」「かわいい」「楽しい」という感情を呼び起こさせる作品をつくるのに役立つかどうかです。「かっこいい作品」「かわいい作品」こそが良い作品なのです。「理論どおりの作品」はけっして良い作品ではありません。だからマンガ、アニメ、ゲームなどの創作の理論とは、観客を楽しませる良い作品をつくるための手段の知識をまとめただけにすぎない道具が「理論」です。
だから、ある理論がもし創作の役に立たないなら、その理論はさっさと捨て去るべきです。少なくとも、その理論のために手間を掛けたのに作品を作っている自分すらも「かっこいい」「かわいい」「楽しい」などの感情を呼び起こせないなら、捨て去るべきです。
ゲーム業界でも同様のことが2010年から既に書籍で言われており、証拠として、ntny著『ローポリスーパーテクニック』で、ゲーム業界の普通の3DCGデザイナーが目指すべき目標としては、決して物理的・幾何学的に正確なモデリングではなく、「かわいい」「かっこいい」と思わせるモデリングこそが良いモデリングであると語られています[1]。
「かわいさ」「かっこよさ」はビジュアル的な訴求力ですが、そのほかアニメ・マンガ・ゲームの創作に共通的に必要な方針があるとしたら、「わかりやすさ」でしょうか。証拠として、書名は不明ですが、ドラゴンボール作者の鳥山明が、マンガは「わかりやすさが大事」と、21世紀ごろの新しめの絵柄のマンガで述べています。
アニメ業界でも、ジブリアニメ映画『海が聞こえる』が典型ですが、わかりやすくするためにアニメ版のいくつかのシーンを原作から改変しています。主人公の少女は原作小説(氷室冴子が原作者)ではあまりデレデレしないんですが(原作者の氷室冴子さんがクールビューティを描きたかった)、しかしアニメ映画版では後半、少しデレデレしています。そうしないと視聴者が主人公 少女の恋愛経験による心境の変化を把握できないだろう、とジブリの監督が予想をしたので改変したのでしょう。
このことは、社会学者の宮台真司と、原作者の氷室冴子との対談で、なにかの書籍で1990年代に語られています。宮台はこの点でジブリを批判しています。原作の良さが台無しになってるという批判です。
上述のように、視聴者レベル・消費者レベルだと気づかないですが、一般に、大衆娯楽の作家は、わかりやすくするために工夫をいくつもしています。難しめのテーマを扱っていても(たとえば『海が聞こえる』ならフェミニズムとかでしょうか)、アニメ版では分かりやすく改変しているのです。
もしそれに気づかないとしたら、単に勉強不足なだけです。
ゲームも同様でしょう。そのゲームのプレイの楽しみ方が、分かりやすい必要があります。
- 言語の必要性
では、理論は全く不要なのでしょうか。いいえ、そうではありません。知識や技法を言葉にすることで、覚える内容が明確化されるので、同じ手法を今後も何度でも繰り返しできるようになるという再現性(さいげんせい)が生まれます。
一方、勘に頼っていると、その日の体調や気分によって作品が左右され、再現性が不安定になるので、生産性もまた低下します。
この再現性のための言語化の必要性は、「アニメ私塾」のペンネームで有名なアニメーター・イラストレーターの室井康雄がたびたび彼のYouTube動画などで言っていることです。
しかしその室井もまた、「理論どおりに描いてみて、(かわいいキャラ、かっこいいキャラなどを)うまく描けないなら、その理論は捨てろ」という旨の発言を彼のYouTube動画などで言っています(少なくとも2021年の前半に言っている)。
さて、上記の手塚治虫の理論を信じた江川の例は、まだ手塚が漫画家の先輩でもあるので、信じてしまったのも分かります。
ですがインターネット上には、誰が提唱したかも分からない理論もあふれています。このwikiもまた例外ではありません。ゲームやアニメなどは新しい分野であるので、出典をつけるのが困難な分野もあるので、出典も不明な俗説・仮説に頼らざるを得なかったりします。また出典のある情報だけに頼ると、文章構成が著しく断片的になってしまい、読むに耐えないか、読者が理解できなくて教科書としての役に立ちませんので、そういった仮説どうしを文脈的に結合するためにwikiでは更なる仮説によって補充説明などを加えていることすらもあります。
なので、このwikiも含めて、ネットに書いてあることは信用せず、読者は実際に自分で手を動かして確認をとったことをもとに、技術や感性などを磨いていってください。
このwikiは参考のひとつ程度に留めておいて、提唱している仮説を試してダメそうだった場合は、さっさとこのwikiの理論を捨ててください。
- ゲーム業界でも再現性あるマニュアル化が必要
ゲーム業界においても、『FGO』クリエイターの塩川洋介は、『ゲームデザインは「マニュアル化」できる』と、ある程度のマニュアル化ができることを認めています[2]。
けっして、そのマニュアルは「普遍的」ではないですが、しかしだからといって、マニュアルも持たずに「センスや才能(※wiki追記: だけ)でやってはいけない」と忠告しています[3]。
なぜかというと、塩川氏も、「再現性」という言葉を使って、マニュアルの必要性を説明しています[4]。
しかし、実際のゲーム製作は理論だけではうまくいかず、たとえるならスポーツのように実技的な側面もあります[5]。
あと、盲点ですが、スポンサー企業などが、再現性がないと投資してくれないと、塩川氏は著書で述べています。
ただし、理論だけでなく、実技的な能力も必要であり、上記のゲーム業界の塩川氏もアニメ業界出身の室井氏も、よく創作をスポーツに例えています。結局、ゲーム産業だけでなくアニメ業界やマンガ業界の実務分野の教育者たちも、似たようなことをいっており、よくスポーツにたとえます。
創作の世界は、どこも似たような傾向があります。
なので、けっして頭でっかちにならず、実際に手を動かすなどして、スキルを習得していきたいものです。結局、ゲームも漫画もアニメも、マニュアル的なものを準備しつつ、それを検証しながら修正していく運用法が必要になりそうです。
そういう言語の限界を分かった上で、理論は使いましょう。
余談ですが、ルネサンスの芸術家レオナルド=ダ=ヴィンチも似たような事を言っています。レオナルド・ダ・ヴィンチは「科学は隊長で、実践は兵隊である」と述べています[6]。
- 異業種の例
アニメ業界だけでなく、テレビCM業界など異業種でも、言語の必要性と似たようなことを言う人はいます。
97年にソニ-から発売されたゲーム『I.Q』の企画に携わった有名な広告プランナーの佐藤雅彦(お菓子のポリンキーなどのCMの人)は、 映画監督の川村元気の対談集『理系に学ぶ』で、 佐藤いわく「感性とか信念みたいなことを軽々しく言いたくないですよね。最後は感性で探り当てるしかないんですが、 そういった言葉ですぐ片付けるのは、傲慢な気がします。」と述べています[7]。
同じ川村元気との対談集では、別の対談相手ですが医学者の養老孟司は『戦後のものづくりなんかも大勢の理系の人が一生懸命トンネル掘ったり、計算機を作ったりしたけど、あれも嘘をつかない。要は言葉を信用してないんです。』と述べてます[8]。第二次世界大戦の終わった昭和20年8月15日、それまでの日本の新聞やラジオなどのメディアが喧伝する常識が崩れたのです。 養老はそういう過去を振り返って、「言葉を信用しない」と対談集で述べています。養老の場合、嘘をつかない医学解剖に興味をもち、解剖学を専攻したと述べています。
さて、ガンダムなどで有名な富野監督は、2012年ぐらいの書籍か雑誌での対談インタビューで、
その本では異業種の人たちと対談していたのですが、おおむね
「アニメ業界は教育を見直して整備しないといけない。会社が実務的なこともっと教育をしないと、頭のいい若手は、自分で教育を始めてしまう。」といった感じのことを言っていました。もちろん、富野監督は、大いに教育をすべし、という立場であり、その若者を応援する立場です。
自発的に若者が教育を始めるのなら、それだけ聞けばよいことのように思えますが、しかし富野監督がわざわざこういう苦言のような言い回しでいうことには、なにがしか、周囲からの反対意見などもあったんだろうとか、読者は推測してください。大人には、企業の事情などで言えないことがあるのです。
たとえばガンダムを作っている会社は「サンライズ」というアニメ会社なので、他社(たとえばエヴァンゲリオンの会社なら「ガイナックス」とか「カラー」という別アニメ会社です)に所属する若手アニメーターの主催するアニメ作画勉強会に出席するのは、いろいろと大人の事情があり、周囲からは疑義も出てくるのです。
富野監督はさらに、その対談書で、「自分だって絵を描くときにフォトショップを使っている」ということも言っています。もしかしたら、フォトショップに対する反対意見とか、いろいろあったんでしょう。 どこの業界でも、実務教育はいろいろと難しいものです。
創作は、読書だけでなく、スポーツ的に実際に手を動かして作品を作る経験も多く必要です。
それでも やはり読書は必要になり、ゲーム業界人の書いたゲーム専門書やゲーム制作教本などを読む必要が出てくるでしょう。
あまり何十冊も読む必要は無いですが、本屋で2冊くらいは教本を買って読んでおくと良いでしょう。
ではなぜ読書による勉強も必要なのかの話をします。
それは、業界の中にいる、ダメな先輩をダマらせるため、ダメ先輩に足を引っ張らせないためです。どこの業界でも、不勉強・経験不足な割に年功序列などで出世してきている先輩がいます。あるいは、勤勉ではあっても、自社でしか通用しないヤリ方を、業界全体の標準ケースだと勘違いしている先輩もいます。
そういう、ちょっと困った先輩を、それとなく間違いに気づかせるために、同じ業界でもっと経験のある専門家の書いた文献を読むのです。(ただし、新人のうちは、ダメな先輩の教えであっても聞くべきである。新人が考えた「ぼくのかんがえたさいきょうのしどうほう」よりも経験豊富な先輩の教えのほうが遥かにマシだから、である。少なくとも5年くらいは先輩の指示に従おう。)
たとえば、入社1年目の新人には、入社5~10年目くらいの上司がついたりするわけですが、所詮は入社10年目程度の中年サラリーマンなので、間違ったことを教える場合もあります。そこで、同じ業界の20年選手や30年選手のベテランの書いた本を読むのです。(ただし、10年選手の職場のやり方と、自分の職場のやり方が違う場合もある。自分の職場のやり方を優先すること。このため、職場の先輩の指示は尊重しよう。)
だから、本屋で2冊くらいは、業界のベテランの書いた教本を買って読んでおけばいいのです。
このwiki『ゲームプログラミング』でいろんな分野を何十冊も出典紹介していたりするのは、単に著作権上の都合にすぎないので(もし1冊の本だけを書き写したら著作権侵害になる)、ゲーム制作にはそこまでの多くの文献は不要ですが教科書著作のため仕方なく多くの文献を確認しているだけにすぎません。読者はwiki編集者の読書法・勉強法を真似る必要は無いですし、真似るべきではないです。
ネットの情報をあさるのではなく、実際に本で買って読むのが必要です。なぜなら、ネット情報だと、本気度が分かりません。webサイトのスポンサーの都合などもあり、言いたいことが言えないケースもあります。だから、実際に書籍として出版されている本を買いましょう。
予備知識と学習法
[編集]予備知識
[編集]一応、ターン制RPGは関数や配列を知らなくても、作り始めることはできます。
最低限必要な知識として、変数と、キーボード入出力の関数と、if文、タイマー関連、画面出力、あとはせいぜい乱数などがあれば、ゲームを作り始めることができます。
これはRPGにかぎらず、よくプログラミング入門書にあるジャンケンゲームなども同様でしょう。ジャンケンゲームならタイマーすら不要でしょう。
しかし、「関数を知らないプログラミング」とは、たとえば、zボタン(決定ボタンとする)を押したときのプログラム記述で、DXライブラリでの開発なら、マップ画面モードでも戦闘コマンド画面モードでもタイマーをカウント開始すると思いますが、そのタイマーの開始命令と関連する処置を各画面ごとに1行ずつ書いていく方式です。
だからもし、バグなどがあってコード修正する際、すべての画面モードのzボタンを命令を一個ずつ直さなければなりません。RPGはモードが多いので、そのような修正は、なかなか面倒です。
だから、別に作り始めの時点で関数を知らなくてもいいのですが、しかし作っている途中に「バグの訂正で似た部分の繰り返しが多くて面倒だなあ・・・」という気分になったら関数も勉強していくのが望ましいでしょう。
そして、いつごろが関数を学ぶタイミングなのかを知るには、事前に自分がある程度は「関数というものがあって、コードの使いまわしに便利なものらしい」という知識が必要です。
配列も同様です。配列を知らなくても、パーテイ1人目のHPを変数名「p1HP」、パーティ2人目のHPを変数名「p2HP」のような方式でRPGを作れます。
しかし、これを敵15体も登録されているRPGで「m1HP」から「m15HP」とやるのは、少し面倒です。
なにより一番の問題は、配列を知らないと、ドラクエ1的なマップ画面すら作るのが、かなり困難になることです。
よくプログラミング教本などにある例では、マップを二次元配列で実装しています。
たとえば、書籍『12歳から始めるゼロからのC言語 ゲームプログラミング教室』がそうです[9]。
たとえば、
{ {0,0,0,0,0,0,0,2,2,2}, {0,0,0,0,0,0,0,2,2,2}, {0,0,1,1,0,0,0,2,2,2}, {0,0,1,1,1,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0}, }
みたいな感じで、0番が草原、1番が森、2番が岩山、のような感じです。
横10×縦5のマップはそれほど広くないですが、
たったこれだけのマップでも、もし配列を知らなければ変数が50個必要です。
「配列を知らなくてもRPGを作れる」というのは、たしかにノンフィールドRPGなどは作れますし、
あるいはフィールドがあっても「マップはすべて草原、あるいはマップの99%くらいが草原(草原でないところだけ変数指定する方式)」のようなゲームなら作れますが、
しかしそれは、多くのRPGプログラミング入門者が考えているような、ファミコン~スーファミ水準のドラクエ・ファイファン的なイメージのRPGとは異なります。
初心者に「配列を知らなくてもいい」というのは無責任です。
配列を知らなくてもゲームプログラミングは開始できますが、もしも配列が必要になった時点で(変数を一個ずつ宣言するのが面倒なモジュールを作るとき)、 配列を学び始めるのが良いでしょう。
作ってから学ぶ
[編集]別に市販のC言語の入門書の基礎文法すべてに精通してからゲームプログラミングを開始する必要はありません。
おおよその機能だけを市販のC言語入門書に目を通して知っておいて、プログラミングでは変数などの初歩的な知識だけを用いて作り始め、足りない知識は必要になったらそのたびに、市販の入門書などで学べばいいのです、
また、その市販のC言語入門書を読む時間も、通学中・通勤中で電車のイスに座ってる時に読むとか、あるいはトイレでウンコしている時に読むとか、そういう時に読めば、プログラミングの時間を減らさずに時間を有効活用できます(ただし睡眠時間や食事の時間などは減らさないようにしましょう)。
さて、セーブやロードなどの機能は、当分は不要です。そもそも、セーブが必要なほどの長さのシナリオなんて作るのは当分は後です。もしお作りのゲームが1分でクリアできるようなゲームなら、セーブなんて不要でしょう。
だから、C言語のファイル入出力の機能などは、当分は不要です。
つまり「学んでから作る」ではなく「作っているうちに気になった点を調べる」のがコツでしょう。
おそらく「関数を知らなくてもRPG作れる」といっているプログラマーはたぶんそういう事を言いたかった口ベタな人なのでしょう。
なお、配列や関数などは、自分が直接は使わなくても、他人が使います。
たとえば、マップの実装で、もしかしたら二次元配列を使わなくても、原理的には一次元配列を何十個も並べて実装する方法もあるかもしれませんが、しかし他人がそういう一次元配列を何十個も並べてマップ実装したコードなんて読みたくないし、教育者も面倒なのでそういうコードを教えてくれません。
なお、構造体は別に知らなくてもRPGは作れます。列挙体 enum なども同様です。単に構造体の配列を知っていると、(装備品やモンスターなどの)データベースを第三者がより読みやすい形で作りやすくなるだけです。列挙体を知っていると、モードの切り替え(マップ画面モードからメニュー画面モードへの切り替え等)をしやすくなります。
正直、市販の「ゲームプログラマー向け」をうたったプログラミング教本の中には、少なくともゲームプログラマー初心者の時点では不要な高度な知識も多く紹介されています。しかしそういう本でも、出版社などの宣伝の都合からか、「プログラマーになる前に読んでおきたい」みたいな宣伝文句が付けられたりしているかもしれません。そういう高度な知識もあとで仕事で必要になる場合がありうるので、ゲームプログラマー向けとして出版する事自体は正しいのですが、しかし初心者や入門書に進めるような宣伝文句をつけるべきかというと疑問となる内容の市販本もあります。
ポインタ不要論
[編集]よく、「ポインタによる処理で速度の向上!」みたいな事が、ゲーム好きのプログラマーに言われます。
しかしポインタによるコーディングは、プログラムのメンテナンス性を悪くします。なぜならもしポインタ使用個所の周辺でバグが発生した場合に、ポインタがあると原因が特定しづらくなるからです。
一般のグローバル変数やローカル変数と比べてポインタ変数は仕組みが単純ではないことや、配列や構造体にポインタなどを用いる場合はもしその配列・構造体にバグがあると同じ配列内・構造体の別の部分にまでバグが波及するなどするので(コードによっては、もしかしたら、下手したら周辺の別の配列や構造体に波及する場合もありうるかも)、ポインタで実装されたモジュールがあるとバグ発生時に原因の絞込みが難しくなるのです。
もし仮にポインタ処理化を実装した直後では正常に動いていても、しかしあとで、今まで未実装だった部分が実装された場合に、なんらかの不整合によるバグが発生するようなことも良くあり、ポインタ処理はその際、バグ原因の特定を難しくします。
だから仮にどうしてもポインタを使わないといけない場合でも、RPG製作なら、なるべくポインタを実装するのは後回しにして、当分は一般の変数で実装したままにしておくほうが良いかもしれません。
あるいは、もし機能の実験や勉強を兼ねてポインタ処理の実験と実装をしたのなら、現状ではポインタ変数で実装してある箇所でも、あとでデバッグなどの際に一般の変数に戻す可能性も考慮することになります。
RPGの場合、デバッグに時間を多くとられますし、変数がとても多いので、メンテナンス性も考慮せざるを得ません。
- そもそも本当にポインタの必要な処理か?
ポインタによって高速化のメリットが得られる場合とは、C言語教科書などの理論では、大量の要素の構造体を(関数の引数などとして利用する際に)コピーする場合ぐらいです。
だからもし構造体を使う場合であっても、そもそも自作で用いる構造体変数がグローバル変数であればコピーの必要自体がないので、ポインタの必要もありません。
ゲーム内の武器データベースやモンスターデータベースなどが構造体で実装されると思いますが、初心者なら普通はそれらの構造体変数はグローバル変数として実装することになるでしょうし、グローバル変数として実装しても何も困りませんし、実際それでもRPGを作れますし、正常な速度で動きます。
例外的な事例を考えるなら、たとえばマップイベントのデータベースなど、特定のモードでしか使う機会のないデータベースなら、理論的にはグローバル変数でなくポインタによる取り扱いによって処理速度の向上の可能性はありえますが、しかし正直メンテナンス性が悪いし、難しい割にはポインタ化してもしなくても初心者ゲームのレベルでは速度は変わりません。せいぜい勉強になるだけです。
こういう風に、市販のC言語教科書にある知識の一つ覚えは、通用しません。
設計の方針
[編集]身体障害者プレイヤーの配慮
[編集]ターン制RPGは、身体障害などでアクションゲームなど反射神経を要するゲームの苦手な人もプレイします。なので、それを意識した設計にしましょう。シミュレーションゲームも同様です。もっとも、さすがに腕のないプレイヤーにまでは配慮できないので、程度問題ですが。
ともかく、だから、もしどうしても制作中のターン制RPGにアクション要素のあるイベントを入れる場合、たとえば工夫として、
事前にそういうアクションイベントのあることをプレイヤーに作品紹介文などで紹介しておくとか、
あるいはそのアクションイベントをもしクリアできなくてもプレイヤーがゲームクリアできるように設計するなどの配慮が望ましいでしょう。
もしくは、充分にアクション要素の難易度を下げておく必要があるかもしれません。
ユーザーインターフェース
[編集]もしもC言語でゼロからゲームを作るなら、UI(ユーザーインターフェース)をゼロからDirectXなどで作ることになります。 シナリオどうこうより、まずUIを作るのが真っ先になります。
結論から言うと、需要のあるUIの種類はそんなに多くなく、よって最終的には既存の有名ゲームのUIを真似することになります。(練習として新規のUIを色々と制作してみるのは構いませんが、しかし実用的なものを新規に発明するのは困難かと。)
「はい」/「いいえ」の選択肢
[編集]RPGなどで選択肢で『はい』か『いいえ』がよく出ることがあります。
この場合、必ずしも順番どおりに
はい いいえ
とするのが良いとは限りません。
なぜなら、プレイヤーがセリフ送りなどでボタンを連打している場合に、あやまって選択肢も連打してしまうことがあるからです。
文献『ゲームプランとデザインの教科書』によると、選択肢の順序についての設計テクニックとして、現状を維持するほうの選択項目を先に表示したりとか、あるいはマウス操作式のゲームなら現状維持や安全なほうの選択肢にカーソルを事前に合わせておくなどの工夫が、定石とされています[10]。
健康上の留意事項
[編集]カーソル点滅と「光過敏性てんかん」
[編集]例えば、コマンド選択などのカーソルの方式には、
- ファミコン時代のFF(ファイナルファンタジー)のようにカーソル画像を選択内容の左隣に置く方式、
- あるいは、ツクールのゲームの標準設定のように選択項目の背景が点滅する方式、
など幾つかあります。
カーソル点滅させる場合、実はこれはほとんど、カーソル点滅色は白系の色にせざるを得ません。
なぜなら健康上の理由があり、もし青色ウィンドウなのに、たとえばカーソル色を赤(または緑にして)、赤カーソルを点滅させると、「光過敏性てんかん」のような症状を引き起こします。このため、点滅カーソルの背景色は、事実上は、ほぼ白色に決まります。
一応、白でなくても、たとえば青ウィンドウなら水色などの背景色でも可能ですが、手間が増えますし、ほかのウィンドウ色(たとえばウィンドウを赤に変更した場合)では応用が利きません。
ゲームによっては設定カスタマイズなどでウィンドウ色を変えられたりしますが、プログラマー的に分析するコツとしては、カスタマイズできない部分(たとえばカーソル色はカスタマイズできない等)にも注目すると良いでしょう。
色弱の対応
[編集]なお、色弱(しきじゃく)/色盲(しきもう)という病気があり、緑色と赤色の区別がつきづらい人が、世間には居ます(赤緑色盲)。例外の人もいて、青など他の色が見えない人もいますが、しかし色弱患者で統計的に割合がもっとも多いのは赤緑色盲です。
赤緑色盲の人には、赤も緑も、ともに茶色に見えます。なので赤緑色盲の人にあ、赤と緑を区別できないのです。
よって事実上、ウィンドウ色は原則的に青色になります。ファイナルファンタジーや昔のツクールなどが、ウィンドウ色に青色を標準的に採用しているのは、決して偶然ではなく、いろいろな事情があるのです。
どうしても同一画面に赤と緑を表示しなければならない場合、色の濃さを変えるなどして、たとえば濃い緑と、うすい赤、などのように濃度差をつけるなどする必要があります。(この濃度差の手法は、役所などの出す、色覚障碍者向けのガイドラインなどにも書いてある、典型的なアドバイスです。 ) そもそも、色盲でなく健常者でも、濃度の同じくらいの赤色と緑色の組み合わせは、やや見えづらいです。 学校の「黒板」という緑色の板に、赤チョークで書かれた文字が、見づらかった記憶をもつ人も多いでしょう。
なお、赤緑色盲の人でも、黄色は、茶色とは区別して見えます。
なのでウィンドウに、黄色っぽい色と黒を組み合わせて使うのも手です。
あるいは、うすい茶色に、黒い文字という組み合わせもあります。
たとえばロマサガ1は、うすい茶色のウィンドウに、黒い文字です。うすい茶色と黒との組み合わせは、明度も大きく異なっているので、良い組み合わせでしょう。
スーファミ版の三国志4のウィンドウ色と文字色も、似たような組み合わせです。ただし三国志4の場合、文字に白い文字を使う場合もあります。数値などは白い文字です。このため、ウィンドウの茶色部分にはザラザラした模様をつけて白い文字を際立たせる工夫をしたりなど、工夫をしています。
ゲームに限らず、たとえば子供むけの歴史教材や小中学校の歴史教科書などでも、よくコラム欄などで、黄色い背景に黒い文字の組み合わせは使われます。古文書のような古い紙は黄ばんでいるので、歴史教材のコラムではそういう雰囲気に近づける目的で、コラムの枠を古文書の一ページのように描いているものもあります。
ゲームでもロマサガ1のメニューウィンドウは、古文書のように紙の下側の一部が書けていたり、まるで巻物の読み方のように左右が曲げられていたりして、ウィンドウに紙っぽさを出すデザインをしています。
例外的に、商業ゲームでもプレステ作品『俺の屍を越えてゆけ』のようにウィンドウがエンジ色(赤色っぽい色)の作品もあります。このようなUIの差異、当然ですが緑色の文字は使用不可能です。
- ※ その他、ファミコン版のスーパーマリオはマリオが赤で、ルイージが緑です。ですが、のちにシリーズが進むにつれ、ルイージの身長が伸び、また、マリオが太ってるのに対してルイージはやせているなど、色以外にもキャラの体系で区別できるように変更されました。
よくツクールやウディタの同人ゲームで、
回復スキルをつかったときの回復量の数値のフォント色を、うすい緑色または水色っぽい色で表す場合があります。
しかし、攻撃ダメージのダメージ量の数値のフォント色は、ほとんどのゲームで絶対に白色ばかりです。
どこまで同人作家が色盲を意識しているかは知りませんが、
もしダメージのフォント色が赤色だと、色盲(赤緑色盲)の人には、これは回復スキルの緑色と区別できないのです。
ついつい出血の赤色を意識して、赤いフォントを使ってみようと思うかもしれませんが、しかしその場合は色盲の人にはプレイできなくなるゲームになることを覚悟する必要があります。
これはつまり演出面で考えれば、色盲の人には、血の色(赤)と、草の色(緑)とが、同じ色に見えていることを意味します。
ウィンドウを決める場合だけでなく、ゲーム中のミニゲームの暗号パズルなどで色を使う場合も、色盲に配慮しなくてはなりません。
ぶっちゃけ、商業RPGゲーム中にもしミニゲームとして色パズルの暗号イベントがあるなら、
暗号の正解に使える色は、もしそのパズルがクリア必須イベントなら、答えの色は必ず、赤・緑の以外の色になります。
だから答えに使えるのは、商業ゲームなら必ず、黒か黄色か青か灰色か白、のどれかです。
なぜなら、まず赤と緑は当然ながら使用不可能です。さらに、赤と青の混色である紫も使用不可ですし、
青と緑の絵の具の混色(減法混色)である青緑色も使用不可ですし、
青と緑の光の混色(加法混色)である水色も使用不可です。
橙色も、赤と黄色の混色ですので不可能です。
学校教育ではどうやら、こういう色盲のことはゲーム産業やIT産業の志望のための学校ではあまり教育されていないようですが、しかし電気工学の学校ではよく教育される知識です。
電気工業の分野で、抵抗の「カラーコード」というものがあって、下記の色がそれぞれ下記の数に対応しています。
- 黒 0
- 茶色 1
- 赤 2
- 橙 3
- 黄色 4
- 緑 5
- 青 6
- 紫 7
- 灰 8
- 白 9
国家資格の電気工事師は、抵抗素子についているカラーコード部分の色を見て、抵抗値を判断するスキルが必要です。
なので、昭和の規制の厳しかったころは、色盲の人はそもそも工業高校の電気科には入学できませんでした。どんなに中学の成績がよくても、色盲の子は工業高校電気科への受験自体を断られる時代があったのです。
電気工学の教科書には書かれていませんが、工業高校や大学工学部の電気系の学科に入学すると、もしまともな学校ならば、こういう事を教わるものです。
車酔いの防止
[編集]光の点滅以外にも、調整を誤るとプレイヤーに体調不良を及ぼす現象はあります。 、 たとえばキャラの移動速度の加速・減速は、なるべく滑らかに変化しなければなりません。
「車酔い」を思い出してください。もし急加速と急ブレーキを繰り返す車に乗車していれば、多くの乗客は酔います。
なので、たとえばRPGなどで、「マップの1マス移動後にいったん停止」というのは、避けたほうが安全です。
実際、ドラクエは決してそうなってはいません。行き止まりに当たらない限り、カーソルの左ボタンを押していれば、同じ速度で左に進み続けます。(右キーの場合も同様。)
ドラクエの移動速度が等速度であることには、合理的な理由があるのです。
- 動きが時々カタつく場合
自作ゲームでテストした際、マップ移動中に右キーを押し続けたときに普段は等速で移動するのに時々カタつく場合があるかもしれません。その場合の対処法を後述で説明します。(2Dゲームのマップ機能の原理については、セクション『ゲームプログラミング/RPG#2Dマップ』で後述してある。)
なお、普段から等速ですら移動しない場合は、単にバグですので、直してください。
このセクションでは、とりあえず普段は右キーを押し続けたら等速で移動するのに時々カタつく場合にだけ、よくある原因そのと対処法を説明します。
時々テストプレイ中に動作がカタつくことがあり、「バグか?」と不安になることがありますが、しかし必ずしもバグが原因でのカタつきとは限りません。
もしかしたら、Visual Studio コンパイラがメモリ圧迫またはCPU占有などをしていて、単に実行アプリの処理速度が低下しているだけかもしれません。プログラミングに熱中していると盲点ですが、Visual Studio はメモリやCPUの多くを占有することを思い出しましょう。
原因がそれか否かを確かめる簡単な方法は、まったくほかのアプリを起動していないときに、実行ファイルを起動してテストプレイしてみることです。
ただし、状況によってはその「実行ファイル」をビルドするためにVisual Studio を立ち上げないといけない場合もあり、なかなか面倒な作業でもあります(VSは終了や起動に時間が掛かるので面倒)。
1度に確認しようとすると面倒ですので、複数日に分けて確認しましょう。たとえば、このカタツキ問題が気になったときの睡眠前の晩にでも、事前に実行ファイルをビルドしておいてから Visual Studio を終了してから寝ましょう。そして翌日にでもwindows起動したときに、Visual Studio を起動せずに前日にビルドしておいた実行ファイルだけを起動してテストしてみましょう。
そのほか、この問題の検証方法として、どうしても Visual Studio 起動中に、メモリやCPUの圧迫によってカタついているか否かを判定したいなら、簡易的な方法ですが、
最近のRPGツクール製のゲームまたはウディタのサンプルゲームを起動してみて、カタつくかどうかを確かめてみる方法もあります。
もしVisual Studio によるメモリ圧迫やCPU占有がカタつきの原因なら、ツクール製ゲームやウディタサンプルゲームでもカタつくことがあります。いわゆる対照実験です(中学高校の理科(おもに生物分野)で習うアレです)。
上述の方法すべてを試してもカタつきの問題を判明できない場合、残念ですが当ページには原因がもはや分からないのが現状です。(もしお作りのRPGでのカタツキの原因が分かられて、他のプログラマーも遭遇しそうな原因でありましたら、情報提供のため編集に協力していただければ当wikiとしては幸い(さいわい)かもしれません。)
- 高度な検討事項
前セクションの工夫でカタつきが解決すれば、下記の方法は不要です。また、ヘタにいじってバグの原因になると危険ですので、前セクションで解決したなら、そのままにするのをお勧めします。このセクションでは、参考のため、下記のような方法の検討を照会します。
動作をより滑らかにする工夫の一例として、たとえば、キーボードの十字キーの入力判定は、必ず1マス歩行のアニメーションの移動終了の少し前までに、判定を終わらせる工夫などが、考えられます。たとえば、1マス歩行のアニメーションが1.0秒かかるなら、入力判定は0.9秒目に行う、というような感じになります。
しかし、これはけっこう、シビアな条件であり、0.1秒単位でのタイミング調整を行わなければなりません。
歩行で1.0秒間というのは、割とゆっくりです。
一方、もしダッシュ機能とかあってダッシュ速度が1マスあたり0.5秒間だと、すこしタイミング調整が難しくなるでしょう。
もっと早いダッシュの場合は、Windowsだと少し難しいかもしれません。なぜなら、WindowsAPIのタイマー機能がそもそも、そんなに正確ではありません。0.05~0.10秒くらいの間隔になると、タイマーに誤差が出てきます。 DirectXのタイマーも、それに準じた性能だろうと思われます。なので、あまりに早すぎるダッシュでは、歩行のように「直前に入力判定」というのが、難しいかもしれません。
あるいは、もしかしたら、現代のコンピュータは処理速度が速いので、1.0秒のアニメーション終了後に入力判定を行っても、プレイヤーには気づかれないのかもしれません。
しかし、ハードウェアのメモリ容量などの事情によっては、もしかしたらアニメーション終了後の方式だと、動きにカクツキが出てくるかもしれません。ともかく、このように、速度ひとつとっても色々と考えなければならないことがあります。
こういうふうに、歩行アニメーション処理は、こりだすと、検討事項や調整事項が意外とあるので、初心者にはやや難しいです。
だから初心者には、歩行アニメーション抜きの方式がラクであり、いきなりキャラチップが移動先の隣マスに移動する方式(ファミコン時代の『信長の野望 武将風雲録』の戦闘の武将キャラチップみたいなの)のほうが、簡単でしょう。
同じ理由で、カメラの位置や向きなども、むやみに移動しないか、等速で移動しつづけるように注意する必要があります。
もし、「リアリティを出そう」としてカメラをキャラ歩行のたびに左右に振ると。プレイヤーが酔って気分が悪くなりかねません。よほど上手く調整しないかぎり、プレイヤーの体調的な気分を悪くしかねません。
だからカメラアングルで目指すべきは、2Dゲームなら結局、スーファミあたりのドラクエ的あるいはスーパーマリオ的な古典的なカメラ視点であるべきす。あるいは、もしポリゴンゲームなら、結局、カメラアングルで目指すべきは、プレステ1やサターンの時代のころのゲームのカメラアングルなどになるでしょうか。
あるいは、ファミコン時代にどうしても歩行アニメーションを停止しなければならなかった例として、ファミコン時代のウィザードリィや女神転生の3Dダンジョンのように、ポリゴン描写が無理などの理由で、どうしても停止しなければならない場合もあるでしょう。このような場合は、おそらくですが、停止中の1枚絵の表示時間の長さと、進行中の中割り(なかわり)の1枚絵の表示時間の長さが、同じぐらいの長さになるようにしないと、マズイでしょう(未確認)。
実装しようとしているものを明確にする
[編集]UIにおいて、アナログをデジタルで完全に実装するのは、大概困難ですが、アナログの「エミュレート」の実装は、それほど困難ではありません。
これはゲーム制作でも同様です。
たとえば、RPGの作中の武器屋や道具屋などでの買い物のシステムは、現実のスーパーマーケットやコンビニでの買い物とは異なり、まとめ買いはサポートされていないことが有ります。
ゲーム以外の話だと、たとえば通販サイトでの買い物の際、ユーザーレベルの目線では、サーバー側で異なるアイテムを連続して買う行動が一連のトランザクションとして処理されていたとしても、同様のことがありえます。
さて、ゲームでは、プレイヤーにとっては操作がやや面倒でも、デバッグの都合やメンテナンス等の都合で、あえてデジタル的な操作をしているゲームもあります。
たとえば昔のドラクエ3では、酒場で仲間をパーティに加入させる際、1人ずつ逐次的にコマンド入力をして加入させる必要がありました。(「3人まとめて加入」とかしたくでも、できない仕様。)ウィザードリィなどでも同様で、仲間のパーティ加入の編成などは、一人ずつ逐次的に操作する必要がありました。
このような、「まとめて〇〇を編集・編成」みたいな処理は、その分状態(取り得るケース)が増えるため、それに比例してデバッグにかかるコストが増える場合が有ります。
- 家電とはゲームは操作性が異なる
上記のケーススタディの教訓としては、つまりデジタル家電(地デジ対応テレビやMP3プレイヤーなど)のような操作仕様ですら、ゲームには不適切な場合があります。(家電のハナシ)現実世界で機能に直接対応したボタンのある家電と、一方でゲーム中の選択肢だけしか(十字キーなどを介して選択してからでしか)押せないゲームとでは、操作の前提条件が異なるのです。
アナログ家電(アナログ放送テレビや銀塩カメラやラジカセなど)と、(デジタルコンテンツである)ゲームとでは操作性が異なるのはもちろんですが、デジタル家電ともゲームは操作性が異なることに、気をつける必要があります。
アナログとはやや違いますが、例として、たとえばゲーム中に、CG閲覧機能として、プレイヤーがゲーム中でクリアしたイベントに関するCG閲覧機能などがある場合で、「アルバム機能」などの名前が付いている場合を考えましょう(2000年以降のノベルゲームなどで、よくある機能です)。
「アルバム」等の名前から、ついつい仕様の設計時には、実際の写真アルバム(物理)の操作性を真似したくなるかもしれません(つまり、「本の手触り」的な)。具体的には、電子書籍のようなUIを実装しようとか、思うかもしれません。
ですが、ゲームにおけるアルバム機能では、そういうことは無視して、単にCGの閲覧と検索のしやすさだけを考えたほうが、操作性とメンテナンス性もよくなるでしょう。
画面更新と速度との関係
[編集]- 60 FPSは意外と遅い
DXライブラリは、標準的な映像モニタでは、1秒間に60回のループ更新をします。
これは、一般的な映像モニターの画像の更新回数が60Hz、つまり映像モニタでは1秒間あたり60回の更新があるので、 プログラム側の更新回数もそれにあわせたものです。(これらのことを「60 FPS」などという。)
このような、映像の更新の早さの度合いのことをフレームレートといいます。
さて、人間の計算速度と比べたら「1秒間に60回」は速いですが、しかしゲームとしては、必ずしも速くありませんので、そのギャップに気づかないとプログラマーは不安に「パソコンが処理落ちしてるのか?」と悩んでしまいます。
たとえば、アニメーションの表現などとして、キャタクター立ち絵が1ピクセル(1px)ずつ1回の画像更新で移動したとしましょう。
すると、1秒たっても60pxしか移動しません。RPGツクールの標準の画面サイズは横816px、縦624pxなので、つまり60pxの移動では、画面の10%にすら到達していません。なので、もっと早い速度で立ち絵を移動させたい場合、1回の更新で3pxや5pxあるいはそれ以上のピッチ間隔で画像を移動させる必要があります。
このことに気づかないと、立ち絵の高速移動のつもりで1pxずつ動かしても、画面の横幅の10%すら動いてないので、「まるで処理落ちでもしているのか」と錯覚しがちで不安になりますが、しかし1pxずつ動かした際に意外と遅く見えるのは正常な動作です。そういう速度計算での命令文だからです。
具体的にはどう悩むかと言うと、RPG制作の場合ならばマップチップの枚数が多いので「もしかしてマップチップの読み込み方が間違っていて処理落ちしているのか?」とか、あるいは「キャラ画像の周囲の透明処理による負担が重いのか?」とか色々と不安を思ってしまいますが、まったくの無関係です。
1更新あたりに1pxのピッチ速度とは、それだけ遅いのです。なので、1秒後に画面内でチョコっとしか動かなくても、1秒後に動き終わっていれば正常です。
さて、ついでの話題ですが、もしアクションゲームやシューティングゲームなどで、1更新あたり5pxや10pxといったピッチで攻撃対象の敵などが動いている場合を考えて見ましょう。
たとえば、もし移動中画面で、現在の瞬間の描画位置 から 次の瞬間の描画位置とのあいだの中間位置、つまりピッチの中間の場所に自キャラの放った攻撃などが当たったときの例を考えてみると、なかなか悩ましい問題でもあります。つまり、画面上ではピッチの中間の場所には映像が書かれていないのに、なのに、その場所に攻撃を食らう「当たり判定」が存在するからです。
実際、アクションゲームなどで、高速で動く敵の軌道上に攻撃を放ってみると、画面上にいない位置なのに、攻撃があたる場合もあります。
だからファミコン時代あるいはそれ以前のゲームセンター時代といった黎明期のシューティングゲームですら、高速で動く弾丸チップや機体チップなどの表現には、とても工夫しているのでしょう。
このように、キャラチップなどの画像を高速で動かすのは、思いのほか、調整事項が多くて大変です。なのでRPG制作の初心者は、あまり高速な描画表現には手を出さないほうが無難です(まずプログラミングの全体像を作る必要あるので)。
実写の動画ですら、フレームレートの遅さに由来する違和感は生じます。実際、実写映画のDVDなどをコマ送りで見てみると、意外と動きが飛び飛びです。
人間は1秒間のあいだに、意外と多くの動きをしています。たとえば仮に、おやつのドーナツを食べる実写動画を例に考えてみると、1秒間のあいだに、まずドーナツに手を伸ばしたあとに、つまみあげて口元に持っていくぐらいの動作を、人間は1秒のあいだで平気でしています。もしアニメキャラだったら、ひょっとしたらドーナツ一噛みを始めているぐらいです。
このように1秒のあいだに多くの動作をするのに、たった60コマのフレームレートしか用意されていないのです。だからコマ送りで実写の動画を見ると、けっこう動きが飛び飛びで、まるでアニメを見ているかのような感覚です。たとえるならアメリカ製のアニメ映画は、日本アニメよりも1秒のコマ数が多く、日本アニメが1秒で8コマなのにアメリカ制アニメが1秒で24コマの違いはありますが、しかしアニメはしょせんアニメのような飛び飛びの動きです。実写の60フレームレートですら、アメリカ製アニメのたったの約2倍しかないフレームレートです。
また、半導体の性能向上は2010年以降は止まっていることなどを考えれば(「ムーアの法則」の破綻)、今後に市販の液晶モニターのフレームレートが飛躍的に上がる可能性は乏しいでしょう(説明の便宜上、液晶も「半導体」として扱った)。
さらにもし仮に製造業で技術革新が起きたとして、液晶モニターのフレームレートを飛躍的に十倍や百倍に向上させる新技術が出来たとしても、今度はハードディスク容量の問題があり、つまりフレームレートがもし10倍になったら、録画映像の容量バイト数も10倍になります。つまり、アニメや映画のブルーレイディスクなら、ディスク枚数が10倍になることを意味するし、これはつまりアニメなら作画枚数が10倍になるし、消費者にすればディスク購入費用も10倍ですし、生産者側からすれば予算も10倍なので投資リスクも10倍です。
このような技術的制約および経済的制約から、今後のフレームレートの向上の可能性はなかなか乏しいと考えざるを得ませんし、仮にフレームレート向上しても恩恵を受けられるのは実写やCGなどのようなアニメーターが手で作画しなくて済む分野の映像だけです。
万全のゲームシステムは無い
[編集]少なくとも2D-RPGのゲームシステムやその実装に関する限り、処理が早くて操作性も良くてメンテナンスもしやすくて・・・のような究極万全なシステムやアルゴリズムは一切ありません。
たとえば操作性の分かりやすいアルゴリズムなら、おそらくはプレイヤーのさまざまな発想に対応するためにif文の分岐増えたり、あるいはグラフィカルに説明するために画像の制御が追加で多く必要になったりするなどして、そのぶん他の特性が悪化しますし、説明用グラフィックが増えれば処理速度の負担にもなります。
しかし現在のパソコンではファミコン時代よりも桁違いに多くのメモリ容量を扱えるようになり、CPU性能も桁違いに向上しました。よってゲームプログラミングでは基本、処理落ちをしない程度にまで処理速度に負担を掛けて犠牲にして、そのぶん操作性やグラフィック性やゲーム性やメンテナンス性などを向上させることになるでしょう。
だから実はファミコン風のドラクエ1のような簡素なプログラムでも、あれはあれで処理速度とメンテナンス性をもし最優先に目指すなら現在でも合理的な一形態なのです。
また、ドラクエ1的なアルゴリズムはメンテナンス性がよいので開発もしやすいし、開発時のデバッグもしやすいです。なので、私たちがRPGを手元でプログラミングして作る場合も、まず2D-RPGならドラクエ1~3のようなUIをまねたゲームを作ることになるでしょう。
ただし、真似るのはあくまでUIのみです。パスワードシステムのような、セーブ機能の発達した現在では不要なシステムを真似る必要はありません。「話す」コマンドとか「とびら」コマンドみたいに、現代では不要になったコマンドも不要でしょう。また、ドラクエのウィンドウ色は黒色ですが、しかし私たちにはデバッグの都合のためファイナルファンタジーや昔のツクールのような青色を基調としたウィンドウのほうがデバッグしやすいかもしれません。
だから初心者は、昔のRPGツクールっぽくアレンジしたドラクエ風UIを作ることになるでしょう。(ドラクエの難易度デザインやストーリー性などはプログラミングではないので、本ページでは言及しません。)
製作の順序
[編集]本ページでは説明の都合上、RPGをモジュールごとに分割して説明しています。
たとえば、マップ関連のモジュール、戦闘関連のモジュール、装備関連のモジュール、のようにです、
しかし、実際のゲーム製作での開発順序は、少なくともRPGをVisual C++でゼロから作る場合においては、けっして、「マップモジュ-ルを完成させてから戦闘モジュール」のような順序にはなりません。
そうではなく、まず自分が興味をもったモジュール(仮にマップモジュールとする)の初歩を作り始めたあと、とりあえず操作できて満足できるところまで作ったら、
次に関連する別モジュール(たとえば戦闘モジュール)を作る、というような順序です。
たとえば、すでにマップモジュールがあるなら、戦闘モジュールを作り始める際に、まずマップ画面上に固定敵を配置することで、
戦闘モジュールのテストをしやすくなります。
このように、すでに作ったモジュールの初歩を土台にして、別のモジュールの初歩を同様に作り始めます。
そして、まずRPGの基本システムを満足するところまで作ります。(シナリオ作成などは後回しになるでしょう。)
マップシステム、戦闘システム、装備システム、道具システム、買い物システム、酒場システム、会話システム、・・・
など色々あるので、まず一通り、好きなシステムから順に、キリのいいところまで作りこんで、どんどんと次に作りたいシステムの開発に移行していき、
とりあえずゲーム全体の基本システムを構築していくことになるでしょう。
- 2周目
そして、一通り、満足するまで基本システムを作るのを1周したら、
次に2周目として、また、たとえばマップのシステムを作り始めます。
1周目のマップシステム製作では放置してた部分、たとえばキャラチップの歩行グラフィックなど絵が必要で放置してた部分の製作に取り掛かるとか(たとえば1周目では矢印の画像でゴマかしていたとする)、
そのように2周目では、1週目で放置していた部分の製作にとりかかるのです。たとえば、歩行グラフィックで前足を上げているポーズと、前足を着地させるポーズの切り替えプログラムとか、そういう1周目では面倒で放置していた部分に、2周目では取り掛かったりします。
このように、RPGの製作は、まるでラセン階段を昇るかのように、周回的に開発していくことになるでしょう。
-
親柱付非常階段(マンハイム大学)
-
キレスベルク塔の二重螺旋階段[11]
-
コペンハーゲンの螺旋階段
企業などで作る場合はどうか知りませんが、少なくともVisual C++でゼロからRPGを作る場合はラセン階段でしょう。
RPGはモジュールがとても多いし、また相互に関連するモジュールも幾多もあるので、一度に各モジュールを作りきるのは不可能です。
だから、ラセン階段のように、開発を昇っていくことになります。
なお、IY業界ではこういうラセン的に徐々に機能を追加していく開発手法を、スパイラル型の開発と言います[12]。
スパイラル spiral とは、単に、らせん状の渦巻きのこと を英語にしたものです。なお、
なお、銀行などのシステム開発はこれとは違い、銀行のシステム開発はふつうはウォーターフォール型の開発です[13]。ウォーターフォールとは、事前に仕様を細かく決めるのに長い時間をかけて、実装時にはほとんど仕様を変えない手法です。
ほか、プロトタイプ型の開発もあり、これは大まかに動く試作品を短期間で作り、あとから仕様を修正していく方式です[14]。
ゲームに関した話題ではないですが、スマートフォンのアプリの開発などでは、プロトタイプ型やスパイラル型の開発がよく利用されるようになってきています[15]。
たとえば理工書 『化学の基礎』 (化学入門コース 1) , 1996/4/17
などのシリーズ (化学入門コース) の巻頭の学習法ページにもラセン階段の絵が書いてあり、ラセン階段のイラストの絵の各所に対応する科目が描いてあったような気がします。
ただし、プログラミングは暗記科目ではないので、そこは手を動かしてプログラムを実際に構築していく必要があります。そういう点は、数学というか物理学というか。共通カウントアップ機能
[編集]RPGではシステム中にマップ画面モード、メニュー画面モード、戦闘画面モード、・・・など様々なモードがありますが、
DXライブラリを使っている場合にどのモードでも必ず使うことになる機能があり、それは自作タイマーのカウントアップです。
まず、DXライブラリでは1秒間に60ループするので、それを利用して1秒間に60回カウントアップする機能を作ります。
そして、そのカウンターを活用することで、たとえば、「zボタンを押してから40フレーム間(環境によっては約0.8秒くらい)は次のzボタンの入力を受けつけなくする」等のボタンをしばらく入力させなくする機能を作ります。
そうしないと、プレイヤーは1回だけzボタンを押したつもりでも、システム的には何十回もzボタンを連打したことになり、プレイヤーがうまくコマンド操作などを行えません。
また、コマンド操作などの他にも、マップ画面中でキャラクターがマップ1マスぶん動くスライドのアニメーションでも、カウンターが必要になります。
マップ画面の歩行グラフィックで「歩行カウンター」みたいな変数が必要になるでしょう。もしくは、歩行のキー入力に使った矢印キー用のカウンターを流用するなどの処置になるでしょう。
ともかく、このようなカウンター機能が必要なので、whileループ中での個別モードに分かれる前の共通部分のところに、カウントアップのコードを書くことになります。
「共通部分のところ」とはどこか具体的に言うと
while (1) {
if (ProcessMessage() != 0) { // メッセージ処理
break;//ウィンドウの×ボタンが押されたらループを抜ける
}
if (CheckHitKey(KEY_INPUT_ESCAPE) == 1) {
break;
}
ClearDrawScreen();
// ここにカウントアップ機能
のように、whileの開始のテンプレ文の直後に書き始めることになるでしょう。
なお、DXライブラリでよく書くことになる開始時のテンプレ文については 『ゲームプログラミング/画面出力#DXライブラリのコード初期設定』 で説明してある。
カウントアップではなくカウントダウンでも構いません。
イメージ的には、下記コードのような感じになります。ただし、下記のモジュールだけでは動きません。
あらかじめ、各モード側でzボタンを押した時などにカウンター値の設定などを行う必要があります。たとえばzボタンを押したときに
nyuuryokuMatiZ =60; // デバッグしやすいように少し遅めにした
などをセットしておきます。また、zキー入力可能かどうかを判定するフラグ変数、下記コードでは変数 keyEnableZ となっていますが、そのようなフラグ変数をあらかじめ用意しておき、もし値が1なら入力可能、0なら入力不可能として、
zボタンに対応する処理を1回実行してからz=0;
に設定して、zが0のときにカウンタが動くようにする必要があります。(ツクールなどではマップ画面からメニュー画面を開くのはx(エックス)ボタンだが、読者のバツボタンとの混同を防ぐため、上記の文ではzボタンを例に説明した。)
while (1) {
if (ProcessMessage() != 0) { // メッセージ処理
break;//ウィンドウの×ボタンが押されたらループを抜ける
}
if (CheckHitKey(KEY_INPUT_ESCAPE) == 1) {
break;
}
ClearDrawScreen();
// ここからカウントダウン機能
// zボタン用のカウントダウン
if (keyEnableZ == 0 && nyuuryokuMatiZ > 0) {
nyuuryokuMatiZ = nyuuryokuMatiZ - 1;
}
// zカウンタが0以下に到達したらzキーが再入力可能
if (nyuuryokuMatiZ <= 0) {
nyuuryokuMatiZ = 0;
keyEnableZ = 1;
}
// 下記はエックスボタンのこと。 バツボタンではない。
// xボタン用のカウントダウン
if (nyuuryokuMatiX > 0) {
nyuuryokuMatiX = nyuuryokuMatiX - 1;
}
if (nyuuryokuMatiX <= 0) {
nyuuryokuMatiX = 0;
keyEnableX = 1;
}
// 長いので、あとは省略
このコード例のように、共通カウンタ部では単にカウントするだけでなく、さらにカウンタが目標値(カウントダウン方式なら0が目標値)に到達したときの処理(フラグの設定など)も行います。
いきなり上記のような機能を作るのは、かなり難しいです。なぜなら、カウンタだけでなく、マップ画面やメニュー画面側のzボタン押し時やxボタン押し時の命令も記述しないといけないからです。
だからまずは、マップ画面とメニュー画面の入り口画面(まだメニュー画面の各コマンドは作らなくていい)だけでいいので、でキーのカウント処理をうまく行えるようにしましょう。
なお、メニュー画面に移ったらキャンセルボタンで戻せるように、あらかじめメニュー画面にキャンセル機能を実装しておきましょう。
新しい画面を作るときに最初に必要なボタン機能は、実はキャンセル機能です。キャンセル機能が無いと操作不能になって、いちいちアプリ再起動の手間が生じます。
- アクションゲームで肩慣らし
いきなりマップ画面やメニュー画面を作るのは難しいので、ひとつの手として、RPGを作る前に、簡易的なアクションゲームのような操作性のプログラム、または簡易シューティングゲームの操作性のようなプログラムを作るのも手です。
たとえば、スーパーマリオやスト2のようなアクションゲームなら、ジャンプボタンは1回入力したら着地までは再入力の受付が禁止なので、上記のようなカウンタ処理による再入力可否の制御が実験できます。
べつに、本格的にアクションゲームやシューティングゲームを作る必要はないです。後述のように、異なるジャンルのゲームをひとつのアプリに同梱するのは、管理の手間が増えますので(不可能ではないですが)、面倒が生じます。
これには準備として、マリオ(キャラ名)やスト2リュウに相当する自キャラ表示のため、あらかじめキャラチップの絵が必要です。別にアクションゲームを作るわけではないので、マウスでいいので何かキャラの全身絵を3分くらいで雑に書いてください。そして、キャラの周囲の空白部を、ドットエディタなどを使って透過させてください。(あるいはinkscapeを使ってキャラ絵を書いて、PNGエクスポートする、などの手法もあります。)
ジャンプさえ出来る絵であればいいので、すでに手元にゲーム用キャラチップっぽい大きさの生き物の絵があるなら、別に人物の絵である必要もなく、動物の絵でもモンスター画でも何でも構いません。
さて、なんとかジャンプの実験に成功したら、ついつい次の目標で「右歩行中にジャンプして右斜め上に飛ぶ」機能とかを実装したくなりますが、本ページはRPGの教科書なので、アクション機能については説明を省略します。
- ミニゲーム同梱時のカウンタ管理
ミニゲームとしてターン制RPG以外のジャンルのゲームを同梱するなら(たとえばシューティングなど。ファミコン『さんまの名探偵』では推理ゲーム中、シューティングゲームのミニゲームがあった)、別途、そのミニゲーム用の各モード前の共通部分に、ミニゲーム用のカウンタが必要になるかもしれません。
ターン制RPGとアクショゲーム・シューティングゲームは、カウンターに要求される待機時間が違ってくることと(ターン制RPGの操作はゆっくりめでいいので待機時間を多目にとれるが、しかしアクションは機敏な動作に対応するため待機時間が短い)、カウンタが目標値に到達したときの処理がRPGとアクションとで違うので(たとえばRPGなら決定ボタンを押したままの状態なら目標値の直後に受け付けしてもいいが、アクションゲームでは決定ボタンがジャンプやパンチに対応していることから、プレイヤーにパンチ連打などのためにボタン連打させたい場合は目標値直後の処理を変える必要があることなどから)、RPGとアクションを混在させるのは(可能ではあるが)難しいことが予想できます。
だから、もしあるゲーム中に別ジャンルのミニゲームを同梱するなら、メインとなるホスト側のジャンルを決めて、そのメインジャンルのカウンタだけをwhileループの冒頭でカウントアップすると安全かと思います。たとえば本ページはRPGなので、メインのジャンルはRPGなのでwhileループの序盤ではRPG用のカウンタだけをカウントアップ、という事になるでしょう。シューティングについては、while冒頭ではカウントアップしないか、あるいはRPG用のカウンタの流用で済ますかどのちらかです。
もしwhileループの冒頭でシューティング用のカウンタまで記述してしまうと、コードの管理が複雑になってしまうので、避けたほうが安全でしょう。
普段は使わないミニゲームのカウンタのせいで、普段から使うメインジャンル(本ページではRPG)のカウンタの可読性が下がるのは避けたいです。
RPG用のカウンタをシューティングに流用するのは、シューティング用にコードが最適化されてないので軽量性が若干は下がりアプリが重くなりますが、しかしゲーム制作ツールのRPGツクールやウディタなどで製作されたアクションゲームやシューティングゲームなどは、こういった流用の一種でしょう。
アイテム関係
[編集]アイテムの定義
[編集]アイテム表示
[編集]アイテム表示における、上詰めの自動化などを解説しています。
設計方針
[編集]ゲーム全体を先に作る
[編集]ともかく、ゲーム全体を先に作るのが先決です。ニュアンスは違いますがアトラス社いわく(ゲーム開発では)「ゲーム全体に全体に値を回すのが先」という格言があります[16]。
プレイヤーが見たいのは、ゲーム全体のストーリーやテンポといったゲームの全体像です。カーソルの動きとかウィンドウの動きとかは、あくまで補助的であり、そういったUIに対するプレイヤーの興味も、あくまでオマケの範囲でしかないのです。
似た機能を複製する場合
[編集]複製前の確認事項
[編集]- 短さよりも可読性を重視せよ
世の中には、残念ながら時々、他人の理解しやすさを無視して、やたらと短いコードを書く、独りよがりな人がいます。
しかし、たとえ仕事としての集団作業であっても、要求されるのは、同僚や後輩などが読んだときの分かりやすさです(「可読性」と言います)。
なので、少しぐらいコードが長くなってもいいので、分かり易いコードを書きましょう。
具体的には、できるだけ、どこの入門書にもあるような基本的な機能を使ってコードするのが安全です。具体的には、できるだけ、せいぜい構造体(およびC++ならクラスの構造体的な使い方)や配列や関数あたりまで、の機能どまりです。
また、「どうしても」と言った必要性のない限り、その分野の一般の入門書に書かれてない機能(たとえばメモリ管理の組込み関数など)や、解説の極端に乏しい機能は(入門書なっても巻末の機能一覧にだけしか記載のないような機能)、利用を控えましょう。
そもそも、一般入門書に書かれてない機能は、取扱いが難しかったりして、設定などの確認不足で使うとむしろバグの原因になりやすかったりとか、学習コストの高さなどの難しさがあるから、入門書から除外されているのです。なのに、そういう難解な機能を多用するのは、あまりよくプログラミングの仕事が分かってない自称プロかと思われても仕方ありません。
- ゲームとして実態のある関数や配列を作れ
関数や配列をつくるときは、なるべく、ゲーム用語で説明できる関数および配列だけを作りましょう。たとえば「戦闘コマンド選択関数」や「モンスターデータベース配列」のような感じです。
けっして、そういうゲーム用語とは無関係に、単に抽象的に関数を呼び出す関数を呼び出すような関数を呼び出す関数、とか、配列を読む込む配列を読む込むような配列、のような高階層的な機能は、たとえそれでコードが短くなっても、同僚や後輩などが管理をしづらくなるので、避けましょう。
書籍『Game Programing Patterns』にも、Amazonのweb立ち読みのP13に
「コードの抽象化に凝りすぎると、コードベースのアーキテクチャはどんどんと複雑化していきます。やがてインターフェースと抽象化レイヤだらけになり、プラグインシステム、抽象規定システム、仮想メソッドが溢れかえり、」
とあります。その結果、
(中略)「抽象化の階層の中を探し回って実際に仕事をしているコードにたどり着くまでに、果てしなく時間が掛かります。」
と書籍『Game Programing Patterns』にあります。
実際にゲームを作った事がある人ならばこの問題に気づきますが、世間には知ったかぶりで逆の事を言う人がいます。オブジェクト指向だのポインタだの何だの理屈をこねて、抽象化のレイヤを増やそうとする人です。ダマされないようにしましょう。
もちろん、関数から関数を呼び出してもいい場合もあり、たとえば「RPGで戦闘モードの関数から、攻撃コマンド関数を呼び出す関数から、さらに攻撃対象の選択をする関数を記述する」といったようなゲーム仕様に直接的に結びついた関数ならば、普通のゲーマーなら理解できるので、書いても平気でしょう。ですが、それはゲームとしての実態に対応する共通化です。けっして、ゲームの実態に無関係の共通化ではありません。
- というか、そういう無意味な抽象化を避けないと、あなたが会社から避けられるでしょう。(これはゲームに限らず、数学以外の物理学や化学で扱う数式も同様で、たとえば理系大学の論文指導やゼミなどで、なるべく物理的な実態や化学的な実態に対応のある立式をするように、よく大学の卒業研究では要求されています。具体的には、「質量(mass)を表す文字式には m を使え」(高校レベルですが)みたいに既存の慣習に合わせるように指導されます。たとえ数学的には矛盾のない立式でも、物理学の研究室なのに「質量に記号 a 、速度に記号 b 」みたいな書き方をする学生は、教授から学生がダメ出しをされます。)
IT業界だけなく法律業界でも、むやみに高階層的な法令を設計するとその法令の構造が複雑になる現象は知られていますDaniel Martin Katz, Corinna Coupette, Janis Beckedorf & Dirk Hartung "Complex societies and the growth of the law" , nature.com, Published: 30 October 2020 。近年、世界各国でIT的な考え方を使って法律の設計論を研究しようという学問分野があり、その学問でそう指摘されています。これらのIT的な法学では、法律中の単語数、階層数、法律間の相互引用数が多ければ多いほど、その法律の構造は複雑になると考えられています。ご参考に。
- 仕様そのものの類似性に気を配れ
また、ゲーム中で、仕様ではあまり類似性のない処理は、たとえ偶然的にコードの実装が似ていても、むやみに共通化して同じ関数にしたり配列にしたりするのは避けましょう。なぜなら他人(後輩など)が管理しづらいからです。
ゲーム業界以外でも、googleの技術者が似たような注意をしています)[17]。たまたま現状ではコード記述が似ているコードでも、違う概念(※wiki追記:異なる目的)に対応するコードは、けっして統合してはいけないと注意喚起をしています。 なお、参照サイトの
The approach on the right seems to violate the DRY principle since the ValueError checks are coincidentally the same. However, tasks and payments represent distinct concepts with potentially diverging logic. If payment date later required a new validation, you could easily add it to the right-hand code; adding it to the left-hand code is much more invasive.
の部分です。
異なる概念(concepts)を表している2か所のコードが、偶然にも(coincidentally )コードが同じ場合があります(the same)。そのようなコードを、ときどき新人は統一してしまいがちですが、しかし、それをグーグル技術者は戒めて(いましめて)います。万が一、別々の概念を持つコードをひとつに統一してしまうと、そのコードをそれから各概念の用途に応じて改修するのは、google 技術者たちにとってすら難しく、まるで外科手術的(invasive)になってしまいますよと警告しています。
もちろん、共通化してもいい場合もあり、たとえば「回復アイテムによる回復対象を選ぶ関数」などは、
- たとえば戦闘モードでの「RPGで戦闘モードの関数から、(攻撃コマンドではなく)道具コマンド関数を呼び出す関数から、回復アイテムを選んだ時に、回復対象の味方キャラを選択をする関数を記述する」と、
- マップメニューモードでの「マップメニュー画面モードの関数から呼び出す、道具コマンドの関数で、回復アイテムを選んだ際に、道具の使用対象を選ぶ関数」
が、ともに仕様が「回復アイテムを選んだ時の使用対象の味方を選ぶ」と似ているので、場合によっては共通化するのも良いかもしれません(職場によるだろう。共通化しないほうが良い場合もある。たとえば戦闘モードでは敵にも回復アイテムを使えるゲームも存在する一方、マップメニュー画面では味方にしか回復アイテムを使えないので細部が異なるから)。
しかし、仕様自体すらも似ていない部分を共通化するのは、ダメでしょう。
たとえば、もしたまたま、「戦闘モードでのコマンド選択時の関数」(まだ道具コマンドを入力する前なので、どのアイテムを使うからすら選んでいない段階)と、「マップメニュー画面モードで道具コマンドのあとに使用対象キャラを選ぶときの関数」が、もし偶然にコードが似ていたとしても、そういう仕様的にあまり関連性の無いコードは共通化されても管理しづらくなるので、共通化するのをやめましょう。
ほかの例では、たとえばHPとMPの表示は「戦闘画面」にも「メニュー画面」にもあるでしょうが、だからといって両画面で使いまわせる「HpMp共通表示関数」みたいなのを作るのは危険です。
具体的になぜ危険かと言うと、その共通表示関数にはif文章が増えますので(戦闘画面とメニュー画面とで表示位置などが微妙に違うため)、デバッグの手間として「どんな場合の戦闘画面とメニュー画面でも、はたして本当にif文が正しく機能しているか」という手間が増えます。
一方、ベタ書きで直接的に戦闘モードにHP表示とMP表示、一方で同様にベタ書きでメニュー画面モードにもHP表示とMP表示を書けば、こういう余計なデバッグの手間は増えません。
HP表示とMP表示のたった2行ていどのコードを「HpMp共通関数」で1行にしても、何箇所かで呼び出すからそのぶん減少量は倍増しますが(たとえば4箇所で呼び出したとすれば 1×4=4で4行ほど減る)、しかし関数を新たに作るので差し引きで、うまく減らせても結局は1~2行くらいしか減らせません。
コード量を簡単なコードの1~2行しか減らせないのにデバッグの手間を大幅に増やし、まったく割に合いません。
また、共通関数の側に呼び出し先の戦闘画面やメニュー画面の情報があるので、共通関数を使ってしまった場合にはデバッグをする際に戦闘画面のコードなども調べなければなりません。こういう事情も加えて、デバッグの手間がさらに増えやすいという問題があります。
教訓として今回の初心者プログラマーにありがちな失敗例を一般化するなら、もし「リファクタリング」のつもりで共通化してif文が増える場合、もしかしたらデバッグの手間が増えてしまっている傾向があるので、上述のような間違った共通化している可能性があります。
wikisource s:プログラマが知るべき97のこと/共有は慎重に にも似たような話題があります。
私がコードをライブラリ化してしまったことで、それを利用する部分には依存関係が生じました。まるで、一本の靴ひもを、両足の靴に通したような状態になったのです。ライブラリのコードを1行変更しただけで、その影響は複数箇所に及びます。互いに独立していた時なら、該当部分の保守コストは無視できるほど小さかったのに、ライブラリ化してから、変更のたびに大変な手間をかけてテストをする必要が生じました。
(引用)
順番は前後しますが、参照先ウィキソースに、下記のようにコンテキスト(文脈)の話があります。
それは 「コンテキスト」です。 たとえシステム内に同様の処理を行う部分が2つあったとしても、両者のシステムにおける役割が大きく異なっていれば、再利用によるメリットは小さいのです。私がコードをライブラリ化するまで、そのコードを利用する部分間に依存関係はまったくありませんでした。元々の成り立ちが全然違うコードだったのです。従って、状況やニーズが変われば、その後各部分のロジックにはまったく別の変更が必要になる可能性が高いということです。たとえコードが4行ほどのもので、行っていることが同じだったとしても、それはたまたま一時的にそうなっていただけのことです。私が入ってくるより前は、むしろ一致していない方が普通だったのです。
(引用)
高校や大学など学校では、コードの再利用を重要だと教えるかもしれませんし、たしかにそれも重要なのですが、しかしだからといってコンテキストの異なるも のまでむやみに共通化してはいけません。
大学では「再利用」を優れたソフトウェア開発プロジェクトの象徴として教わってきました。どんな論文や教科書を読んでもそう書いてあったし、経験豊かなプロのソフトウェア技術者もそう語っていたのに、それらは全部間違いだった、ということでしょうか。 考えた結果わかったのは、私が1つ重要なことを見逃していた、ということです。 それは 「コンテキスト」です。
(引用) ※ そして上述の「たとえシステム内に同様の処理を行う部分が2つあったとしても」に続きます。 この情報を見るかぎり、どうも日本の学校だけでなく海外の学校教育でも、コンテキストの都合を考えずにむやみにコードをライブラリ的に共通化して短縮ばかりを考える教育をされる傾向が2010年頃まではあったようです。
ゲームでは上述の説明では暗黙の前提として何となく戦闘システムやメニューシステムなどの基本システムを想定して説明しましたが、なにも基本システムに限らず(RPGなどの)特殊イベントなどでも同様のことが考えられます。
ゲーム中のストーリー進行に関わる特殊イベントでたまたま似たようなイベントがあったからといって、むやみにシステムを共通化してはいけないのです。もし、仕様変更などの要望が上司などから加わった場合、むやみに共通化してしまっていたら、あとから修正するのが大変になってしまいます。
他の書籍でもそうです。洋ゲー「ゴーストオブツシマ」の開発者の一人による書籍『ルールズ・オブ・プログラミング ―より良いコードを書くための21のルール』( 2023/8/28、Chris Zimmerman (著), 久富木 隆一 (翻訳))でも、著者が新人によくする指導法として「問題の例が三つ集まるまで一般性のある解法を書くことを許さない」というのがあります(amazon見本を参照)。このChrisの会社でも、大学を出たばかりの新人は、新人が1つの問題をみつけると、他の問題も解ける一般的なプログラミングを書こうとするのですが、Chrisは「そういうでかい問題は解かなくていい」と指導して、目の前の問題だけをまず解くことに集中させるように指導しています。そういうでかい問題を解こうとするプログラムは、往々にして、バグを潜みやすかたり、理解のための学習コストが高くなりがちなので、ゲーム開発では避けるべきだということを Chris はこの著書で述べています。 ともかく、目の前に無い、でかい問題を一般化して解こうとすると、メンテ性の悪いプログラムイが出来上がりやすいのです。
予備知識のない新人はこういうミスをしがちです。気をつけましょう。
- 視線移動の問題
関数化の是非の判断について、(けっして、たまたまコードが似ているかではなく、)仕様そのものの類似性にこだわる理由は、上記の変更可能性の他に、別の理由もあります。それは、「視線移動」の問題です。コーディング時の、視線移動(およびマウスホイールなどの操作量)です。
あえて関数化しないノウハウの出典としては、書籍では「A Philosophy of Software Design」( John Ousterhout 著)に、視線移動の事が書いてあるようです[18](wiki編集者が未読。読んだ人が編集に協力して上書きしれくれると、うれしい)。
もし、なんでもかんでも関数にしてしまうと、視線移動が多くなってしまうとの事です。そして視線移動をしないと分かりづらいので、なるべくドキュメントを残す必要があると著者ジョン(John)先生が言っているとの事らしいです[19]。
これは裏を返せば、ドキュメント化しづらい処理は、あまり関数にせず、代わりに(「関」数ではなく)変数などの、あまり視線移動をしなくて済むような別の方法で、プログラミングを切り抜ける必要があるかと思います。
ジョンは言ってないでしょうが(そもそもジョンの書籍はゲーム設計論の本ではない)、このため結局、私たちはゲーム的な仕様そのものの類似性にこだわる必要があるでしょう。単にコードが似ているかではなく、仕様書に落とし込んだときに、その仕様書そのものの記述が似ているか、といった事も、気にかける必要があります。
また、上記ジョンの書籍では言及していないかもしれませんが、コードだけでなく、加えて仕様書を読む際の視線移動のことも、可能ならば考える必要があるかもしれません。
とはいえ、「ゲームは面白くてナンボ」でしょうから、あまり視線移動の少なさそのものだけにコダワルのも一考ですが、しかし視線移動が多い設計は、それなりのメンテ上のコストがあるかもしれません。
本ページのこういった分野を考察する学問についての名称ですが、「ソフトウェア工学」で良いだろうと思われます。書籍『ルールズ・オブ・ゲームプログラミング』の冒頭の『本書への賛辞』に
『ルールズ・オブ・ゲームプログラミング』には、どんなソフトウェアエンジニアでも自己のレベルを次のレベルに引き上げるために使える、実用的経験則としてのルールが満載だ。
とあります。エンジニアとは技術者という意味です。エンジニアリングを日本語に訳すと「工学」です。なのでソフトウェアエンジニアリングなら「ソフトウェア工学」です。
ソフトウェア工学では「DRY原則」(Don't Repeat Your Self、 「繰り返しを避ける」の意味)という考えかたがあり、同じコードの繰り返しを悪とする考え方がありますが、しかし実務は上記のように、必ずしも「DRY原則」のとおりとは言い切れません。もしくは、世間一般の「DRY原則」の解釈のなかにはコンテキストの考え方を見落とした、まちがった解釈が流布している場合もあるので注意が必要、ということかもしれません。
よほど長いコードの繰り返しやら10回や20回以上とかの繰り返しなら実務上の都合からコードの重複を避けたほうが良い場合や必要もあるかもしれませんが、しかし、そうでない場合でたった数行ていどの少々のコードやらそれらの2回程度の繰り返しならばコンテキストの都合からは似たようなコードをあえて共通化しないほうが安全な場合もあります。
初心者プログラマーはこういった事をいっぺんには体得できないでしょうから、一見すると、本来なら合理的なコードが、初心者の目には少し汚く見え、「少しだけ可読性が悪い」ように感じがちです。つまり、初心者目線で少しだけ可読性が悪いぐらいのコードこそが、実は本当は可読しやすいコードです。
具体的手順
[編集]まず、それらの機能の共通点を探します。次に、その共通点を抽象化します。抽象化のコストと、それを再テストするコストの合計が、そのままにしておくコストよりも高く付くと考えられるならば、抽象化は後回しにしても構いません。
テキスト比較ツールを使うと、効率的に違う部分を強調して表示することができます。
GNUのdiffutils、Gitなどのバージョン管理システム、あるいは「ベクター」や「窓の杜」からインストールすることができます。
リファクタリング
[編集]モードの管理手法について
[編集]戦闘モードとか、マップ画面モードとか、メニューモードとか、そういうののハナシです。とりあえず「モード」と言いましたが、ゲーム業界で何と言うのか知りません。もしかしたら、モードではなく「戦闘シーン」とか「戦闘パート」とか言うのかもしれません。ただし、書籍『ゲームプランとデザインの教科書』が、架空のスマホゲーム企画書で「モード」という言葉を使っています。「移動先指定モード」とか[20]。
2Dマップ
[編集]暗黙の前提ですが、マップ中でキャラを移動させるためのキー操作は、十字キーで行わせるべきです。
当然だと思うでしょうが、しかし90年代の集英社「Vジャンプ」にあるゲーム取材記事によると、意外と当然ではないようであり、当時のゲーム会社の新人にUIを考えさせると、よくコマンドを増やした提案をしがちだったようです。
あるゲーム会社では、そういうコマンド増加の提案に対し、ゲーム会社の先輩はよく、コマンド「あるく」という例をダメな例として説明して教えていたようです。
たとえば、もし十字キーではなく、コマンド「あるく」→
どちらのあし? みぎあし ひだりあし
→
ほうがく? きた にし ひがし みなみ
→「ゆうしゃ ああああ は みぎあしを まえに だした。
ゆうしゃ ああああ は ひがしにいっぽ すすんだ。」
というゲームがあったらクソゲーでしょ?操作が面倒でしょ?こういうふうに、操作の多い余計なコマンドは削るんだよ。 ・・・と先輩社員は新人に比喩で説明してたようです。
マップのアルゴリズム
[編集]方針だけ述べる。具体的なコードは、上手い人のコードを参考にしよう(コードがけっこう長くなり、紹介がメンドウくさい)。
2Dマップがあると、俄然、RPGっぽく見えるようになって、ヤル気が出るので、さあ作ろう。
マップのデータは、ふつう、2次元配列で書く。
まず、グローバル領域で、たとえば
static int maptable[10][10] = {
{ 0,0,0,0,0,0,0,0,0,0 }, //0 y
{ 0,0,0,0,0,0,0,0,0,0 }, //1
{ 0,0,0,0,0,0,0,0,0,0 }, //2
{ 0,0,0,0,0,0,0,0,0,0 }, //3
{ 0,0,0,0,0,0,0,0,0,0 }, //4
{ 0,0,0,0,0,0,0,0,0,0 }, //5
{ 0,0,0,0,0,0,0,0,0,0 } //6
};
のように、とりあえず配列を確保しよう。
ここで重要なのは、 確保した配列のタテとヨコは、ヨコの並びをx方向として、タテの並びをy方向としたとき、
maptable[y][x]
のように確保されていることに注意する。
さて、各配列に「0」を入れても、はたして0番が何を意味するか、まだ何も決めていない。
ゲームにもよるが、とりあえず、何もマップチップを上書きしない領域だと定義しよう。
背景色と同じ色のベタ塗りのマップチップを作っておけば、それで上書きすれば、あたかも何も上書きしてないように見える。
いちいちif文などで、何も上書きしないように場合わけをするのもメンドウであるので、どっちの方式にするか、決めておこう。
さて、マップ用の配列は、作成したいマップで最低限必要なマス目よりも、やや大きめの領域を確保しておく必要がある。
たとえば、5×5のマップを作りたいなら、配列ではもっと大目に、たとえば 8×7 とか確保しておく必要がある。
もし、なんらかのバグで確保されていない領域を呼び出してしまうと、プログラムがエラーで異常停止してしまう。
たとえば、一番左端をもしx=0とした場合、もしこの場所に主人公がいると、さらにプレイヤーが「左端に行こう」と考えて、左ボタンを押したときに、エラーで異常停止してしまう。
こういう停止はメンドウくさいので、だったら念のため、最初から、マップを移動可能な場所よりも何マスか大目に確保しておくと安全である。
さて、まだ配列を確保しただけなので、「0」が何かとか、「1」が何かとか、まったく定義していない。
とりあえず、
- 0番は、進入不可能の暗闇。
- 1番は、床とか、草原とか平地とか、とにかく歩ける場所
としよう。 だと思ってればイイ。
たとえば、もし配列 maptable の宣言が、
static int maptable[10][10] = {
{ 0,0,0,0,0,0,0,0,0,0 }, //0 y
{ 0,0,1,1,1,1,1,1,0,0 }, //1
{ 0,0,1,1,1,1,1,1,0,0 }, //2
{ 0,0,1,1,1,1,1,1,0,0 }, //3
{ 0,0,1,1,1,1,1,1,0,0 }, //4
{ 0,0,0,0,0,0,0,0,0,0 }, //5
{ 0,0,0,0,0,0,0,0,0,0 } //6
};
のような宣言だったら、このマップでは真ん中のほうに4マス×5マスの移動可能な地帯がある。 その周囲は移動不可能になっている。
ともかく、このように、プログラマーは、まず、何番のマップチップが何を現すかということを、脳内で設計しておく必要がある。
配列の範囲外の読み込みエラー防止のためも兼ねて、0番は、すべてのゲームキャラが進入禁止としておくのは安全だろう。
あとはもう、win32 API などの機能を使って、マップチップの画像を配列にもとづいて各マスごとに表示すればいい。
前提として、まずマップチップ画像の読み込みを行う必要がある。あるいは、画像をあらかじめ実行ファイルに組み込んでおく必要がある。
画像の実行ファイルへの組み込みは、Visual C++ にそういう機能があるので、それを使えばいい。
画像を読み込みたいなら、ゲーム起動時にでも読み込んでおけばいい。
イメージ的にコードの雰囲気(ふんいき)を書くと、たとえば case WM_PAINT:
の節に、下記のように for文などを使って画像ハンドらに画像を代入するコードを裏画面に書いていき、最後にまとめて本画面に描画することになる。(「裏画面」とか「ダブルバッファリング」と言われるテクニックを使います。詳しくはネット検索して調べてください。)
- (イメージ)
-
- (※ あくまでイメージです。このままでは、動きません。)
int iTemp;
for (x_map = 0; x_map <= 9; ++x_map)
{
for (y_map = 0; y_map <= 6; ++y_map)
{
iTemp = maptable[y_map][x_map]; // 配列の文字が長いので、いったんiに置き換え
hbmp = hbmp_mapchip_list[iTemp].hbmp_mapchip;
SelectObject(hMdc, hbmp);
BitBlt(hbackDC, 225 + x_map * 32, 140 + y_map * 32, 32, 32, hMdc, 0, 0, SRCCOPY);
// DeleteDC(hMdc); // これを入れると、マップが表示されない。
}
}
// 裏画面から本画面に転送
BitBlt(hdc, 0, 0, 700, 500, hbackDC, 0, 0, SRCCOPY);
hbmp = NULL; // 初期化。
// 中間ハンドルからhbmpを解除
SelectObject(hMdc, NULL);
SelectObject(hbackDC, NULL);
// 不要になった中間物を削除
DeleteDC(hbackDC);
DeleteDC(hMdc);
DeleteObject(hbmp);
}
SelectObject や BitBlt は、Win32 API の専用の関数なので、分からなければ読者で調べてください。
なお、マップチップ画像そのものの作成は、単にWindowsアクセサリ『ペイント』などの画像作成アプリでビットマップ画像を用意すればいい。
ツクールやウディタなどだと、いくつかのマップチップをまとめた「タイルセット」などを取り扱うが、実はプログラミング的には、わざわざタイルセットを作らなくても、マップチップ1個ずつを読みとりすることも可能である(単にwin32APIのビットマップ画像の読みとりの機能を使えばいい)。
(win32 API の初期設定のままではビットマップしか表示できないハズ。PNGなどを使いたいなら、GDI+の設定を行うこと。GDI+については説明を省略。)。
なお、マップチップのサイズの規格は、よくある規格は16ピクセルの倍数で、「px」をピクセル単位の意味として16px×16pxまたは32px×32pxまたは64px×64pxのマップチップ規格がよくある(ツクールやウディタのマップチップのサイズ規格も、この系統)。
とりあえず、私たちにとって作るのがラクなのは、小さいほうが作成がラクなので、とりあえず16×16のマップチップを作ろう。
プログラム技術としてはマップ表示は単なる二次元配列なので難しいことは無い。しかし、その他の準備が忙しく、たとえばマップチップ用のビットマップを用意したりとか、あるいはキーボード操作のプログラムを作ったりとか、そういう準備が難しい。
これだけだと、まだマップを表示しただけなので、さらに主人公キャラのキャラチップとか、主人公がx=何マス目、y=何マス目にいるかの変数とかも宣言する必要がある。
主人公チップを移動させる際、ドラクエ1みたいに1ピクセルずつ動かしたいと思うだろうが(実際は何ピクセルかしらないが)、しかしそれは難しい。まずは、十字ボタンを押したら、その方向にいきなり隣のマスに動く仕組みをつくろう。
しかし、これすら、また難しい。
win32APIなら、ボタンを押して話す動作で1回の入力と判断してくれる。 なので、win32APIで隣マス移動の仕組みを作るのは、比較的にラクである。
しかしDXライブラリの場合、1瞬だけボタンを押しただけでも、何十回も入力があると判断され、 いっきに壁際のマスまで動いてしまう。
なのでDXライブラリのマップ移動の作成の場合、まずはタイマー系の関数を利用し、一定時間のボタンの押し続けがあって初めて「入力された」と判定すうプログラムが追加になる。
なお、win32APIでも別のシーン(たとえば戦闘モードでのコマンド決定後の自動処理など)でタイマー自作が必要になるので、どちらにせよラクではない。
最終的には、21世紀的な一般的なゲームを作りたいなら、(win32APIではなく)DXライブラリで作るのがラクである。 (ただし本セクションでは説明のしやすさの都合から、win32APIで説明した。)
もし、ドラクエ風に1ピクセル(ないし数ピクセル)ずつアニメーション的に動かしたいなら、DXライブラリがほぼ必須であり(原理的にはwindowsAPIでも出来るが、しかしwindowAPIでのアニメーション表現はとても手間が多く難しく、強く薦めない。)、DXライブラリで開発しているRPGコードに、タイマー計算するコードと組み合わせて、下記イメージのようになる。
- (イメージ)
-
- (※ あくまでイメージです。このままでは、動きません。)
if (moving == 1 && nyuuryokuMatiLR > 0) { // nyuuryokumati は「入力待ち」時間の意味
nyuuryokuMatiLR = nyuuryokuMatiLR - 1; // タイマーとして流用
}
// 移動の終了処理
if (hero1_direction == rightward && moving == 1 && nyuuryokuMatiLR <= 0) {
keyEnableRight = 1; // moving 回復までに時間が掛かるので、ここは1に。
nyuuryokuMatiLR = waitTime1;
nyuuryokuMatiLeft = waitTime1;
nyuuryokuMatiRight = waitTime1;
xPosi++; // 右へ1マスだけ移動
moving = 0;
townFlag = 0;
}
} // 右移動
// nyuuryokumati は「入力待ち」時間の意味
上記コードの仕組みは割とひどく、マップ移動中の特定の左右方向用のタイマーを、今後の戦闘モードなど他のすべてのタイマーと流用するという、ひどいコードである。
ナナメ移動にも対応したりを考える場合、左右(LR)方向と上下(UpDown)方向のキーボード入力を別々の変数として処理する必要があったりと、割と面倒くさい。
移動先が移動可能でないカベや川なのに移動できたらおかしいので(ドラクエだと川は移動不能。『信長の野望』での渡河とか野暮にツッコまないこと。)、
事前にそういう移動可能判定を色々とクリアしたら、移動中モードとしてmovingが1になるようにしているコードである(長くなるので上記コードでは省略した)。
キーボード入力の判定は、事前のmoving側の判定計算ですでに行っているので、上記コードでは省かれている。実際にはさらに、下記コードのようなキーボード入力判定プログラムが、上記コードの直前あたりに付け足される必要がある。
// 移動先予定地の入場可否の判定
if (CheckHitKey(KEY_INPUT_RIGHT) == 1 && keyEnableRight == 1 && moving == 0) {
if (map1table[yPosi][xPosi + 1] == 1) { destMovable = 0; }
if (map1table[yPosi][xPosi + 1] == 0) { destMovable = 1; }
// 入場可能ならフラグ設定
if (destMovable == 1) { // destとは目的地点 destination の略。
moving = 1;
hero1_direction = rightward;
keyEnableRight = 0;
nyuuryokuMatiLR = waitTime1;
}
}
いったん移動し終わったら、もはや移動中ではないので、再度movingをゼロに戻し、また移動可能判定を調べなおす、という手間になる。
マップ移動だけでも、こんな手間になる。
だから、市販の『中学生でも分かるゲームプログラミング』的な題名の本でこういう手間が無いのは、 それはその市販本の著者が、初心者が苦手感を抱かないように独自のライブラリなどで上記のような手間をライブラリ側に隠蔽する工夫などをしてるだけにすぎない。
ネットに出回るデマの、「RPG風のUIをつくるのは簡単」というのは、つくづくウソである。ドラクエ1風のUIですら、なかなか面倒である。 (『中学生でも分かるゲームプログラミング』的な本の手間だけでドラクエ1風UIが作れると思ってる、本を読んだだけの知ったかぶりが、ゲームプログラミング経験者ヅラしているということであろう。)
ゲームでは、キャラクタの移動において、背景を固定してキャラの位置を動かす方式と、もうひとつの方式としてキャラを中心ちかくに固定して背景をキャラ進行方向とは逆に送る方法があります(たとえばキャラが右に移動するには背景を左に送る方式)。
もし背景のほうをスライドさせる方式の場合、背景のフレームレートは基本、けっして余計なウェイト(待機時間)をはさまずに、つまり必ず 60FPS( または120FPS )にして滑らかに背景を動かす必要があります。
根拠として、すでにアニメ業界で同様のことについて実験結果が知られており、昭和の後半の時代にガンダムなどを作ってる会社(サンライズ社)が、当時に実験済みであり、実験として「アニメ業界ではキャラクタの動画は1秒間に8枚だが(※ 現在でいう 7~8 FPS くらい)、背景(アニメの普通は背景は 30FPS くらいで動かしている)もキャラみたいに1秒間に8枚またはそれ以下の枚数で動かしてみたらどうなるか? ためしに社内で実験してみよう」と背景のスライドのコマ数を色々と変えてみて背景送りスピードにウェイトをはさんでみる実験したことがあります。そのアニメ会社の実験の結果、(30FPSでない10FPS以下程度での背景の動かす方式のは)「見るに耐えなかった」という結果が、ガンダムシリーズの生みの親である富野監督のインタビューなどで公言されています。
人間の視覚の生理的な感覚はアニメファンの目でもゲームファンの目でもそう違いはないはずですので(同じ人間という生物の視覚ですので)、せっかくアニメ業界が公開してくれた過去の知識を活用しましょう。
なお、2020年代の現代のTVアニメでも、歩行シーンなどで背景を動かす方式の場合、動画プレイヤーでコマ送りをして確認すると、キャラは3コマごとに動きますが、背景は1コマごとに動いています。
マップレイヤー画像について
[編集]歩行グラフィック
[編集]ゲームにおけるキャラチップの歩行アニメーションの作画の定石は、テレビアニメの歩行の作画の定石とは、まったく違います。
説明の単純化のため、右向きの歩行を例に説明します。
3枚歩き
[編集]まず、ツクール水準の2D-RPGの歩行グラフィックではなんと、歩き始めの作画の立ちポーズのドット絵と、横向き時にプレイヤーから見てキャラ歩行中における右足と左足とが重なっているドット絵(便宜上「中ポーズ」と呼ぶとする)が、なんと同じドット絵です(テレビアニメ作画の常識とは大きく異なる)。
つまり2Dゲームのドット絵では、歩き始めと歩き中の中ポースとに、区別がないのです。
これは、実際にキャラチップのタイルと、キャラのゲーム中での歩行動画を録画して見比べると分かります。
テレビアニメ産業では、歩きお よび 走りの作画は、基本的になるべく滑らかに見せるために、6枚の絵を使います。 だからアニメ業界では「6枚歩き」とか「6枚走り」とか言います。
しかしゲームは違います。上記の言い方に習うなら、ゲームは「3枚歩き」です。「3枚歩き」というのは別にゲーム業界の用語ではなく、テレビアニメ業界での数え方をそのままゲーム業界に置き換えただけの本wikiでの独自的な呼び方です。
しかもゲームのドット絵では、走りと歩きの区別がありません。つまり、歩きのドット絵を、走りのドット絵でも使いまわします。
つまりゲームではドット絵だけなら 「3枚歩き」=「3枚走り」 でもあります。
ドット絵のゲームにおける走りと歩きの区別は単に、キャラチップのスライドさせる早さを切り替えることで区別しているだけです。
なお、テレビアニメ産業などでは、正面向きかつ走りシーン(ゲームにおける下キー入力時に相当)なら例外的に(6枚ではなく)4枚作画でも走りを描けることが知られています。なぜこれが通用するかというと、よく分析で言われるのは、6枚でなく4枚だと滑らかさはなくなりますが、しかし歩きではなく走りであるので動きに少々のギャップがあっても勢いがむしろ強調されるという点と、また6枚時の中ポーズのシルエットと4枚時の中ポーズノシルエットがあまり変わらないという点があるため、テレビアニメ業界では正面走りだけ「4枚走り」でも可能なのです。
しかしゲーム産業では正面向きでないのに、右向き左向き前向き後ろ向きすべてで、なんと「3枚歩き走り」なのです。ドット絵のゲームではキャラチップが小さいので、なんと3枚で歩きも走りも表現できてしまうのです。
このように、ゲームとアニメとでアニメーションの常識が大幅に異なります。
しかし、両業界でも一致している作画の風習もあり、それは歩行中グラでの頭の高さの上下運動です。
アニメ作画の頭の上下運動を文字だけで説明するのは難しいので、興味ある読者は外部サイトでアニメーターの作画教育サイトを見るなり、あるいは同人フリーゲーム用の公開キャラチップ素材などを実際に目で見るなどして、読者ご自身で研究してください。
ともかく、3枚で右向きの歩行を表現できます。RPGでは上下左右の4方向が必要なので、最低でも3×4 = 12枚 の絵画必要です。
なお、斜め歩行が加わると、2倍になるので必要枚数は合計24枚になります。
- 思考回路の切り替え
もし、外部のゲーム用フリードット素材を使わずに「ゲームエンジンを一人で作る」というなら、上述のようなアニメ産業とゲーム産業での作画のギャップも知って絵を描く必要が生じるでしょう。頭の切り替えが必要になります。
今までプログラマー脳だったのを、ドット絵を描くのに絵描き脳に切り替える必要があって、しかも既存のテレビアニメ作画理論はそのままでは使えないので、ゲーム用に人気作のドット絵のグラフィックを研究しなおす必要が生じるのです。
チップ作画の留意点
[編集]3枚歩き
[編集]また、絵1枚ごとのRPGのキャラチップの作画も、テレビアニメとは違います。
まず、2D-RPGでは、前後向きのキャラチップの横幅のサイズ規格と、左右向きの横幅のサイズ規格が、RPGでは同じです。
しかし、テレビアニメでは、このような横幅はありえないのです。
なぜなら、人間が歩いていて左右の腕をふっていて前に突き出た状態の手先から胴体までの距離は、胴体の厚さに比べると、(手~胴体 の距離は)かなり長いです。
しかし、こういったリアルな腕の寸法に忠実に書くと、サイズ規格の横幅をオーバーしてしまい、少なくとも腕の片方の先がチップ外にハミ出てしまいます。
だから、進行方向側の腕だけは、2D-RPGキャラチップではあまり伸びていないです。
なお、進行中の胴体はやや進行方向に寄っています。これはリアルな人体がそうだからでしょう。
なので、もはや進行方向側の腕には、伸ばすだけの余裕がありません。
だから、進行方向側の腕を折り曲げる場合すらも存在します。または、進行方向側の腕だけ短くゴマかすなどしている場合もあります。
これは別に2010年以降の流行でなく、既に1980年代の商業ゲームでもファミコンのドラクエ3の勇者の母親のキャラチップも、実は進行方側に腕を伸ばしたときは、腕を折り曲げています。
なお、ドラクエ3の主人公である勇者は、剣を持っているので、利き腕の右手がもとから折り曲がっており、よく分かりません。
また利き腕の表現のためドラクエ3では、右向きと左向きのキャラチップを、けっして反転処理でゴマかさずに、きちんとポーズを書き分けています。
ともかく、一般にRPGの歩行キャラチップでは、進行方向の腕があまり伸びてないので、バランスを取るためにRPGでは、進行方向の反対側の手足を、目いっぱい、伸ばしています。
これはリアルで考えるとありえないし、テレビアニメでもありえない作画です。
ですが、ゲーム産業の2D-RPGでは、昔からこういう作画がよく行われています。
- 歩きの滑り
2D-RPGの歩行ではキャラは地面を滑ります。横向きグラフィックだと、比較的に確認しやすいでしょう。
テレビアニメだと、キャラが滑っているように見えなくさせるためもあってか6枚も使って作画する一方、
2Dゲームでは正反対であり、2D-RPGゲームでは「滑り上等!」な態度の作画です。
走りシーンだったら、「まるで滑っている」かのように高速で走っている身軽なキャラのようにも見えるのですが(実は、実際に足の作画が滑っているだけ)、
2D-RPGでは歩きですら滑りある作画です。
「3枚歩き」で描く以上、こういうリアリティを無視した作画ですので、あまり他のリアリティにこだわっても仕方ありません。
- 後頭部は意外と大きい
なお、横向きの顔で、後頭部の髪のある部分の大きさが、顔前面の髪のない部分の大きさと同じくらい囲う東部のほうが大きいのは、
これは現実のデッサンと同じです。後頭部は意外と大きいのです。
例としてアニメ『新世紀エヴァンゲリオン』の主人公・碇シンジの後頭部の大きさが、顔と同じくらいなのは、別にけっして「親父の碇ゲンドウが天才科学者だから息子シンジも遺伝で脳が大きくて後頭部も大きい」とかではなく、もともと人間の後頭部というのはあのくらいの大きさなのです。
- 上マツゲは頭の中間位置ぐらい
また、目より上の頭の頂上までの大きさが、アゴから上マツゲまでの長さと同じくらいなのも、これは現実と同じです。
つまり、上マツゲは、頭の中間くらいの位置です。
ついつい、髪の毛の生え際の位置につられて、目より上の頭部の大きさを小さく認識してしまいますが、それは錯覚です。実は、目より上の部分の長さは、意外と長いのです。
また、ゲームの場合、45°上の角度から見下ろしているので、すると後頭部が少し見えることも意識すると、もうすこし長く書いても構わないのかもしれませんが、しかしチップのサイズ規格に上限があるので、あまり頭部を伸ばすこともできません。
初心者の抱きそうな疑問
[編集]- キャラチップの横幅を大きくしたら?
標準的なキャラチップの前側の腕は折りたたまれています。原理的には、キャラチップの横幅を長くすれば、横向き歩きのゲーム作画において、リアルの作画や、テレビアニメの作画に近づけることはできます。
ですがそれをすると、キャラチップがそのぶん大きくなってしまい、そのせいでチップタイルの横幅が大きくなってしまいます。
正確には計算していませんが、少なくとも2.5倍くらいは横幅が長くなると思います。斜め移動も含みで考えると、通常のツクールなどのチップタイルはやや横長なのに、さらにそれを2.5倍に長くするのは、やや編集時に見づらいかもしれません。
ドット絵のキャラチップという小さくて見づらい絵の、さらに「腕」という2ドット(枠線まで含めると4ドット)くらいしかない部分に対して、そこまでする必要あるのか、意義が不明です。
このように、ゲーム演出の特有の事情もあります。
- プログラム側の事情
また、もしキャラチップの横幅の規格だけ長くすると、マップチップの横幅の規格と、食い違いが出てきます。このため、プログラムがやや難しくなります。
なるべくキャラチップ横幅とマップチップ横幅の規格の長さが近いほうが、プログラムは楽になります。
このように、プログラミング側の事情もあります。
どうしてもリアルにアニメしたい場合
[編集]- どうしても腕振りをリアルに近づけたいなら
それでも、どうしても腕の振りをリアルやテレビアニメに近づけたいなら、
キャラチップのサイズ規格を変えるのではなく、キャラチップのサイズ規格はマップチップ規格と同じくらいの長さにしたままにして、
歩行時にキャラチップを3枚横につなげるなどの方法もあるかもしれません。3枚の真ん中のチップが胴体と腕の付け根側で、隣の左右チップがそれぞれの腕の先端側に対応、という手法です。おそらくですが、ファミコン時代の古いアクションゲームでも、巨大ボスなどを、複数のチップをつなげて作画していたでしょうから、そういうのを現代的に応用する手法というわけです。
ただし、これだとキャラの処理速度の負担も、単純計算で3倍になります。(実際は、左右のキャラチップは無描画の場合もあるし、描画量が少ないので、もっと少ないが。)
主人公とかボス敵だけのキャラチップなら、グラフィック重視のゲームなら、その意義もあるかもしれません。しかし、果たしてモブキャラのキャラチップにまで、そこまでのチップ3倍にする手間を掛ける価値があるかどうか。
また、製作ツールなどの事情により、読み込みできる画像の枚数に制限のある場合があるので、あまり目立たない部分のために画像を1枚増やすのは非効率です。
ですが、べつに全キャラに6枚歩きを実装する必要もありません。お好きなように。
- ミニゲームで分離など
どうしてもドット絵で手足の動きをリアリティある書き方で細かく表現したいなら、いっそ、ゲーム本編ではなく別途ゲーム中にミニゲームでリアルな腕の振り方のあるミニゲームを組み込むなどの方法のほうが、良いかもしれません。
この方法なら、たとえばもし2D対戦アクションゲーム(スト2みたいなの)のミニゲームを組み込んだら、歩きや走りだけでなく、ジャンプだろうが座りだろうが、もはやパンチやキックや剣撃なども思う存分に大きく作画できますし、作画によってゲーム性も向上します。(アクションゲーム特有の非リアル描写もあるだろうが、本ページはRPGの教科書なので、アクションゲームの非リアル描写には深入りしない。)
背景を手書き背景イラストにすれば、マップチップのサイズ規格に縛られる必要もなくなるので、思う存分にキャラの好きな大きさで作画できますので、腕などの細かい部分も目立つ大きさで作画できるでしょう。
ただし、この方法の背景だと、スーパーマリオのような、背景の空中ブロックの上に、キャラがジャンプして飛び移ったりとかの処理は、スト2的ゲームのままでは不可能です。別途、マリオ的システムのプログラムを組む必要があります。つまり「スト2のようなスーパーマリオ」というゲーム企画を実装する必要が生じてしまいます。
また、ツクール的システムのRPGでなければ「3枚歩き」に縛られず、歩きと走りの作画を分離するのも自由ですし、「6枚走り」と「6枚歩き」のシステムにすることで「滑り」も無くせてリアリティ向上します。そこまで作画する意欲があればの話ですが。
2D-RPG歩行のドット絵の描き方
[編集]歩行グラフィックの切り替えパターンはゲームごとに違うのですが、下記コラムでは実装のラクなパターンを紹介します。
なお、ツクールやウディタの歩行パターンとは異なります(ツクールなどは、もっと複雑なパターン。後述する)。
ドット絵の書き方ですが、プログラマーの場合、絵の表示のテストをしないといけないので、絶対に最初は下書きまでに止めます。細部は書き込まないのがベストです。(下書きの手法については、別コラムでまとめてあります。)
なぜなら、もし自作プログラムでRPGをゼロから作る場合、表示プログラムのコーディングをしながら絵を描くので、プログラマー脳と絵描き脳を、まるで反復横とびのように何度も行ったり来たりします。だから絶対に、この段階では(作り始めの段階では)絵は下書きまでに止めます。下書きで描くドット絵はすごく大雑把なラフ画でいいです。
さて、いきなり4枚描く前に、まず2枚だけでいいので、方向による表示の切り替えプログラムをテストします。
たとえば最初の1枚は下向き静止画像でしょうから、残り1枚は、右向きか左向きか上向きの静止画像になると思います。
たぶん、多くの人は右向きか左向きを書くと思います。後ろ向きをまっ先に書きたがる人なんて、いるとしても一部の重度のアニメ作画マニアぐらいです(アニメーターは360度いろんな向きでポーズを描くので)。
とりあえず説明の簡単のため、私たちプログラマーは右向き静止画像のドット絵を書いたとしましょう。
さて、とりあえず下と右の2方向の表示の切り替えプログラムさえ出来れば、同様のアルゴリズムでどうせ左向きと後ろ向きの静止画像も表示できるだろうから、さっさと歩行中の足を上げてるポーズの作画に入ります。そっちのほうがプログラマ-的には楽しいと思います。
で、すでに右向きの静止ポーズは描きあがっているとして、さらに前足を上げているポーズを1枚書いたとします。つまりこの時点では右向き絵は、静止右向き絵 と 足上げ絵 の合計2枚あります
1マスの歩行に60フレームを使っている場合、とりあえず表示フレームのタイミングは二等分により、
- 前半の30フレーム中に前足あげポーズを表示、
- 後半の残り30フレーム中に静止ポーズを表示すれば、
とりあえず何とか歩いているっぽく見えます(実機で確認ずみ)。
次に後ろ足を上げているポースも書き終えたら、今度はフレーム間隔を調整して変更し、3枚の右向き絵がありますが、三等分すれば 60÷3 = 20 なので、
- 前半の20フレーム中に前足あげポーズを表示、
- 中盤の20フレーム中に後ろ足あげポーズを表示、
- 後半の残り20フレーム中に静止ポーズを表示すれば、
とりあえず歩いているっぽく見えるでしょう(確認ずみ)。
静止ポーズ中の20フレーム中に、直立ポーズのままスライドするのはリアリティ的には奇妙ですが、
しかしこの静止ポーズがないと表示テストでは隣マス(1マス目)の到着直後にさらに隣マス(2マス目)に直進するときにキャラクターがまるで膝蹴りを発動しているかのようなアニメになってしまうので(実機で確認ずみ)、
決断によって静止ポーズのまま20フレームぶんをスライドさせるべきなのです。
実はツクールやウディタなどの歩行アルゴリズムは、本ページの上記コラムのプログラムのようにはなっていません。
なお、ツクーツとウディタで、キャラの標準的な歩き方のアルゴリズムは両方とも、
- 前足を上げる状態で進む → 直立 → 後ろ足をあげる状態で進む → 直立 → 前足を上げる状態で進む → 直立(同様に繰り返し)
のように、直立ポーズを挟んで、前足あげと後ろ足あげを交互に繰り返す方式です。(最初に始まるのが前足か後ろ足かの若干の違いはあるかもしれませんが、本質的ではないので深入りしない。)
けっして、
- 前足を上げる状態で進む → 後ろ足を上げる状態で進む
のようにはなりません。
これはおそらく、直立のように見えるグラフィックが、実は歩行中の前足と後ろ足とが重なった状態を兼ねている表現だろうと思われます。
そのほか、ツクールやウディタで1マスだけ進むのを繰り返すと分かりますが、
1マスしか動かないで停止したとき、キャラチップをよく見ると、その場で足踏みして最終的に直立して止まることがあります。
ツクールもウディタもソース非公開なので不明ですが、これはプログラム的には仕組みはおそらく、
移動中の間だけ「移動中カウンタ」的な変数が進み、それが一定値になると0に戻るという仕組みを繰り返していると思います。
そして隣マスに到着時の進行判定の際、もし右ボタンが押されていなくてマスに停止しており、
移動中カウンタがまだ0に戻る前の値だったら、その値を保存したまま、
表示プログラム用にカウンタ値だけカウントアップしていって0に戻るまで表示が進むような仕組みで、実装できるかと思われます。
もし読者がツクールやウディタっぽい歩行アルゴリスムを実装したいなら、ご自身で研究してください。どのみち、ツクールとウディタで、歩行のポーズ切り替えタイミングなど微妙に違いますので、統一されていません。
どのみち、たとえばファミコン版ドラクエ3のアルゴリズムは、ツクールやウディタとも違います。ドラクエ3ではマップ上でマスに立ち止まっても、キャラチップは静止せずに足踏みを続けます。
このように歩行パターンはゲームそれぞれです。
テレビアニメの下書きとは、ドット絵は、作画の順序や手法が大きく違います。
テレビアニメの下書きでは、まずエンピツで線画を描き(「原画」(げんが)という)、あとから色を塗る(バケツ塗り)という手法です(「彩色」(さいしき)という)。(このあと「撮影」とか「編集」とか色々な工程があるが、ゲームに関係ないので省略。)
ですが、ドット絵はこれとは作画の順序や手法が大きく違います。
ドット絵グラフィッカーによって手法は個人差があるでしょうが、とりあえず一例として、下記のようなドット絵の書き方を紹介します。もちろん、コンピュータ上でのドットエディター上での作業です。
- 前後左右4方向の中ポーズ(立ちポーズ)のラフ色塗り
- ↓
- ゲームに組み込み目視で画面を見てテスト
- ↓
- テストで異常あれば下書きを修正。異常なければ次に進む
- ↓
- 前後左右の腕足振りポーズを2枚ずつ色塗りで大まかに下書き。(3枚作画なので、あと2枚が追加で必要)
- ↓
- ゲームに組み込み目視で画面を見てテスト
- ↓
- テストで異常あれば下書きを修正。異常なければ次に進む
- ↓
- これから細部の書き込みに進む。細部に興味なければここで終わる。
- ↓
- (細部の書き込みと、そのテストなど)
- ↓
- (たぶん)終わり
上記フローのような書き方は、あくまでRPGのマップ画面でのキャラチップのアニメの書き方だけに限定の手法です。だから、それ以外には、そのままでは応用できず、たとえば戦闘画面のモンスター画像などには応用できません。
あくまでマップ画面モードのキャラチップのみの話題です。
さて、ではなぜ上記のような手順で描くと合理的なのかを説明します。
もし40px × 40px 程度のドット絵なのに、テレビアニメのように色の違う箇所すべてに枠線を書こうとすると、ゲームのチップは小さいので服の模様などによってドットが黒一色(rgb値が 20,20,20 とか)で潰れてしまうので、色がなにも見えなくなってしまうから、テレビアニメ的な作画の手法は使えないのです。
だから基本、ドットの下書きでは、まず、色を大まかに塗ります。この際、ポーズも大まかに仮に決めて、下書きのときに一緒にポーズを書きことになるでししょう。
なぜ、静止ポーズの前後左右4枚を先に描くかというと、歩行中の残り8枚の絵よりも静止4枚のほうがプレイヤーの目に入る時間が長いからです。
ポーズボタンのないRPGの場合、もしプレイヤーが歩行中8枚の絵のいずれかをじっくり見ようと思ったら、プレイ動画を録画してから一時停止などの手間が必要です。
しかし静止ポーズ4枚は、何もしなくてもプレイヤーが画面の前でボーッとしてるだけでも目に入ります。
だからこの4枚を優先的に作画していく必要があります。今後の工程で細部の書き込みをする際にも、静止ポーズ4枚を優先して細部を仕上げると良いでしょう。
そして、大雑把に色が塗れたら(すごく雑(ザツ)な塗りでいいです)。最低限、向きは前後左右の4ポーズの立ちポーズを描かないといけないので、とりあえず、さっさと4枚を大雑把に早く書いて、実際のゲーム画面で表示テストしてみます。
ゲームドットの下書きなので、けっして細かく書く必要はないです。一方、テレビ番組アニメ用のアニメーター教育などだと、「練習でも細かく描け。すると本番ではそれを早く書けるようになっている」と教育する場合もありますが、しかしゲームのドット絵の下書きの場合は目的が違います。
実際にテストで確認してみて、問題があれば書き直しです。
だから、もし最初から細かく描いてしまうと、書き直しになったときにそれを消さないといけないので、無駄になってしまうのです。だから、色を大まかに塗るだけなのです。
そして下書きのドット絵の中ポーズの4方向の向き転換などをテストしてみて違和感がないことを確認できたら、次に手足を振っているポーズの作画にとりかかります。ここでも、色を大まかに塗るのと、ポーズを決めるだけです。
そして、とりあえずテストで実際のゲーム画面中で動きのアニメーションを確認してみて、「キャラがおかしなポーズになってないか?」とか、遠めで見たときに歩いているように見えるかとか、そういうことを確認していきます。
どのみち2D-RPGゲ-ムでは(テレビアニメではありえない)3枚作画で歩きを買いているので、少々の滑りや、実際とのポーズの少々の違いは無視です。
おおむね、こういった作画の流れになるでしょう。もちろん個人差はあるでしょう。ですが、けっして「いきなり細部から描く」ことはしないのは、ほぼ確定でしょうし、線画はドットが潰れるので輪郭以外の線は描かないことも、ほぼ確定でしょう。
あくまで、2D-RPGのキャラチップの 40px × 40px 程度のドット絵だけに限定した話です。なので、反論などで顔グラフィックなどキャラチップ以外の話題を出されても、ピント外れです(相手が言ってもいないことで反論することを「w:藁人形論法」(わらにんぎょう ろんぽう)と言います)。ましてやRPG以外のアクションゲームなどのドット絵の手法で反論しないでください。
デッサン的なこと
[編集]女性キャラのチップだと、前後の歩行中チップでは髪やスカートが左右にゆれたりします。男性キャラでも、マントを羽織った騎士などなら、マントが揺れたりします。
テストでは、そういう点をチェックをしていくことになるでしょう。
こういった感じで実際にゲーム画面中でテストしてみて、もし問題がなければ、あとで細部を書きこんでいくことになるでしょう。
また、キャラクターデザインの細部に興味がなければ、下書きの塗りを修正し終わった段階で、このままで完成としても構わないかもしれません。
なお、髪の毛が右に揺れる場合、足で伸びているのは反対側である左足です。
髪の毛が左に揺れる場合も、足で伸びるのは反対側である右足です。
これはなぜそうなるかというと、歩行時の腰の ねじれ を表現するためです。
普通の歩き方では、右手が前方に伸びているときは左手が後方に伸びており、このため上半身が左回転します。
このままだと体が左回転して進路が左にそれてしまうので、足側では左足が前方に伸びて右足が後ろに伸びることで下半身は右回転します。
こうして上半身が右回転する一方、下半身が左回転するので、全身では回転が打ち消されることで前方に歩行できます。
また、上半身と下半身とが逆方向に回転しているので、腰はわずかに ねじれます。
だから、髪の毛が右に揺れるなら、これは上半身が右回転した直後の表現なので、つまりこのとき下半身側では左回転した直後の表現になっていなければなりません。これはゲームに限らず、アニメでもそうでしょうし、現実の人体がそうでしょう。
なお、無理やり、腕の振りのタイミングと、足の降りのタイミングを一致させる歩き方を、ナンバ歩きといいます。つまり、右手が前に出ているときに右足も前に出る歩きがナンバ歩きです。
格闘技や古武術などだと、色々な理由によりナンバ歩きをする場合もあります。 だから、もしかしたら対戦格闘アクションゲーム(スト2みたいなの)ならナンバ歩きで描く場合もあるかもしれません。しかし、2D-RPGの歩行チップでは不要でしょう。
ドット絵作画的なこと
[編集]さて、いくつかフリー素材を調べたところ、横向き時の中ポーズの足は、1本しか見えません。
ついつい歩行中の中ポーズも意識して足を微妙にズラして2本書きたくなりますが、しかし静止ポーズにも使うことと、ドット絵なので微妙な足の段差を表現しづらいことなどから、
決断して横向き時の足は1本しか見えないほうにするのが効率的でしょう。
どうしても中ポーズでも足2本を見えるようにしたい場合の裏ワザとして、2本の足が合体した太い1本足を描くというワザがあります。やや太めの1本足です。
一部のフリーゲーム素材がこういうワザを使ってドット絵を描いています。
ドット絵なんてこんなもんのゴマカシの連発ですから、あまり下調べの段階なのに細部を調べても仕方ありません。さっさと書き始めてから、書いてる途中に気になった点から手本を見直して調べていけばいいのです。いろいろと調べるの必要になりますが、さっさと書き始めるのも重要です。こういうのは、調べていたらキリがありません。
デッサンのコツなんて、細部を書き始める時点までに、把握できていればいいのいです。
仕事でなければ、イラストなんて最終的には描いた自分で満足できればいいのです。プログラマー視点で見るにしても、歩行プログラムの検証をできる最低限のイラストさえ書ければいいのです。
当ページはプログラミングの教科書ですので、まず先にプログラム検証のための最低限イラストを描くことになります。
戦闘
[編集]とりあえずバブルソートの話題など。ほか幾つか。
データベース的なこと
[編集](※: 未確認)戦闘中のモンスター隊列
[編集]さて、戦闘中のモンスター隊列の構成を定義するための配列(または同等の内容の数列)も、必要です。
まず、画面に敵を、表示したい順に表示するためも必要です。
また、上述の「素早さ順」での行動処理のためにも、前提として、敵パーティの構成を表すための配列が必要になります。
説明の単純化のため、敵は横一列に、左から右に、並んでいるとしましょう(ドラクエ2~3みたいな方式だとしましょう)。
たとえば敵が左から順に
- ホブゴブリンが1匹、毒々スライムが1匹、ゾンビ兵士が1匹
出現したとしましょう。(説明の単純化のため、モンスターは各種類ごとに1体だけとする。)
そして、ホブゴブリンのモンスター用データベース中でのIDが13番だとして、 毒々スライムのIDが8番、 ゾンビ兵士のIDが25番、 だとしましょう。
すると、この敵パーティを表すための配列として、
- {13,8,25,-99}
のような配列が必要です。(もしくは、配列に格納できるような数値の列「13,8,25,-99」)
末尾の-99は、これはその配列の読み終わりとして使用するための数字です。この数字は、別に「-10」でも「-99」でもいいですが、モンスターのidとけっして重ならないようにする必要があります。
通常、なんらかのデータベースのidの値は0以上の正の整数ですので、マイナスの整数を「終了」の意味で使用しておけば、安全でしょう。
このように、敵側にも、配列が必要になります。
しかも、この敵側の配列は、そのゲーム中で出現する敵パーティの全パターンを用意する必要があります。
たとい1体だけしか出現しない敵でも(たとえばボス敵や強敵)、その1体分の敵パーティの配列が必要です。
たとえば、その1体だけで出現する、あるボス敵「暗黒大王」のモンスター用データベース中でのIDが205番だとしたら、
- {205,-99}
のような、敵1体だけの配列を用意する必要があります。
余談: マイナスを含む並べ替え
[編集]なお、もしマイナスの数を含む数をふくめて並べ替えをしたいのでしたら、 上記の「-99」など終了処理を負数で区別する方法は、そのままでは使えないです。
たとえば、方向でもし東向きをプラス、西向きをマイナスとした場合、終了コードのつもりの「-99」は、誤解で「西に99マス」などと誤解される恐れがあります。
たとえば、
- -3 , 5 ,2, -6, -2 , 0 , 1
の並べ替えをしたい場合を考えましょう。
解決策としては、いろいろあります。
解決策1
[編集]一番ラクな方法は、要素数を新たな変数として追加し、それと組み合わせて配列を使うことでしょう。
たとえば、
- -3 , 5 ,2, -6, -2 , 0 , 1
は7個の数が並んでいるので、
youso = 7;
みたいに新たに変数を用意します。
そして、たとえば最初の「-3」から順番意読み取りの際に、数えおわった個数を+1していき、+7になったら読み取りを終了することです。
この方法は、拡張性が高いのでオススメですが、ただし、要素を書く前に個数を宣言する必要があります。
実際の配列データは
- -3 , 5 ,2, -6, -2 , 0 , 1, 0 ,0 ,0 , 0 ,0 ,0 , (以下略)
のようになっているので、けっして数え終わって欲しいところでキリよく要素が終わるわけではないので、なので、要素数をあらかじめ宣言する必要があるのです。
さもないと、8番目の「0」が、読み取りたい数値としてゼロなのか、それとも、単にデータなしのためゼロなのか、不明になります。
終了コードなどとして負数を使用するのは、マイナスを含む並べ替えでは、あきらめましょう。それが安全でしょう。
解決策2
[編集]最初の解決策1(要素数を新たな変数として追加)がもっとも安全かつ簡単ですが、比較のため、別の解決策も紹介しておきます。
次の解決策2は、数学的にはエレガントですが、しかしプログラミング的には、煩雑になり、また、バグ時などのエラーの波及をしやすい欠点があります。
さて、並べ替えのもうひとつの解決策として、
一例は、符号と絶対値を分離して、その組み合わせとして処理することです。
たとえば、
- -3 , 5 ,2, -6, -2 , 0 , 1
の並べ替えをしたい場合、
プレイヤーからは表面的には「-6」は1つの数に見えまずが、これをあえて、「フラグhugouが状態2(マイナスに相当)である」「絶対値 zetai が6である」というように、2つの変数hugou と zetaiに分けます
符号がプラスなら、hugou = 1 にでもしておきましょう。また、ゼロは便宜上、hugou = 1 にしておきましょう。
すると、上記の数の並びは、(hugou, zetai)のベクトルで考えると、
- (2,3) , (1,5) ,(1,2), (2,6), (2,2) , (1,0) , (1,1)
という組み合わせに変換するので、マイナスが使われない形になります。
なので、終了処理に、「-99」などの負数を割り当てても、並べ替え対象の数値と重なる心配がなくなります。: (2, 6,) , (2, 3) , (2, 2) , (1,0) , (1,1) ,(1,2), (1,5)
- (2,3) , (1,5) ,(1,2), (2,6), (2,2) , (1,0) , (1,1),(-99,0)
などのように、終了コード「(-99,0)」を末尾に追加しても、なんの混同の心配もなくなります。
さて、いったん上記のように、数を、符号と絶対値の組み合わせに分解して、同じ符号どうしで並びかえたなら、
あとは、符号が同じものどうしで、並べ替えをするだけですみます。
たとえば、
- (2,3) , (1,5) ,(1,2), (2,6), (2,2) , (1,0) , (1,1)
の並べ替えをしたい場合、ベクトルの第一引数に注目し、※ (第一引数、第二引数)
まず、
- (2,3) , (2,6), (2,2) のグループ
- (1,5) ,(1,2), (1,0) , (1,1)のグループ
に分けます。
そして、絶対値(例では第二引数)だけに注目すれば、
- マイナス数の絶対値は 3,6,2 → 2,3,6 と並べ替え → 逆順にして6,3,2 → マイナス符号をつけて -6,-3,-2
- プラス数の絶対値は 5,2,0,1, → 0,1,2,5 と並べ替え
となるので、簡単に並べ替えできます。
あとは、これを合成し、
- -6, -3 , -2 , 0 , 1 ,2, 5
と、並べ替えできます。
なお、説明の都合上でベクトルを蒸気の解説で使ったが、しかしC言語にベクトルの機能は無いので、もし上記の機能を実装するなら配列や構造体配列を使って、ベクトルと似たような処理を実装することになる。
たとえば配列で実装するなら、符号用の配列 hugou[id] と、絶対値用の配列 zetai[id] のようなものを用意したりすることになるだろう。
もし1番目の最初の数が「-3」なら、符号フラグは2、絶対値は3だからベクトル表現では
- (2,3)
であるが、これは配列で表現するなら、たとえば
- hugou[0]=2; zetai[0]=3;
のように記述できる(C言語では配列の番号は0から数える)。
- 欠点
この方法は、一見すると数学的にエレガントに見えるかもしれませんが、しかし、もしバグやタイプミスなどによって数え間違えると、数え落としや重複があると、以降の符号が1個ずつズレてしまうなどの大きな影響があります。
たとえば、説明のため上記では
- -3 , 5 ,2, -6, -2 , 0 , 1
の分解を
- (2,3) , (1,5) ,(1,2), (2,6), (2,2) , (1,0) , (1,1)
とマトメて書きましたが、 実際には
- hugou 2,1,1,2,2,1,1
- zatai 3,5,2,6,2,0,1
のように別々の変数に分かれて保管されるのです。
もし、たとえば hugou の最初に、バグなどで「1」が加わると、
- hugou 1,2,1,1,2,2,1,1
- zatai 3,5,2,6,2,0,1,0
のようになってしまい、すべての符号が1個分、ズレてしまい、
- 3, -5, 2, 6 ,-2 , 0, 1 ,0
となってしまいます。
正しい元々の数字の並びの
- -3 , 5 ,2, -6, -2 , 0 , 1
と比べると、符号がいくつも間違っており(1個目と2個目と4個目が違う)、解決策2ではエラーの波及が大きいことが分かります。
脚注
[編集]- ^ いわゆるドラクエ シリーズや昔のFF(ファイナルファンタジー)1~3あたり
参考文献など
[編集]- ^ ntny著『ローポリスーパーテクニック』、ソフトバンククリエイティブ、2010年2月16日 初版 第5刷、P.14
- ^ 塩川洋介『ゲームデザイン プロフェッショナル』、技術評論社、P.23
- ^ 塩川洋介『ゲームデザイン プロフェッショナル』、技術評論社、P.24
- ^ 塩川洋介『ゲームデザイン プロフェッショナル』、技術評論社、P.26
- ^ 塩川洋介『ゲームデザイン プロフェッショナル』、技術評論社、P.40
- ^ 青山愛香『アルブレヒト・デューラーとレオナルド・ダ・ヴィンチ――《メレンコリアⅠ》(1514 年)を巡る考察 ――』, 2013 , 2023年12月25日に閲覧.
- ^ 川村元気『理系に学ぶ』、ダイヤモンド社、2016年4月21日 第1刷発行、P.62
- ^ 川村元気『理系に学ぶ』、ダイヤモンド社、2016年4月21日 第1刷発行、P.15
- ^ 大槻有一郎『12歳から始めるゼロからのC言語 ゲームプログラミング教室 Visual Studio 2015対応』、2016年3月1日 第1刷発行、2017年4月10日 第2刷発行、P165
- ^ 『ゲームプランとデザインの教科書』、P399
- ^ 『世界の美しい階段』エクスナレッジ、2015年、200頁。ISBN 978-4-7678-2042-2。
- ^ 高等学校情報科「情報Ⅱ」教員研修用教材(本編)『第4章 情報システムとプログラミング』、P230 2023年9月27日に確認.
- ^ 高等学校情報科「情報Ⅱ」教員研修用教材(本編)『第4章 情報システムとプログラミング』、P230 2023年9月27日に確認.
- ^ 高等学校情報科「情報Ⅱ」教員研修用教材(本編)『第4章 情報システムとプログラミング』、P230 2023年9月27日に確認.
- ^ 高等学校情報科「情報Ⅱ」教員研修用教材(本編)『第4章 情報システムとプログラミング』、P230 2023年9月27日に確認.
- ^ 『【ゲームの企画書】『ペルソナ3』を築き上げたのは反骨心とリスペクトだった。赤い企画書のもとに集った“愚連隊”がシリーズを生まれ変わらせるまで【橋野桂インタビュー】』2019年10月30日 11:30 2020年12月1日に閲覧して確認.
- ^ Dan Maksimovich『Don't DRY Your Code Prematurely』Tuesday, May 28, 2024
- ^ 『技術的負債を抱えたレガシーコード。変なメソッド名と入り組んだロジック、リファクタリングするならどちらが先?(後編)』, , 2024年7月1日
- ^ 『技術的負債を抱えたレガシーコード。変なメソッド名と入り組んだロジック、リファクタリングするならどちらが先?(後編)』, , 2024年7月1日
- ^ 川上大典 ほか著『ゲームプランとデザインの教科書』、秀和システム、2018年11月1日 第1版 第1刷、P.255