ExcelでVBAを使わないでドラクエ3を再現する

English

久しぶりにExcelでゲームを再現してみました。

ツイッターでどんなゲームを作るのが良いか募集したところ、なんと1位がドラクエ。

正直どう作って良いか見当もつかないまま見切り発車で進めましたが、なんとか形になったので公開の流れになりました。最適化されてないロジックもありますので温かい目で見守っていただけますと幸いです。

一応、再度伝えておきますがVBAは一切使っていません

[ad01]

どんなものが出来たか

実際にどんなものになったか、見ていただいた方が早いのでまずは結果をご覧ください。(※音声はありません)

あのイルカ
あのイルカ

最後まで見た方が面白いですよ。

ポイント

お忙しい方向けに、どんな技術を使っているか3行で説明します。

・地図やモンスターは散布図で表示する

・循環参照を使ってカウンタを作りF9を押しっぱなしにする

・セル上でプログラミングをする

今回のドラクエで一番課題だったのがアニメーションです。マップ上でキャラクターを定期的に歩かせたり、戦闘シーンに入る部分はVBAを使わないと不可能だと思っていました。しかし今回はそこを特定の制限を設けることで突破した形になります。

詳細

それでは詳細になりますが、この記事は「Excelでゲームを作る技術」の紹介になります。「ゲームを作る技術」は省略しますので細かい仕様や知りたい点があれば、他のサイトのゲームのブログを見に行くか、コメント欄にて質問してください。

散布図を使いたおす

散布図は2つのデータの関係を示すもので、点(マーカー)の集合であらわされるグラフです。Excelでは2つの列をデータとして指定することで散布図を表現できます。

マーカーに画像を挿入できる

散布図は好きな位置にマーカーをプロットできる部分が非常に魅力的です。

そしてデータ系列の書式設定でマーカーのオプションから画像を指定できます。

画像はそれぞれ変えられるのでマーカーごとに画像を登録しておけば好きな位置に好きなキャラクターを置けるようになります。

画像を登録する際の注意点

マーカーに挿入する画像ですが、拡大縮小ができません。このため、画像を作る時は大きさを事前に決めてから作成する必要があります。また、画像が大きいとExcelの動きが鈍くなります。適度な大きさを心掛けないといけません。

マーカーを動かしたり消したりできる

マーカーの数が一定数以下の場合、データの値を変えるとヌルヌル動きます。マップ移動で勇者が移動したときに後続者がついてきたり、マップが動く技術はこれを使用しています。

ドラクエが完成した時点ではマーカーが一定以上に達したためヌルヌル動いてくれず瞬間移動していますが、ゲーム進行上問題なかったためそのままにしています。

また、データの一部が欠けると散布図上に表示されなくなる性質を利用して表示の切り替えができます。切り替えを素早くすると歩いているように見えます。

戦闘シーンへの切り替えやコマンドの表示も全てこの仕様を使っています。

マーカーにラベルを登録してテキストを表示する

マーカーにはラベルを設定できるので、戦闘シーンのステータスやメニューコマンド、メッセージなどは全てラベルで表現できます。

一文字ずつ表示することで滑らかなメッセージ表示が出来るようになります。またフォントをファミコンっぽいものに変えることで雰囲気が出るようになります。

ちなみに、マーカーには表示する順番があり、一番下のデータが一番手前に表示されます。何を前面に出すかデータの順番を意識しないといけません。またこの仕様により、勇者たちの並べ替えを実装するには結構な手間が必要になりますがそれは別のお話ということで触れないでおきます。

トピック

散布図のアニメーションを利用したゲームがスライドパズルです。マーカーの数が少ないのでアニメーションしていますね。

循環参照を利用してセルの値を変動させる

セル上でゲームを作る時に大きな壁になるのが「ループ処理」です。関数式を並べればループもできなくはないですが、複雑なゲームになればなるほど関数では対応しづらくなり、改修も面倒になります。

そこで循環参照をあえて利用してループ処理を再現します。

反復計算を有効にして循環参照を許容する

Excelのオプション設定から反復計算を有効にします。すると循環参照で警告が出なくなり指定した反復回数だけ同じ計算が実行されます。

