すぷらとん2_実機

クソゲー1weekのトリセツ 1日目:二次元のトリセツ

ゲーム制作始めました

Unityって知ってますか?
ゲームを作るのに必要な色んなシステムが詰まっていて、聞いたところによるとプロのゲーム制作会社でも使われてるらしいゲーム制作エンジン。それがUnityです。しかもなんと無料で個人でもインストールして使えます!

そんなわけでUnityでゲーム制作始めました
目的は僕という人間のレベル上げです。大事ですよねレベル上げ!
僕はRPGが大好きなのでレベル上げも大好きです。自分のレベル上げは初めてですけど

まずは自己紹介から

素人のスペック

名前:スーパーカブ
ゲーム開発歴0年
Unityは1年ほど前に少し触っただけなので経験は実質0
プログラミングは工業高校時代にC++(ポインタ、構造体あたりまで)を習ったけど、ずっと使ってない上に学生時代もオリジナルでなにかを作ったことはなかったので座学止まり
Unityに至っては上にも書いたようにインストールして数日で投げ出したので実質経験0です

書いた通り、Unityは1年ほど前にShadowverseのバトル画面を作るぞ!って思ってインストールして数日で断念した経験が。いきなり目標が高すぎた
というわけで今回はちゃんとチュートリアルからコツコツとゲーム制作に取り組むことを目標にします
7日連続で時間をとるのが厳しそうなのでとにかく1日1本完成を7回繰り返すのを目指します(この記事を書き始めたのは1日目実施日の3/7)
ちなみに「【Unity】素人が7日間クソゲーを作り続けてわかったこと」という企画をリスペクトしてます! 5年後越しな上に南蛮船耳朶って話ですがスキルアップになりそうなんだから仕方ない
この記事のタイトルもリスペクト元の「クソゲー」とUnity1weekジャムという1週間でお題に沿ったゲームを製作する企画を合体させてます。だからクソゲーという部分に悪意はまったくないです!(予防線)


0日目

まずは準備ということでUnityの更新
前はver5.○みたいな表記だったのがver2017.○みたいに変わってて本当に最新バージョンかしばらく疑ってました
更新したついでに公式サイトにあるチュートリアルの玉転がしも作成。3~4時間くらいかかった
久しぶりでもプログラミングはやっぱり楽しいです
高校時代を思い出しますね!!


1日目 すぷらとん2(試し射ち)

いよいよ開始。まずはスプラトゥーンを作ることにした。理由は時代遅れにも最近スプラトゥーンの動画を見ていたから
とはいえいきなり4vs4の対戦を作ろうとしたら1年前の二の舞なので試し射ち場を作ることに。ちなみに僕はスプラトゥーンを持ってないしやったこともないから動画だけが情報源

簡単な仕様はこんな感じ

ゲームを完成させてからこの記事用に作製
めちゃくちゃ下手くそなのは絵心がないのに加えて
デジタルで描いたからです(言い訳)
絶対に最初に描いとくべきだった・・・

壁に囲まれたL字のフィールドにプレイヤー×1、動かない的×3、左右に反復運動する的×3が配置
プレイヤーの攻撃を当てると的が破壊、一定時間経つと復活するギミックです
本物のスプラトゥーンには2階へのスロープや高台があるけど今回は割愛。プレイヤーを操作して的当てする部分に焦点を当てます

本物では様々な武器を試射できる仕様だけど何種類も武器を実装してる時間はないのでので一番わかりやすいボムを投げることにした(斜め上に投擲して放物線を描くのが物理演算っぽいしね)
爆弾の着弾後に破裂エフェクトが表示できたらいいなあとかも思ってる(開発後記:エフェクト入れる時間なかったです。習得も時間かかりそう)

完成したのがこちら

真ん中下が実際のプレイ画面
手前の円柱+球がプレイヤーで正面の的に向けて
青色の球(ボム)を投げつけてます

スクリーンショットでさえしょぼいのがビシビシ伝わってきますね!
とはいえたったこれだけの中にも数えきれない学びがありました!!!

ほんとはunityで作ったゲームはブラウザでもプレイできて専用の投稿サイトとかもあるので、投稿してこの記事にリンクを張れば誰でもプレイしてもらえるんです

