Visual Basic 中学校
VB2005 対応 VB2008対応 VB2010対応

3D-DASの解説みたいなもの

夏休み!プログラミングコンテスト優勝作品の作成者(無記名)さんから優勝作品3D-DASの解説をいただきました。あんなゲームどうやって作っているのか興味ありますよね?プログラムをいじって不死状態になったり好きなステージからはじめる方法も解説してもらっています。

概要

・3Dにしたかった!

・弾幕ゲーが作りたかった

・不死になる方法、攻撃力を上げる方法、好きなステージから開始する方法

 

1.みなさんはじめまして

どうも、今回VisualBasic中学校:夏休みプログラミングコンテストで優勝に選んでいただきました、(無記名)です!
ハンドルネームとか特にもってないので(無記名)がハンドルネームだった、という体で行きたいと思います。

3D-DAS

■画像1:これが優勝作品3D-DASです!

今回作成したゲームは、ゲーム名が " 3D-DAS " (ダスって...)で、三次元(3Dimension)弾幕(Danmaku)アクション(Action)シューティング(shooting)の頭文字をとって、それっぽくこじつけた非常に安易な名称(決定に要した時間約8秒)となってまして、名前の通り3次元空間っぽく見える(見えますよね?)ゲームステージ内で、敵や弾幕に対し、シューティングで倒すやら逃げ回るやらで、やり過ごし、最終的にボスさえ倒せればそれでいい、というゲームです。

操作は、移動と通常弾、特殊弾の発射だけなので、誰でも簡単にできると思います。
そこはかとなくこちらを馬鹿にしているような表情のボスたちにイラッとしながら、ゆっくりとお楽しみください。

ちなみに、初見では「これ、クリアとか無理だろwww」とか思うかもしれませんが、作者(無記名)は完成後三日ほどで、全クリアにたどり着きました。
それまでに何度死亡したかもう覚えてません。しかし、クリアできることは証明済みなので安心してプレイしてください。
製作者ですが、クリア後は、割とはしゃぎました(笑)。
ではでは、この辺で、工夫、苦労した点の解説に行きたいと思います。

 

2.3Dにしたかった!

 

今回のゲームで工夫、というか苦労というか、それらしいところといえば、やはり3Dにしたところだと思います。

なんとなく弾幕ゲーを作ってみたかったけど、普通じゃおもしろくないなぁ。と思って行きついたところが3D化でした。

プログラム中では、x(左右), y(上下), z(奥行き)の三次元座標で移動、当たり判定などを行い、画面に表示するときだけ二次元に変換しています。
ソースコードで言うところの、Base.vbの中の

vir_size(ByVal z As Integer, ByVal size As Integer)

vir_x(ByVal x As Integer, ByVal z As Integer)

vir_y(ByRef y As Integer, ByRef z As Integer, ByVal size As Integer)

こいつらが、三次元の座標を二次元上に描画するときの座標と、描画する画像のサイズ(奥に行くほど小さくなる)を返してくれます。

関数の中身は、座標に整数型の変数を使っており(計算が軽くなるようにと思って)、割り算で丸められるのをできるだけ抑えるべく、最初に作った数式からごちゃごちゃといじくった末のものなので、何のことかよくわからん感じになってます。

とにかくこの関数がなかったら、このゲームは崩壊します。コード中にさらっと描いてありますが、縁の下の力持ちです。

他には、描く物すべてに影をつけたことで、その物(自機とか、敵とか、弾とか)がどの位置にいるのかがずいぶんわかりやすくなりました。(Base.vb内のdraw関数の中にあります)
影がないと、空中にいるのか、奥に進んだのかがさっぱりで、死亡率8割増です。ついでになんか酔います。

あと、これはプログラムとは関係ないんですけど、キャラクタ画像にそれらしく陰影をつけたのも、三次元感を出すのに一役買っていると思います。いくら大きさを変えることで奥行きを表現しても、画像が薄っぺらな感じだとちょっと無理があったので、徹夜で描きました。ノートパソコン標準搭載のマウスがわりの、あの四角いパネル(いつもお世話になっていながら、名前がよくわからない)で描きました。翌日、手がぷるぷるしてたのは今となってはいい思い出です(笑)

 