例えばA1セルに「=A1+1」と入れておけば通常循環の警告がでますが、反復を許容しているのでそのまま計算してくれます。デフォルトだと反復回数が100になっているので1を加算する処理が100回繰り返され、結果として100が返ってきます。F9で再計算をするとさらに100が加算されます。

反復回数を1にしてカウントアップするセルを作る

デフォルトでは100回になっている反復回数を1回に変更します。すると先ほどの加算が1ずつになるので、再計算するたびに1ずつ加算されていきます。これでカウンタが出来上がります。

F9を押しっぱなしでアニメーションを実現する

カウンタがあればその値を見ながら描画を切り替えられるようになります。カウンタは何も操作しないと変わらないので、(ここが最大の制約ですが)F9を押しっぱなしにして再計算し続けることで動かします。

先ほど、散布図の切り替えをチェックボックスで行っていましたが、カウンタを参照することで自動的に切り替えられるようになります。下のイメージではカウンタが10増えるごとにマーカーを切り替えています。

この技術で戦闘シーンへ入る時の渦巻きや、ダメージを受けたときの枠の振動を表現しています。

トピック

セル上で疑似的なプログラミングを実現する

一番重要なポイントがここです。

マップと戦闘の切り替え、選択したコマンドの記憶、敵の行動、メッセージの表示など、関数だけで実装するにはこれまで培ってきたノウハウだけでは難しく、やはりプログラミング的なステップを管理する必要がありました。そこで循環参照を使ってセル上で疑似的にプログラミングを作る方法を構築しました。

例:セル上でFizzBuzzを作ってみる

実際にどのように構築するか、簡単な例、おなじみのFizzBuzz問題で説明します。

最終的な出来上がりはこんな感じです。

ヘッダに入力値用のセル、フッタに戻り値用のセルを設け、その間で処理をおこないます。

処理についてですが、各行にステップ数を設けています。「処理内容」はコメントみたいなものなのでその行で何を行っているか分かるようなテキストにしておきます。メインの処理は「式」です。ここに処理の内容を書きます。「次のステップ」には次に処理するステップ数を書きます。ほとんどがすぐ下の行のステップになりますね。最後に「処理中のステップ」で現在どのステップを実行しているか記憶していきます。

この形が基本です。

それではそれぞれの式について説明します。

処理中のステップについて

処理中のステップ = IF(入力値="",1,XLOOKUP(処理中のステップ,$A$5:$A$10,D5:D10,処理中のステップ))

入力値が空欄の間は常に1になりますが、何かしら入力値が入るとXLOOKUPが実行されます。XLOOKUPで探すものは自分自身になります。(反復計算が有効なのでここでエラーは起きません。)XLOOKUPでは「次のステップ数」を取得しています。こうすることで再計算が行われると処理中のステップの値が次々と変っていきます。プログラムカウンタのような役割になりますね。

ステップ1 処理開始

この行は直接的には何もしていません。準備運動みたいなものですぐに次のステップに入ります。(本当は意味のある行ですが、この例では何にも影響しません。詳細は後述します。)

ステップ2 cnt++

カウントアップです。値を1ずつ増加させてFizzBuzz判定していきます。

cnt = IFS(入力値="",0, 処理中のステップ=A6,cnt+1,TRUE,cnt)

入力値が空欄なら0、処理中のステップが2になったら1増加、それ以外は増加しない(=自分自身)という計算になります。つまり、cntは入力値が空欄の時に初期化され、処理ステップが2になったときだけ1増加します。VBAでいうところのcnt=cnt+1ですね。

ステップ3 FizzBuzz判定

判定処理です。ステップ2と同様、まずは入力値を見て初期化の処理と、ステップ数を見て3だったらFizzBuzz判定をします。

判定結果
=IFS(
      入力値=FALSE,
            "",
      処理中のステップ=A7,
            IFS(
                  MOD(cnt,15)=0,
                        "Fizz Buzz",
                  MOD(cnt,3)=0,
                        "Fizz",
                  MOD(cnt,5)=0,
                        "Buzz",
                  TRUE,
                        cnt
            ),
      TRUE,
            判定結果
  )

最後の「判定結果」は自分自身(C7)を指します。ステップ3以外の時は自分自身のになるため、判定処理されず前回の判定処理が保存されます。

ステップ4 判定結果を保存