でもなんとunity2017.1以降にはwebGL(ブラウザで遊ぶための形式)で完パケするときにビルド(パッケージ化作業)が失敗する原因不明の不具合があって!!! 見事にそれに引っかかりました!!!!!!!!!!
一応ネットで対処法を調べて色々試したんですが! なんの成果もなく!!!太刀打ちできませんでした!!!!!!! 1人でも遊んでもらえたら嬉しいなと思って8時間ぶっ通しで生み出したクソゲーが自分しか遊べないのやるせない!!!!!

というわけでIL2CPPに関するビルドエラーの解決方法を募集中!!!!!
解決したところで公開されるのはまごうことなきクソゲーですが!!!


1日目のまとめ

とまあやるせない気持ちを無理やり!マークで誤魔化したところで振り返り

・仕様を図にしておくべきだった
毎回一息で完成までたどり着けるとは限らないので時間が空いた時も仕様をイメージしなおせるように、仕様書を作っておくのが重要そう。次からは下手でも先に仕様の図解を描く

・ゲームクリアを用意するべきだった
自分の中ではスプラトゥーンの試し射ちが完成型だったので無限に的当てができるのが当然と思っていたけど、誰かに遊んでもらうことを考えるとクリアは必要不可欠だった。作る側のイメージの押し付けがわかりやすく表れたので自戒にしたい
もしクリアを作るなら的が復活する前に全部破壊とか、あるいは終わりがない代わりに当てるほどスコアが増える(コンボあり)とかが良かったかな

・タイトルと操作説明を付けるべきだった
終わってから気づいたけどタイトル画面はあるだけでゲームっぽさが増すので付けるべきだった(マウスクリックで無効化処理とかでできるかな?)
あとゲーム単体では何をすればいいかわからなかったので操作説明も欲しくなった(ボタンをクリックで操作説明表示。テキストウィンドウはタイトル画面と同じ仕様で実装できそう?)
これはゲームらしさに大きくかかわるので2日目には必ずクリアしたいところ

・アセットストアを使いこなせるようになる
今回はプレイヤー用にイカの3Dアセットを使おうと思っていたけどストアの検索が捗らず、断念してオブジェクトを組みあわせることになった
欲しい3Dアセットを探す能力は必須かつ習得が早いほど絶対リターンも大きいので、一刻も早く習得したい


2日目の目標

最後に2日目にやりたいことをまとめて1日目の記録を締めたいと思います

・他のオブジェクトが持つデータを参照する
RPG大好き人間なのでRPGの根幹であるダメージの計算(プレイヤーとエネミーのステータスの参照)ができるようになるため、オブジェクトAのデータを参照したオブジェクトBが処理。みたいな動きが必要になるゲームを作る
具体体にはジャンケンとかトランプとか? トランプは必要なオブジェクトが多そうだから厳しいかも

・タイトル、操作説明、ゲームクリアを実装する
ゲームといえば欠かせないこれらの要素の実装を覚える。一度覚えてしまえば概ね使い回せると思うし、習得が早ければ早いほどリターンも見込める最優先事項

・アセットストアを使う
今回は時間を割く(気持ちの)余裕がなくてアセットを使わなかったので、次回は必ず使うことにする
とりあえず何かアセットをストアで購入してダウンロードして使用までが目標
最悪ゲームにまったく必要ないものでも目的を決めて探して、オブジェとして配置したら完了。としておこう

というわけで1日目の挑戦はここまでです!
完パケ品を投稿できないのは本当に残念だし、今は作るという達成感と充実感でドーピングしてるだけで、いつモチベーションに大きな支障をきたしてもおかしくないのでなんとか解決したいです!!!
解決して投稿した暁にはぜひプレイしてみてくださいね!!!!!
てわけでスーパーカブでした!!!


ツイッターやってます
FGO専門のアカウントとして開設したので今はそっち系多めですが、少しずつツイートの割合を変えていく予定なのでフォローお願いします!!!
スーパーカブ@hyperkabu50cc



ここから先はほぼ自分用の覚え書きです

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

プレイヤーを動かす

まずはPlaneで生成できる板をL字に3枚配置して壁で囲う。板の大きさはデフォルトの10m×10mに