3.せっかく3Dにしたので

 

次はゲームの内容ですね。せっかく3Dにしたので、上下方向もどんどん使っています。

まずは重力です。空中にいると地面に落っこちるあれです。
Base1.vb, Base2.vb内にある、gravity()関数が担っていて、基本的に、空中にいれば下方向の速度が増加していき、地面に当たると、速さを減退させつつ向きを逆させる、ということを繰り返すことで、落下してバウンドして、という動きを作っています。

3D-DAS

■画像2:バウンドが見事に表現されています。

Base2.vbのほうのgravity()関数のほうが複雑になってるのは、ボスの放つ弾幕の制御の一部をここで負っているためです。gravity()で落下、地面との衝突を管理しているので、重力を無視した弾とかを作るのに、ここに割り込ませました。おかげですごく見にくいです。すみません。
ですが、この見にくいコードから生まれる弾幕はなかなかいい感じにできていると思います。2面ボスの河童さんが出す水滴の弾幕なんかがお気に入りです(格好はいいけど撃たれ放題)。また、4面ボスの噴火型弾幕は迫力もあって、攻撃としての出来もいい感じだと思っています。この辺はプレイしてお確かめください

次は自機の上下移動です。自力では動けません。上矢印型のジャンプアイテムに接触すると問答無用でジャンプします。

上下方向を有効に使いたかったので飛行できるようにしようと思ったのですが、それだと弾幕をよけるのが簡単すぎるし、かといって弾幕を全般的に上下方向にも撒くと、いくら影があってもわけがわからなくなる。そんなこんなでジャンプ式になりました。重力を作ったのもこの時だったかな。と思います

で、始めはジャンプキーを設けていたのですが、避けるのが簡単なのには変わりないし、キーの同時入力って普通二つくらいまでしか受け付けてないようなので(キーボードがいいやつだともっと多く受け付けられるらしいですが)、弾を撃つのと、移動の前後か左右で既に二個埋まってるので、ジャンプはアイテム制にしてしまえ、ということにしました。
ジャンプアイテムがないとクリアできないステージがある一方、敵が真上にいるときにジャンプアイテムをとってしまうと滑稽な自爆を引きおこす。このアイテムは地味にいい味出してると思っています。

 

4.弾幕ゲーが作りたかったわけで

 

今回、弾幕ゲーが作りたいということで始めたのですが、作り始めのころは弾幕になりませんでした。
ゲーム中での物の移動は、座標にベクトル量を加算して動かしていたんですが、

という感じで、できませんでした。弾はいっぱいあるからその座標とかに小数使うと重くなるよなーということで、座標には何が何でも整数型を使おうとしていました。(なぜかベクトルは早々にあきらめて小数型にしたのに)

でも、そのままでは弾幕にならない、ということで、移動に使われなかった小数部を保存する変数(小数型)を各弾の各座標ごとに用意して、1までたまったら座標に反映するようにしました。おかげでめでたく希望した形状の弾幕を作れました。(Base2.vbのmove_base()関数をご覧ください)

3D-DAS

■画像3:きれいな弾幕が見られます。見ている分にはきれいなんですが…。

で、今これを書きながら気付いたわけです。新しく小数型の変数を作ったら、座標を整数型にとどめた意味ないよね?これじゃ座標を小数型にしたのと変わらない、むしろ重くなってる!

これはあれですね、バカですね。はい、みなさんも御一緒に「バカですね!!」

ですが、こんな無駄なコードをはさんでもゲームとして機能できるVBってすごいですよね!ね!誠にすばらしい!!
こんな感じで、コードを見渡すと無駄なところが多々あるかもしれませんが、それでも大丈夫なVBの素晴らしさ、という方向にシフトチェンジしましょう。