判定結果を文字列として結合して保管していきます。

判定結果ストック = IFS(入力値="","",処理中のステップ=A8,TEXTJOIN(",",TRUE,判定結果ストック,判定結果),TRUE,判定結果ストック)

VBAでいうところの判定結果ストック = 判定結果ストック & "," & 判定結果になります。

ステップ5 ループを抜ける判定

cntが入力値未満ならステップ2に戻り、入力値以上ならステップ6に行きます。

式 = cnt < 入力値
次のステップ数 = IF(C9,A6,A10)

式には判定結果を書き、「次のステップ数」が切り替わるようにしています。

ステップ6 処理完了

ステップ6まで来たところで「判定結果ストック」を最終結果としています。

処理結果 = IFS(入力値=FALSE,"",処理中のステップ=A10,判定結果ストック,TRUE,処理結果)

戻り値

ステップがEndとなったところで戻り値を反映します。これで一連の処理が完了となります。

戻り値 = IF(処理中のステップ="End",処理結果,"")

再計算させてみます

こんな流れになります。

ステップごとに計算や判定がされ、プログラミングと同じような動きを再現しています。

関数ごとの切り分けも可能

上から下に流れるだけではなく、引数や戻り値をつなぎ合わせることで特定の処理に切り分けることもできます。VBAでいうとこころのFunctionやSubのような動きになります。

この手法で機能を切り分けて開発ができるため、複雑にならずに済みます。

以上が疑似的なプログラミングを構築する方法です。

もうなんでも作れます

セル上でも疑似的なプログラミングが可能だということは、ほとんどのゲームが作れるということです。下のサンプルは戦闘時の処理の流れです。汚いですが普通のプログラミングですね。こんな感じのコードを大量に作って組み合わせればドラクエが完成します。

唯一出来ないのが音を出すことです。VBAを使わずに自由に音を鳴らす方法がいまだにわかりません。

ボタン操作は過去のゲームと同じ

ボタン操作は今まで何度か作ってきたゲームの記事で説明していますので省略します。

以上が、Excelでドラクエを再現する方法になります。

その他

循環参照の注意点

循環参照の計算順序は意識する必要があります。

シートの左から右、上から下の順序で計算されます。つまりセルA1がセルA2を参照するとA2が計算される前にA1を計算してしまいます。具体的にどんな時に不具合が起こるかというとRAND関数とRANK関数の組み合わせの場合が分かりやすいでしょう。

ランダムな数を順位付けする単純なもんですが、順位が重複するシーンが散見されます。通常、反復計算が無ければRANDがすべて評価されてからRANKが評価されるので重複はほぼありません。しかし循環参照があるとA2→B2→A3→B3→A4→B4…と評価されるので順位が重なる場合が発生します。このあたりは仕様なのでどうしようもできません。なるべく処理させたい順番になるように上から下へ書くようにしましょう。例えば、上記順位付けの問題を解決するならこうします。

このほか、セルの位置だけではなくシートにも優先順位がついているようです。関数ごとにシートを分けると、思わぬ順番で処理が進むこともありますので注意しましょう。前述した処理の中で「ステップ1 処理開始」という何もしないステップがありましたが、これは循環参照の計算順番が狂わないようにするための休憩ポイントとして設置したものです。詳しくは解析していませんが「この行があるとなぜかうまく動く」現象が発生したポイントです。暇があれば解析したいと思います。

画像をどうやって用意したか

散布図では画像のサイズを拡大縮小できないので、地図やモンスターの画像サイズを統一して用意する必要がありました。期待する画像がネット上に転がっているはずもないので1から作っています。せっかくなのでExcelで作りました。

セル範囲をコピーして画像として保存することでサイズを統一できます。画像を用意するのも大変ですね。

ファイルサイズは?

ファイルサイズは約5.2MBで、そのほとんどがMAPに使った画像になります。戦闘だけですと660KBほどになります。

ということで

今回の成果物はまだ最適化されていない部分があったり、不安定な部分があったり、著作権とかアレなのでGitHubでの公開はしない予定です。もし興味のある方はご自身で作ってみてください。

最後に