次はプレイヤーを配置。イカをアセットストアで探そうとするがうまく探せない。ここらへん英語力不足が痛い(イカを英語でなんて言うか1分くらい悩んだ)。というかunityが全体的に日本語化されてないこと多くてちょっと辛い

アセットストアとずっとにらめっこしてても作業は始まらないので、ゲーム内でオブジェクトを組みあわせて作ることに(とはいえアセットストアの活用は最優先スキルだからすぐに習得したい)
ところが三角柱の作り方がわからない。さらにさらに計画変更で円柱の上に球を乗せてプレイヤー完成
フィールドの広さに合わせてプレイヤーは50cmくらいにしたけどこれが間違いだった(後述)
一応プレイヤー生成に使った円柱と球を空のオブジェクト「Player」にまとめて入れておいた。これで管理しやすくなるのかな?

続けてPlayerに矢印キーに合わせてAddForceで力を加えるスクリプトを実装。ここらへんは昨日の玉転がしでやったから順調・・・と思いきやまた問題発生
前日に操作した球と違い今回のプレイヤーは下が面なので、力を加えると移動せずに倒れてしまう。倒れた後は2方向に転がれるけどそうじゃない

どちらにせよAddForceだと慣性が働いてスプラトゥーンっぽくないので、別の移動方法を探す
ぱっと思いついたのが矢印キーが押されている間、その方向の座標に数値を足し続ける方法だったので調べるとtransform.positionを使って移動させるといいみたい。汎用性も高そうだし習得のために採用
unityにはxyzの3要素を扱うVector3という型?が存在するがC#では直接Vector3を代入できない(Player.positon=(10,0,10)みたいに記述できない)みたいなので、Vector3を扱う変数posを宣言してそこに座標を代入して扱うことに(開発後記:new Vector3(x,y,z)という記述をすれば直接xyzを代入できるっぽい?)
これでようやくプレイヤーを自由に動かせるように!
(プレイヤーをまっすぐ移動させてると頭の方から倒れる問題も発生したがRigidbodyのインスペクターにあるFreeze Rotationのx軸とz軸にチェックを入れたら解決した。チェックを入れ軸が動かなくなるみたい。positonもあったからシンボルが衝突したときにずれてほしくないオブジェクトとかに使うと良いかも)

ちなみに移動速度が速すぎると壁を突き抜けるし、移動速度が適切でも壁と壁の継ぎ目はすり抜けて落下する。こういう不具合見つけるとめちゃくちゃテンション上がりますね!
(関係ないけどSAOHR(ソードアートオンラインのゲーム)で中ボス登場のムービー中に移動ボタンを押しっぱなしにしてたら、ムービー終了後に壁を突き抜けた先の暗黒空間にいた。最初は壁に引っかかってた敵も突き抜けて追いかけてきたり、帰り方がわからなくて壁に片っ端からジャンプ切りしてたらすり抜けて帰れたりとめちゃくちゃ笑わせてもらいました)

カメラを追従するようにしたら深刻なバグが

続けて玉転がしで習得したカメラを追従するスクリプトを実装
なんですが壁に勢いよくぶつかると突然カメラが暴れ出すように。解決できなかったので、回転したり高さが変わったりすることはなく、xz軸だけで動き回ってたことだけメモしとく(ぐるぐる動くから酔った)
このあとのボム投擲とかでもプレイヤーの座標を参照するとおかしな挙動になったからプレイヤーオブジェクトそのものがおかしいのかも
あと円柱と玉の複合だからかy軸の座標が常におかしくて、余計にy軸を加算する処理をするはめになった。要調査

ここら辺で計画通りに作ろうと思うとカメラを任意で90度回転させるシステムが必要なことに気づいた。時間的に無理そうだったので移動する的は断念(よく考えたらパネルを移動して動かない的の奥に動く的を配置すれば良いだけだった)

次はボムを投げられるように

プレイヤーの移動とカメラの追従がひとまず終わったので、いよいよ新要素の物の投擲に突入
(実はその前にボムがないと消滅するスクリプト作る意味がないことに気づかずに的オブジェクトを作って配置してた)