弾幕そのものはBullet.vbの後半にある、弾の発射方向、個数などを決める関数を、Boss.vb内で組み合わせたり、Boss.vb内で新たに作ったりしています。

弾幕の弾間の広さや数の調節もここで行っているので、現状の難易度に不満のある方は、Boss.vb内のattack_(数字)()で使っているset_deg()の角度(deg)や、forループの繰り返し回数あたりをいじると、より鬼畜なゲームになりますよ。気が向いたらお試しください。

 

5.クリアできるように

 

最後に苦労したのは、難易度の調整です。途中までは常に無敵状態でデバッグしていたので、いざ通常モードでプレイすると即死でした。私もそんなにゲームの得意な人間ではないので、それも原因ではあるのでしょうが。
うん、あれはひどかった。

まず、ボスが硬すぎるので、特殊弾(ダメージ大、周辺の敵、弾を消滅させる)をつくりました。ボムっていうんですかね。
しかし、これ強すぎて面白くないです。手前の方で引きこもって、ボムを撃ちまくっていればクリア!もうこれはゲームじゃない、ってことでいろいろ制約をつけました。

当然ボスに近づけば、それだけ弾幕を避けにくくなります。
これによって、リスクを冒してガンガン攻めるか、遠くで安全第一か、と戦略みたいなものもでてきて、よりゲームらしくなった気がします。
そのほかにも弾幕の角度や敵の数、発射(出現)頻度、速度、などを調整して出来上がりです。

6.無敵バンザイ

 

ところで、このゲームで遊んでみた方はいますでしょうか?
慣れないと割と難しいところがあるので、苛立ちがこみ上げている方もいるのではないでしょうか?
そこで、こっちが上達しないならゲームを簡単にすればいいのよ!ということで、ここをいじればこうなる、というのをいくつか紹介しましょう。

[不死になる]

Hit.vbのhit_jiki()関数の中のif文中にある
c.jiki.set_c(0, 0, 0)
c.jiki.set_v(0, 0, 0)
c.jiki.set_s(False, 0, c.jiki.HP - 1, c.l_size, 0)
の部分をコメントアウト(行頭に ' を付ける)すると、あら不思議。自機は死ななくなります。
(ボス、敵、弾と接触するパターンの各ループ内のこれら(↑)をそれぞれすべて消してください)
(ちなみに、効果音のあとBGMが鳴らなくなりますのでそれが嫌な人は、近くにあるSound.play_se(0)も、一緒に消してください)

[自機の攻撃力を上げる]

これも同じくHit.vb内の、hit_teki()関数の中にある
c.teki(j).HP -= 数字
やら、
c.boss.HP -= 数字
とかの数字をあげてみてください(c.boss.HP -= 1000みたいに)するとなんとまぁ、あんなに硬かったボスがサクっと倒せてしまいます。
また、この関数内の
c.score += 数字
とかの数字をあげると、じゃんじゃんスコアが上がって、強力な特殊弾撃ち放題!的なことになります。

[~面から始めたい]

Function.vb内のstart()関数内にある
c.stage = {1, 0}
の、1のところ(ここ→{"1", 0})を1~5の数字に変えると、その数字のステージから始められます。また、同一関数内の
Teki.teki_end_flg = False
に、Trueを代入すると、ボス戦からの開始になります。
おまけに、同一関数内の
c.jiki.set_s(True, 0, 5, c.l_size, 0) '5回まで死ねる
の、5の部分を違う数字にすると、LIFE(死亡可能回数)がその数字の状態でゲーム開始できます。

と、まぁ簡単にいじれて、いじくった結果が分かりやすいのはこれくらいですかね。
クリアできずに苛立った人、もっと難しくしたい人、ただ無性にプログラムをいじるという行為がしたい人、どなたも気が向いたら改造してみてください。

 

7.解説、出来てたんですかね?

 

では、これにてこの解説兼感想ときどき自虐の文章を、御理解いただけたかわかりませんが、終わりたいと思います。
長ったらしい文章に最後までお付き合いいただきありがとうございました!!