これまでのゲームは1週間程度で作っていましたが今回は1か月以上かかりました。ドラクエは作るパターンが多いです。また、疑似プログラミングを構築してみたものの結局やっていることがVBAと大差なく、更にVBAより気を配る部分が多いので正直疑似プログラミングの手法も制限したい気分です。

次はテトリスですね。頑張ります。

【VBAを使わないゲームシリーズ】

このページで利用している株式会社スクウェア・エニックスを代表とする共同著作者が権利を所有する画像の転載・配布は禁止いたします。
© 1988 ARMOR PROJECT/BIRD STUDIO/SPIKE CHUNSOFT/SQUARE ENIX All Rights Reserved.

コメント

  1. たまげたなぁ より:

    才能の無駄遣い(誉め言葉)

  2. 著作権 より:

    せっかくすごいので、ちゃんとスクエニにドット絵の使用許可をとったほうがいいですね

  3. すげえ より:

    控えめに言って天才

  4. 新世代ニート より:

    すげぇ!!変態だぁ。と一人つぶやいてしまいました。(誉め言葉)

    • じゃがりこ より:

      pcwatchの記事を見てyoutubeで検索したが出てこなかった
      限定公開じゃなくて公開にしてほしい

  5. なし より:

    セル上でプログラミングってできるんだ・・・

  6. かきかき より:

    どういう頭の構造してるのですか?
    というか、何故思いついた?

  7. パパセンセイ より:

    皆様コメントありがとうございます。
    大変励みになります。

    > ちゃんとスクエニにドット絵の使用許可をとったほうがいいですね

    ありがとうございます。
    そうですね、只今問合せ中です。NGなら動画と画像を差し替えようと思います。

    > そもそも、ドット絵をどうやって手に入れたのかなぁと。

    まだファミコンが現役なので、ゲームを起動してテレビを見ながらドットを写しました。
    地図の画像はそれをもとにVBAで生成しています。
    https://twitter.com/10mikiya/status/1310216516793589761

    > 限定公開じゃなくて公開にしてほしい

    ありがとうございます。
    ここまで大きくなるとは思ってもいなかったので限定にしていたのですが、せっかくですし公開設定にしました。

    > というか、何故思いついた?

    過去の作品でいろいろとノウハウが貯まっていたのでそれの積み重ねですかね・・・?

  8. ぽんぽんぺいんに苦しむ人 より:

    循環参照つかえばプログラムカウンタが実現できるんですね
    スタックポインタも実装すれば、普通のプログラミング言語が作れちゃいますね
    暇な時に作って遊んでみようと思います
    良い記事ありがとうございました

  9. れいれい より:

    発想と技術とチャレンジが素晴らしいです!プログラミングて難しそうだと思ってましたが、自分が好きなゲームを作れるなら!と興味が湧きました。

  10. まとめサイトから より:

    頭おかしい(最高の賛辞)

  11. ななし より:

    実際、FC版をアセンブラで作った時も
    同じ以上に苦労したんだろうなあ

  12. Francis より:

    I don’t really understand because it was in the Japanese language. But I was amazed by your talent how could you come up with that idea to make a game in excel and it was no VBA. You have a very brilliant mind. I hope you can make an English version of this and also tutorial and upload it on youtube for you to have profit and also to share your talent with us. because that talent should not be wasted.

  13. KZ-EDIT より:

    半端ないですね!
    すごすぎます
    自分の脳と交換希望します

  14. 通りすがり より:

    うーん…無許可で動画うpして拡散かぁ…、やるならオリジナルでやりなよ。
    いくら凄くても著作権侵害してる時点で尊敬しない、つーか犯罪なんだからダメでしょ。
    技術があったら何してもいいなら、世の中もっと技術あるやつが既に滅茶苦茶してるわけで

    • パパセンセイ より:

      コメントありがとうございます。

      そのあたりの話については直接スクウェア・エニックス様に問い合わせをしまして、ガイドラインを提示していただき、そのガイドラインに準拠して掲載しています。

  15. 新人 より:

    YouTube拝見して純粋に感動して実現方法知りたいなと思いサイトも見に来ました。そんな機能がExcelにあったのか、という驚きとともにExcelの可能性の広さと、アイデアの凄さと精巧さにますます感動してます。
    VBA使わないでセル上でプログラミングって実現できるんですね!読んでてすごくワクワクしたので自分でもやってみます。

  16. タケチャン より:

    私も以前にvbaではないexelマクロと、その時はvbaを補助的に使って株ロボ(株式の自動売買と分析&結果表示)のプログラムを作ったことがありますが、今回の様に完全な”非vba”の”プログラム”は初めて知りました。本当の天才と言うのは通常人が思いもつかないような、ある道具のある使い方を編み出すことが出来る人です。普通人は固定観念に縛られて、あるいは批評を気にしてそういう自由な発想がなかなか出来ません。天才と凡人の違いはある現象を見て(今回は散布図や循環参照の挙動等)を見て『あっ、これは何々に使えるな!』と思う所。今回はexel上に立派にノイマン型のプログラム処理が出来てしまっている。実はこういうチョットしたヒラメキと言うのが科学の歴史を見渡すと大袈裟に言うと人類の大きな発明に繋がって来たのです。今回の成果はソフトの世界のたかがゲームのエミュレートと言われるかもしれませんが、そういう活動に似たところがあると見ることも出来ます。(←最大限の賛辞)きっとこれを見て一番ビックリするのはマイクロソフトのエクセル開発チームではないでしょうか。そして『ヘェ〜こんな使い方は思いもつかなかった!でも、それならexcelの仕様を変更してもっと自由にしても良いかも?』と言う様に、ユーザと開発者の間で相互に知的刺激し合えたならば素晴らしいですし、exelもまだまだ進化するかもしれません。それと、今回のプログラミングスタイルって本格的に突き詰めれば何か現状とは異なる新たなプログラミング言語が生まれるヒントを秘めている様にも感じますね。
    ちなみに現代に溢れているホンモノ(?)のプログラミング言語にもまだまだ使い方の進歩の余地があるかもしれません。まさに “プログラミングとハサミは使い様” の世界です。そこに欠けているのは、固定観念からの脱却と自由で創造力豊かな発想力なのかもしれません。

  17. クマ より:

    すご〜い!
    これぞ「知恵」ですね!!知恵は全てを解決する(^^)
    カイルくん、面白かったです!

  18. Daisy より:

    すげえまじか!
    もうこれ完全に神だwww
    応用すればドラクエ以外もできるのかな。
    初代マリオカートとかやってみてほしい気がする。
    他にはポケモンとかね。
    それかオリジナルゲーム作るってのもいいんじゃないかな。

  19. […] ねとらぼより。 パパセンセイは天才だし、それが可能なExcelは神ソフトであるとしか言いようがない。 ⇒パパセンセイ自身の紹介 […]

  20. ナミキカズオ より:

    すばらしい。とにかくすごい。昔30年前トランプゲームのポーカーをエクセルで作りましたのでこの凄さを実感します。レベルの差は天と地ですが。

  21. ねとらぼから来ました より:

    コマンドの「まほう」、正しくは「じゅもん」、
    「まものたち」、正しくは「まもののむれ」です!!

  22. のきざる より:

    2020年11月22日のヤフー記事を読んで、ここへ来ました。
    ヤフー記事では700件以上のコメントがありましたよ!
    https://news.yahoo.co.jp/articles/a37045dd939e12c0215e65cefe0cbc7d16b50a98
    上記、参照元のヤフー記事です。
    天才と変態の融合だと思いました(誉め言葉)(; ・`д・´)

  23. […] ExcelでVBAを使わないでドラクエ3を再現する https://papasensei365.com/excel-game-dq3/ […]

',b.captions&&s){var u=J("figcaption");u.id="baguetteBox-figcaption-"+t,u.innerHTML=s,l.appendChild(u)}e.appendChild(l);var c=J("img");c.onload=function(){var e=document.querySelector("#baguette-img-"+t+" .baguetteBox-spinner");l.removeChild(e),!b.async&&n&&n()},c.setAttribute("src",r),c.alt=a&&a.alt||"",b.titleTag&&s&&(c.title=s),l.appendChild(c),b.async&&n&&n()}}function X(){return M(o+1)}function D(){return M(o-1)}function M(e,t){return!n&&0<=e&&e=k.length?(b.animation&&O("right"),!1):(q(o=e,function(){z(o),V(o)}),R(),b.onChange&&b.onChange(o,k.length),!0)}function O(e){l.className="bounce-from-"+e,setTimeout(function(){l.className=""},400)}function R(){var e=100*-o+"%";"fadeIn"===b.animation?(l.style.opacity=0,setTimeout(function(){m.transforms?l.style.transform=l.style.webkitTransform="translate3d("+e+",0,0)":l.style.left=e,l.style.opacity=1},400)):m.transforms?l.style.transform=l.style.webkitTransform="translate3d("+e+",0,0)":l.style.left=e}function z(e){e-o>=b.preload||q(e+1,function(){z(e+1)})}function V(e){o-e>=b.preload||q(e-1,function(){V(e-1)})}function U(e,t,n,o){e.addEventListener?e.addEventListener(t,n,o):e.attachEvent("on"+t,function(e){(e=e||window.event).target=e.target||e.srcElement,n(e)})}function W(e,t,n,o){e.removeEventListener?e.removeEventListener(t,n,o):e.detachEvent("on"+t,n)}function G(e){return document.getElementById(e)}function J(e){return document.createElement(e)}return[].forEach||(Array.prototype.forEach=function(e,t){for(var n=0;n","http://www.w3.org/2000/svg"===(e.firstChild&&e.firstChild.namespaceURI)}(),m.passiveEvents=function i(){var e=!1;try{var t=Object.defineProperty({},"passive",{get:function(){e=!0}});window.addEventListener("test",null,t)}catch(n){}return e}(),function a(){if(r=G("baguetteBox-overlay"))return l=G("baguetteBox-slider"),u=G("previous-button"),c=G("next-button"),void(d=G("close-button"));(r=J("div")).setAttribute("role","dialog"),r.id="baguetteBox-overlay",document.getElementsByTagName("body")[0].appendChild(r),(l=J("div")).id="baguetteBox-slider",r.appendChild(l),(u=J("button")).setAttribute("type","button"),u.id="previous-button",u.setAttribute("aria-label","Previous"),u.innerHTML=m.svg?f:"<",r.appendChild(u),(c=J("button")).setAttribute("type","button"),c.id="next-button",c.setAttribute("aria-label","Next"),c.innerHTML=m.svg?g:">",r.appendChild(c),(d=J("button")).setAttribute("type","button"),d.id="close-button",d.setAttribute("aria-label","Close"),d.innerHTML=m.svg?p:"×",r.appendChild(d),u.className=c.className=d.className="baguetteBox-button",function n(){var e=m.passiveEvents?{passive:!1}:null,t=m.passiveEvents?{passive:!0}:null;U(r,"click",x),U(u,"click",E),U(c,"click",C),U(d,"click",B),U(l,"contextmenu",A),U(r,"touchstart",T,t),U(r,"touchmove",N,e),U(r,"touchend",L),U(document,"focus",P,!0)}()}(),S(e),function s(e,a){var t=document.querySelectorAll(e),n={galleries:[],nodeList:t};return w[e]=n,[].forEach.call(t,function(e){a&&a.filter&&(y=a.filter);var t=[];if(t="A"===e.tagName?[e]:e.getElementsByTagName("a"),0!==(t=[].filter.call(t,function(e){if(-1===e.className.indexOf(a&&a.ignoreClass))return y.test(e.href)})).length){var i=[];[].forEach.call(t,function(e,t){var n=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1,H(i,a),I(t)},o={eventHandler:n,imageElement:e};U(e,"click",n),i.push(o)}),n.galleries.push(i)}}),n.galleries}(e,t)},show:M,showNext:X,showPrevious:D,hide:j,destroy:function e(){!function n(){var e=m.passiveEvents?{passive:!1}:null,t=m.passiveEvents?{passive:!0}:null;W(r,"click",x),W(u,"click",E),W(c,"click",C),W(d,"click",B),W(l,"contextmenu",A),W(r,"touchstart",T,t),W(r,"touchmove",N,e),W(r,"touchend",L),W(document,"focus",P,!0)}(),function t(){for(var e in w)w.hasOwnProperty(e)&&S(e)}(),W(document,"keydown",F),document.getElementsByTagName("body")[0].removeChild(document.getElementById("baguetteBox-overlay")),w={},h=[],o=0}}})
タイトルとURLをコピーしました