とりあえず仕様としてはスペースキーを押したらプレイヤーのそれっぽい位置にボムオブジェクトを生成、45°くらいで射出されてほしい
prefab機能を使えば1つのオブジェクトをいくつも使い回せることは玉転がしで習得済みなので、必要なのはprefabのオブジェクトを指定して任意の位置に生成するコマンド
検索するとInstantiateで実行できるらしい
作ろうとしてるゲームが実質大砲ゲームなので情報を探しやすくて便利

というわけでInstantiateを使ってボムの射出を実装。ボムのオブジェクトはシンプルにスフィアで実装しました
まずは参考にしたプログラムに則ってプレイヤーの座標から水平方向に発射する仕様にしたのに、なぜかプレイヤーの立っている場所とは見当違いのところからボムが射出される。でもプレイヤーを動かすと同じだけ射出位置も移動する
しばらくこねくり回してみたが直せなかったので諦めてプレイヤーの座標に固定値を足したものを射出場所に設定。プレイヤーの頭位からボムが投げられるようになったがスクリプトは美しくないのが無念
カメラといいプレイヤーの座標側に問題があるのかな?

で、次はボムが当たった際に的が消滅する仕組みづくり
ボムの方は的だけでなく壁や床に落ちたときも消滅してほしい。
一方で的はボムが当たったときのみ消失して、プレイヤーの接触では消滅してはいけない
というわけでボム側にOnTriggerEnterで接触時の判定を実装。接触した場合にDestroyで自身を破棄するようにした
接触した相手に干渉しないようにTriggerを使ったけど、接触する相手が的(接触時にこっちも消滅する)、壁(staticで固定)、床(staticで固定)とどれも干渉されない者だったので意味はなかったかも。システム的に衝突が不可能ではあるけどプレイヤーとの接触も考慮してたってことにしよう
合わせて的側にも接触時に消えるシステムを搭載。今確認したらこっちもOnTriggerEnterになってたけどisTriggerにはチェックを入れてなかった

開発後記:TriggerとCollisionの違いを調べなおしたらどちらもrigidbodyのときにtrueになるのがColision、どちらか一方でもTrigger化されていればをtrueになるのがTriggerみたい
試しに的オブジェクトのisTriggerにもチェックを入れたらプレイヤーの接触でも消滅するようになってしまった
反対にボムの方のisTriggerを外すとなににぶつかっても消滅しなくなってしまった。Trigger判定を持つオブジェクトが存在しないから当たり前か
たまたまチェックを正しい場所に入れてた製作中の自分偉いし、今見直してもう一方も試して学んだ今の自分も偉い

まとめ:何にぶつかっても消滅したいならTriger判定を持たせてOnTriggerEnterで判定。特定のものとの接触時のみ消滅させたいならOnTriggerEnterで判定してTriggerは自身でない方に仕込む

大詰めにして最大の壁 復活システム

いよいよ最後の仕様である的の復活システム
イメージは消滅した的が数秒後に同じ場所にリポップする(エフェクト不問)
復活システムを付けようと思った時は大体下のような流れで実装できるだろうと思ってました

消滅時に時間を計測するタイマーが始動

一定時間(数秒)経過で復活プログラムが始動

壊された的があった座標にInstantiateでprefab化した的オブジェクトを生成

タイマーをストップ。中身を0に

こんな感じで問題ないだろうと思ってたけど大問題発生
的の消滅はDestroyを使ってるから接触した時点で自分を破棄してしまい、それ以降のスクリプトを実行できない=タイマーで復活をカウントできない
空のオブジェクトをTarget管理役にして消滅とリポップとまとめておこなおうかと思ったが、外のオブジェクトを指定して消滅する方法がわからない。ついでにVector3の取り扱い(プレイヤーを動かすの項)も遠回しな方法しか知らないから座標指定の記述が長くなる

というわけでDestroyせずにオブジェクトを無効化する方法を模索(オブジェクトが別のオブジェクトを指定して操作するのは、それをメインとするゲームでしっかり習得したかった)
どうやらオブジェクトの無効化と有効化を1処理でおこなえるSetActive(true or false)というコマンドがあるらしいので早速組み込む
非表示にするためのSetActive(false)はボムがヒット時のOnTriggerEnter関数に実装。さらに復活処理の開始を示すためにフラグ用変数をtrueにする処理も一緒に入れておいた
復活のための(true)の方は毎フレーム実行されるUpdate関数に実装。条件を満たしたときだけ実行されてほしいのでif文の中に入れた。条件はSetActive(false)時とセットでおいたフラグ変数がtrueである、とタイマーが設定した時間に達したの2つ。プログラムの可読性的な話を考えるとif文の入れ子はよくないのかな。そもそも作るのに手いっぱいでコメント文もろくに描きこめてないけど。これは猛反省してます
タイマー用の変数は1つめのifを通った直後に配置。なぜか1秒間のフレーム数のブレを考慮してdeltaTimeを使うことだけ知っていたから使用
(開発後記:よく考えたらプレイヤーの移動にも使うべきだった。今のままだと1秒間のフレーム数で移動距離が変わってしまう)

というわけで完成! とは行かずなぜか復活しない不具合発生。ここから2時間泥沼の調査が始まった
まず失敗だったのがSetActive、if、deltaTimerのすべてを同時に実装したこと。これのせいでどこが問題個所か全くわからずにあっちこっちコメント分にしてテストしていくはめになった(フラグ変数のbool型も初めて使ったのでそれも調べた)
ifのテキスト、タイマー周りのテストを終えてようやくSetActive周りがちゃんと動いてなさそうなのが判明
ほどなくして原因も判明。なんとSetActiveをfalseにすると見た目や物理判定だけでなく仕込んだスクリプトも停止するらしい。これじゃDestroyと変わんねえ!
当然タイマーをカウントする部分も動かないので何十分待っても時間経過を判定するifが満たされることはなくSetActiveがtrueになることもない。ダメじゃん
SetActiveの仕様は凄い徒労に終わったことが分かったのでまた新たな消滅方法を模索するところまで巻き戻し(実際は失敗して学ぶ実感があったからめちゃくちゃ楽しかったです)
次に見つけたのがenable。オブジェクトを描画するRendererや衝突判定を管理するColliderの状態を指すようでこれをfalseにしてやれば対応した機能をオフにできるらしい
というわけで早速実装。雛型はSetActiveでできていてテストも概ね終わってるので早速組み込んでみたら1発で成功! 今回の製作で1番掛かりました
ちなみにRendererなどをenable = falseするには「Rendererを扱う変数を宣言(今回はrenにした)」→「変数にGetComponentでRendererの状態を格納」→「変数のenable(ren.enable)にtrueかfalseを格納」とする必要がある。Vector3と同じで直接はいじれなかった

完成!!!・・・と思いきや

予想外に復活を手間取ったものの何とか実装できたところでタイムアップ
一応エフェクトについても調べたけど片手間でちょちょいとできそうになかったので今回は断念

最後にBuildして完パケしたものをunityゲーム投稿サイトに投稿したら1日目の挑戦終了!と思ったら最後に最大最強の問題が

なんとwebGLでBuildができない!!!

何回やっても指定した完成品フォルダになにも保存されないからおかしいと思って調べてみたら、unity2017.1以降で発生する原因不明の不具合らしい!
これじゃ誰にも遊んでもらえない!!!!!!! 遊んでもらってこそのゲームなのに!!!!!!!!!!

諦めきれずにAssetをコピーして新規プロジェクトでBulidし直したり、練習用の玉転がしをBuildしてみたり、unityフォルダ内のnode.exeを以前のバージョンに置き換えたりしてみたけどまったくできない!!!

そうこうしている内にタイムアップから大幅にオーバーしてしまったので諦めて一旦終わりにすることに。原因不明の不具合なんてどう対処しろっていうんだ・・・
1人でも遊んでもらえることを夢見ながら8時間通しで作ったゲームはひとまず、僕以外の誰の手にも渡ることなくお蔵入りしたのでした・・・・・・
泥沼にはまった2時間を始めにいっぱい学びがあった8時間だったから充実感と達成感はあるけど、やっぱり完成できないのはやるせない・・・

とはいえこれでやーんぺって言うわけにもいかないですし言うにはゲーム制作面白すぎたので2日目も頑張りたいと思います(実施日未定)
2日目は他オブジェクト内が保持しているデータを参照するのを取得したいな