ブルーノのC#プログラミング & unity勉強日記

プログラミング素人、ブルーノの自主勉強ノートです。他のプログラミングを勉強したい方の助けになれば幸いです。その他趣味の雑記もしていきたいです

No.002 完成

f:id:covory10101101276:20190716211229j:plain
アウヤンテプイ -ギアナ高地-

みなさんこんにちは🌚

もう7月も半ばなのに一向に晴れる気配がなくて気分もじめっとしてるブルーノです😓洗濯物が乾かない...
さて今回も今まで作っていた作品をやっていきましょう...と言いたいのですが実は最近作品を完成させてしまい、記録を残していませんでした笑 実は諸事情により記録をつけている余裕がありませんでした。なので今回は完成作品のスクショを貼っていきます。

完成品

f:id:covory10101101276:20190716212553p:plain
タイトル
タイトルメニュー。タイトル画面中はカメラが動き回りながら町中を写しています。

f:id:covory10101101276:20190716213714p:plain
ステージその1

f:id:covory10101101276:20190716213645p:plain
ステージその2

残りも公開しようと思ったのですがAppStoreにあげようと思うので今回の紹介はここまでにしておきます。もし気になったらぜひダウンロードしてくださいね。実際にリリースしたら記事でも紹介しようと思います。
でも製作者の名前がわかっちゃいますね笑まあいいか笑

No.002 進捗状況16[ゲーム画面のUIの実装]

f:id:covory10101101276:20190517111503j:plain
コルコバード -ブラジル-

みなさんこんにちは🌚

では今回もやっていきましょう!!

これまでゲーム中のオブジェクトやスクリプトなど、ゲーム全体の中枢をなす舞台を主に作ってきました。今回は実際にユーザーが触れるための境界、媒体となるUIを実装していきます。

MobileSingleStickControlの改良
といっても実は以前導入部分だけは触っているんですよね。ただ一番初めに触るべき場所はここじゃないと思ってそのまま放置してしまいました。過去の記事はこちら
dreameaters5239.hatenablog.com
キャラクター移動のスティックは作ってあるので今回はこのCanvasに加速のボタンとライフを実装しようと思います。
加速ボタン(CrossPlatformInput)
以前作った時計っぽいアイコンはデザイン変更しました。

f:id:covory10101101276:20190517194213p:plain
左上の白丸が加速のためのスイッチ。
会話ウィンドウも後ろに写っていますが開始時に消えるので気にしないでください😇
さて、このMobileSingleStickControlもといCrossPlatformInputは仮想ジョイスティックおよびボタンをInputManagerに割り当てたボタンに対応させることができる機能です。
f:id:covory10101101276:20190517195458p:plain
ButtonについているEventTriggerでどのタイミングで動作するかを設定し、ButtonHandlerで作動させるInputManagerのボタンを指定します。今回はTimeQuickerという名前を指定しました。なおこの指定する名前はInputManagerに登録してないと使えないので注意
f:id:covory10101101276:20190517195915p:plainf:id:covory10101101276:20190517195931p:plain
そして名前空間でUnityStandardAssets.CrossPlatformInputを指定して、InputManager.で呼び出しましょう
下のスクリプトを同じくボタンにコンポーネントとしてつけます。
f:id:covory10101101276:20190517200147p:plainf:id:covory10101101276:20190517200206p:plain
4行目でCrossPlatformInputを指定して、InputManagerを使えるようにしています。そしてこの長いスクリプトを要約すると
・内蔵しているChargeTimerをボタンを押している間増やしていく。
・タイマーが基準値を超えている時にボタンを離すと加速する。
このたった2つに落ち着きます笑
さて、このスクリプトをわざわざButtonに作ったのには実は理由があります。知り合いから教えてもらった小技で、画像を円形に表示できる機能を使って円の満ち欠けを使って時計盤っぽく表現しようと思います。
その方法が下の通り。ImageコンポーネントのImage Typeを[Filled]に、Fill MethodをRadial 360にします。
f:id:covory10101101276:20190517204451p:plain
そしてその下のスライダーを左右させると...
f:id:covory10101101276:20190517204716p:plain
画像が円形に満ち欠けする!!
そして先ほどのスクリプトにこのFill Amountにアクセスする記述をします。
f:id:covory10101101276:20190519122642p:plain
ハイライトしてある部分がその部分ですね。36~39行目に書いてある通りImageコンポーネントを取得、さらにそもそも最初にUnityEngine.UIにアクセスできるよう名前空間を指定するのも忘れないように
そしていろいろリデザインした時計でこれを起動すると!!
f:id:covory10101101276:20190519123013p:plain
動かない!
なんででしょう!どうやら原因はいろいろリデザインにあるらしいです...
いろいろImageを重ねたり子にして階層を配置してしまうとその画像に当たり判定が阻まれてしまい正常に動作しないようです。原因を探っているときよく目にしたCanvasコンポーネントをアタッチしてOverride Sortingという機能を使う対処法もなぜかうまく動作しなかったんですよね。
f:id:covory10101101276:20190520142138p:plain
全ての階層の画像にこのコンポーネントをつけて順番を変えられるはずが...?
なのでまた別の方法を見つけました。Graphic RayCasterという方法です。
f:id:covory10101101276:20190520142406p:plain
これはCanvasにアタッチするタイプのコンポーネントで、これをつけているとこの階層のImageすべてにタッチ情報が送られます。しかし、もちろんImageコンポーネントにRayCast Targetがチェックされていない場合は反応しません。これで....
f:id:covory10101101276:20190520142642p:plain
ようやく成功!!!
やっとできました!!長かったですね。。。

ライフUI
では今回はさらにもう一つゲームにはおきまりのライフゲージを今更ですが実装していこうと思います!!
UIに先ほどと同じ要領でImageたちを配置...
f:id:covory10101101276:20190520155845p:plain
彼らはタッチする必要がないので先ほどのような行程はいりません
f:id:covory10101101276:20190520160013p:plainf:id:covory10101101276:20190520160024p:plain
そしてプレイヤーのスクリプトにライフのint値および敵にあたった時の処理を記述する。この時LifePointはPublicにして他からアクセスできるようにしておきましょう。
f:id:covory10101101276:20190520160233p:plain
そしてこちらの新たに作ったLifePointScriptを先ほどのライフのUIにアタッチします。このスクリプト
・プレイヤーのライフにアクセス
・自身のImageコンポーネントにアクセスし、ライフに応じて画像の満ち欠けを3等分
という風になっています。
さあ起動してみると....

f:id:covory10101101276:20190520160549p:plain
ライフが減ってそれに応じた欠け方になった!
うまくいきましたね。イーサンがいないように見えるのはさっきプレイヤースクリプトに足したコルーチンでイーサンの姿を点滅させているからです。今回はここまでにしておきます。
まだ料理でいうと材料揃える段階から進んでいない....

No.002 進捗状況15[ゼルダっぽいタイトル画面]

f:id:covory10101101276:20190514165718j:plain
ブライスキャニオン国立公園 -アメリカ-

みなさんこんにちは🌚

GWは魔の1日バイト9連勤を果たしたブルーノです。今年のGWは天気もそんなによくなかったので遊びにはどっちみち行けなかったから良いんですけどね😠😭

では早速作っていきましょう🌻

今回はタイトルにもある通りゼルダっぽいゲームのタイトル画面を実装してみたいと思います。
ちなみにこんな感じ
www.youtube.com
特徴としては実際のマップを読み込んでおり、その上にタイトルメニューがあることですね。この形式の良いところは最初にマップデータを読み込むのでそこからゲームシーンに移ればマップのオブジェクトを隠したり一度消したりしなくて良いところだと思います。私からしてもこれは簡単だと思い、今回つくろうと思いました。

タイトルのUIの実装
タイトルとゲームスタートのボタンを作りたいと思います。例によってかめくめさんのサイトに良い記事があったのでこれを真似させていただきます。
gametukurikata.com
地味にこのサイトに貼ってある無料日本語フォントサイトのリンクが嬉しかったです。AssetStore有料だし日本語フォント少ないし🌝
f:id:covory10101101276:20190515111659p:plain
こんな感じにしました!!タイトルはどーしよーかなー

そしてGameStart()メソッドを持ったTitleSystemスクリプトを空のゲームオブジェクトにアタッチし、StartButtonに指定します

f:id:covory10101101276:20190515112048p:plain
このスクリプト
f:id:covory10101101276:20190515112110p:plain
適当なオブジェクトにアタッチし....
f:id:covory10101101276:20190515112138p:plain
StartButtonから呼び出す!
ただこれだけでは暗転するアニメーションだけでシーンが変わったりする機能は持っていません。そこで今度はシーン遷移の機能も実装します。今回のゼルダっぽいシーン遷移の方法の利点である先に建物等のロードを済ませるという作業は次のシーンにロードした建物たちを引き継ぐことでそのメリットを活かせます。ということで今のTitleスクリプトにその作業も追加しなければなりません。そこで、またかめくめ氏のこのページを参考にしました。
gametukurikata.com
データを残す方法としてシングルトンという方法を昔少し使ったのですが、いまいち覚えてなかったのでまた勉強です。
f:id:covory10101101276:20190515150336p:plain
シングルトンの中身
はい、というわけで10行目およびAwakeメソッドの中身がシングルトンの実装になります。
10行目で自身のスクリプトをstaticな形で宣言し、共用で使うオブジェクトを12行目のShareObjectに格納しておきます。
最初にテストシーンから起動すると10行目のシングルトンインスタンスには何も入っていないのでこのスクリプトが割り当てられ、25行目にあるように決められたオブジェクトたちをDontDestroyOnLoad()メソッドで壊されなくします。
この状態でこのスクリプトをもつテストシーンから同じくこのスクリプトを持ったオブジェクトがあるゲームシーンに遷移すると、staticな変数にもうインスタンスが割り当てられているということで23行目にあるように新しくAwake()を通ったゲームシーンの方のスクリプトは格納されている同じオブジェクトたちを削除し始めるんですね。おかげで各シーンに同じオブジェクトがあっても共存することなく、かつどのシーンからプレイしても同じ状況でプレイできるということですね。

要はこの唯一無二のインスタンスを保持するためのデザインパターンがシングルトンというようですね。
個人的に一つ気になったのは、ゲームシーンには一応全てのマップ用のオブジェクトが配置してあるわけですがAwake()メソッドを通る前に結局同じオブジェクトたちを一度シーンに作り出すのではないか?ということです。Awake()以前ならレンダリングもされていないから負荷はかからないのでしょうか。ここだけ少し疑問が残りました。

タイトル画面で動く背景
では最後に、タイトル画面のカメラワークを作りたいと思います。「時のオカリナ」から続く3Dゼルダはみんなタイトル画面で実際のマップを使っているのが特徴ですよね。時にRTAやTASでそれが悪用されることもありますが...
カメラワークの作り方はこちらのサイトを参考にしました。
sirohood.exp.jp
動画付きでわかりやすい!!シロフードさんありがとう!!😽
こうして...

f:id:covory10101101276:20190515170313p:plain
できた!!
画像なのでわかりにくいですが、ちゃんとタイトルメニューの後ろでカメラが町中を飛んでいます!!成功です!
今回はここまでにしておきます。、。、

No.002 進捗状況14[人に話しかける挙動]

f:id:covory10101101276:20190508164311j:plain
カッパドキア -トルコ-

みなさんこんにちは🌚

さて今回は以前作ったメッセージスクリプトと組み合わせてゲームのような話しかける処理を実装していこうと思います。

f:id:covory10101101276:20190508164535j:plain
イメージ画像
しかしこの話しかける処理を考えてみたのですがやることが
まず
・会話可能範囲内で対象にカーソルを表示 ←この時複数のキャラが被った場合の処理も考える
そして
・カメラを会話用の位置に移動
・メッセージウィンドウを表示
・キャラ同士を向かいあわせる
と、意外に多いのでとりあえずまだわからないカメラの移動をおいといて他の機能をどうにか実装しようと思います。

カーソルの実装
話しかけることのできるキャラクターをわかりやすくするためにカーソルをまず設けました。
f:id:covory10101101276:20190508210332p:plain
カーソルという空のオブジェクトの子にそれぞれ上部の逆三角錐と下部の円を置いています。地味にこの逆三角錐は初めてのProBuilderで製作したものだったりします。
unity3d.com
最近標準搭載された3Dモデリングツールです。とても簡単!
そしてこれにMessageManagerスクリプトと、対象のオブジェクトに話の内容と位置情報を格納したActiveMessageスクリプトを作成
f:id:covory10101101276:20190508210919p:plain

f:id:covory10101101276:20190508210935p:plain
MessageManagerスクリプト
f:id:covory10101101276:20190508211002p:plain
ActiveMessageスクリプト
ActiveMessageスクリプトは以前作成したメッセージウィンドウのものをちょっといじっただけですがMessageManagerスクリプトは一から書いたためスパゲッティになっています笑笑

わかりにくいので解説するとMessageManagerスクリプトでその名の通りActiveMessageスクリプトを持つオブジェクトを一括管理し、一番プレイヤーに近い会話オブジェクトと同じ座標にカーソルが常にいます。そしてカーソルオブジェクトのトリガー範囲内に入った時だけカーソルが可視化&話すことができるというわけです。製作過程でキャラの範囲が被った場合はどうしようか悩みましたがOnTriggerEnterとExitで実装すると被っている場合でもすんなり処理してくれたのでラッキーでした。そもそも実際のゲームプレイではNPCは離して配置すれば良いだけですしね。

キャラ同士を向かいあわせる
この動きがなかなか大変でした。
数学なんてここ最近ちっともやっていないので一応実装に成功した今でもベクトルだとかQuaternionだとかの理解がちゃんとできていないと思いまする。

f:id:covory10101101276:20190514164741p:plain
向かいあわせる動きをActiveMessageスクリプトに追加
どうしてもお互い向き切った後の角度の参照の仕方がわからなかったので時間で管理しました。一応2秒あればどんな角度でも向けるはずなので問題ないです。実行すると...
f:id:covory10101101276:20190514165115p:plain
お互いに向き合った!!
なんとかできました!!
ずいぶん時間が空いた&リアルが忙しかったですがなんとかまた一つ実装に成功しました。次回以降はいよいよゲームシステムの大枠に移っていきたいと思います。

No.002 進捗状況13[ジャンプする箱]

f:id:covory10101101276:20190428192104j:plain
シャウエン -モロッコ-

みなさんこんにちは🌚

では今回は現在進行中のプロジェクトの最後(の予定)のオブジェクトを作っていきたいと思います。次回以降はゲームの中身的なものを作っていきます。

ジャンプする箱
速く動いてやりたいスタイリッシュなことというわけで考えた結果、落ちる崖やガレキの上を飛んでわたるというアクションを実装することにしました。イメージとしては下の感じ

f:id:covory10101101276:20190428193353p:plain
FF7ACCはスタイリッシュさが詰まっててワクワクするなあ
まあ加速とは関係ないですがイメージということで

まずはいつも通りBoxを用意
目の前には飛び乗るためのジャンプパネルを配置
f:id:covory10101101276:20190428194940p:plain
さらに飛んで渡るためにボックスを連続して配置し、その上にジャンプパネル

f:id:covory10101101276:20190504181449p:plain
こんな感じ
この長いボックスはプレハブ化しておきます。そしてジャンプパネルはもうあるのでこのボックスの動きを制御するスクリプトを作成
f:id:covory10101101276:20190504181646p:plain
f:id:covory10101101276:20190504181807p:plain
重力の適用を遅くするという挙動はなかなか実装方法が思い浮かばなかったので代わりにこの箱のみRigidbodyではなくAddforceによる重力の実装をし、この値を変えることで重力の変化を表現しました。しかし絶対にもっと良い方法があるはずなので少し不満が残りますね。まあさっさと作るために今回は仕方ないです。また今度勉強しましょう。さあ動きを確認すると...
f:id:covory10101101276:20190504183758p:plainf:id:covory10101101276:20190504183815p:plainf:id:covory10101101276:20190504183828p:plain
だいぶイメージっぽい感じにできました!!無駄に3つのアングルから撮ってみました。
でもジャンプパネルに乗った時の挙動が一回ごとにやけに前に飛んだり逆に飛ばなかったり安定しないんですよね。一番健闘できた動きがこれなので今回はこれまでにしておきますがまだまだ改善の余地はありそうです。

No.002 進捗状況12[RPG的なメッセージウィンドウ]

f:id:covory10101101276:20190421111135j:plain
ラス・ラハス教会 -コロンビア-

みなさんこんにちは🌚

今回はオブジェクトではなく、RPGゲームでよくある会話ウィンドウを実装したいと思います。といっても、例によってこちらのサイトの方法を実践するだけなので、こちらをみてもらった方がとてもわかりやすいです。かめくめちゃんさんには毎回お世話になってます😊
gametukurikata.com
まずは最初にある通りキャンバスに色々配置。
f:id:covory10101101276:20190422174214p:plain
MobileJoyStickもキャンバスなんだけどいいのか。いや、とりあえずやってみよう。操作とメッセージは同時に表示しないからあとで切り替えれば大丈夫だろう
次にメッセージスクリプト作成。勉強のため中身を謄写することを試みましたがUpdate()メソッドの中で迷子になったためそこだけコピペしたのは内緒。
f:id:covory10101101276:20190422174700p:plain
f:id:covory10101101276:20190422174722p:plain
f:id:covory10101101276:20190422174744p:plain
これを全部手で写しましたなどと言えばコメントアウトまで綺麗に写したのかと褒められるか見破られるかの2択でしょう。
Updateメソッドの途中でこんがらがってしまったのですが用意した変数や他のメソッドの中身とサイトの記述から大体の仕組みは理解できたと思います。特に最後のSetMessagePanelはこのスクリプト内では呼び出されないものの他のスクリプトから呼び出す際に便利なよう設計されているのは勉強になりました。普通のことかもしれませんが私の中にはなかった概念です。

そしてこれを呼び出すゲームオブジェクトを作ります。
空のゲームオブジェクトに次のスクリプトをアタッチし、今回はmキーで会話を呼び出してみます。

f:id:covory10101101276:20190423101822p:plain
Input.GetKeyDownの中身だけ変える
実行してみると...
f:id:covory10101101276:20190423102401p:plain
できた!!
いやすごいですね。ですが今回作りたいゲームは縦画面なのでUIの構成とかを調整していく必要がありそうです。では今回はここまでにしておきます。

No.002 進捗状況11[NavMesh]

f:id:covory10101101276:20190418202157j:plain
氷河急行(グレッシャー・エクスプレス) -スイス-

みなさんこんにちは🌚

一口サイズに材料を切るのが苦手なブルーノです。

では今回も新しいオブジェクトを作っていきたいと思います。
ところで最近Youtube仮面ライダーカブトの動画を見たんですが、これも今私が作っているゲームと同じく時間を圧縮した中で敵との戦いを繰り広げるといった内容で、とてもかっこいいんですね。というか、このゲームの元ネタはぶっちゃけこの仮面ライダーカブトです。そして仮面ライダーには敵がつきもので、このカブトの敵も超高速で動くため常人には捉えられないんですね。というわけで、今回作るオブジェクトは「超高速で動き回る敵」にしたいと思います。そして動かす敵は、街を作った際にノリでストアから手に入れたこの人にしようと思います。
f:id:covory10101101276:20190418210130p:plain
にしてもなんでこのゴリラおしゃぶりしてるんだろう。では頑張っていきます。

NavMesh
昔Unityの勉強の記事で少しだけ触れたNavMesh、自動で歩ける道を判別しその上をAIが勝手に動いてくれるという代物を今回は使いたいと思います。こちらのサイトを参考にしました。というか、まんまこのサイト通りです。毎回お世話になっています。kanさん。
kan-kikuchi.hatenablog.com

まずはテスト用のフィールドと、ゴリラを配置。
f:id:covory10101101276:20190418211033p:plain
Window→AI→Navigationを開き、Objectタブから歩かせたいエリアに該当するオブジェクトを選択し、NavigationStaticにチェック。
f:id:covory10101101276:20190418211618p:plain
NavigationAreaは[Walkable]を選択しておきます。
f:id:covory10101101276:20190418211709p:plain
準備ができたらBakeしましょう。

f:id:covory10101101276:20190418211859p:plain
ここでも足場の設定が色々できる模様
そしたら今度は、ゴリラにNavMeshAgentというコンポーネントをつける。これだけ。なんて簡単なんだ。
f:id:covory10101101276:20190418212605p:plain
動きを速くするため、SteeringのSpeed, AngulerSpeed, Accelerationを、アホみたいに速くしてみました。これらはそれぞれマリカでいうスピード、曲がりやすさ、加速ですね。
あ、さきほどこれだけと言いましたが、最後にスクリプトから目的地を設定してあげる必要がありました。嘘ついてごめんなさい。
しかし目的地の設定も、NavMeshAgent.destinationにVector3型の座標を指定するだけ。そこで今回は、目的地をこんなオブジェクトにしてみました。
コライダー だけのCubeを用意し、時間経過もしくはゴリラが触れることで特定の座標を移動する目的地です。
f:id:covory10101101276:20190418213826p:plain
f:id:covory10101101276:20190418213916p:plain
これでゴリラが常にある目的地を目指して走り出すはず.....とおもったんですが......
f:id:covory10101101276:20190419184342p:plain
ゴリラが恥ずかしいことになったまま走り回ってるんですが.....原因を調べてみると、どうやらこのアセットにもともとついていたAnimationが、NavMeshと競合してしまっているようです。
NavMesh Agent を他のコンポーネントと共に使う - Unity マニュアル
NavMeshAgentは独自のPhysicsとAnimationコンポーネントを持っているらしく、そのまま一緒に使うと競合を起こしてしまい正常に機能しないようです。ちなみに競合というのは複数のプログラムなんかが同じデータを使おうとして競り合ってしまうことです。まあゴリラがブルブルするアニメーションは今回いらないのでアニメーションは今回は捨てることにしました。Animationコンポーネントを外すと...
f:id:covory10101101276:20190419185449p:plain
直りました。が、今度はまた別の問題が。ゴリラが速すぎてプレイヤーの当たり判定が貫通してしまいました。
f:id:covory10101101276:20190419192152p:plain
どうやらこれは物理演算の関係上速い動き全てを計算すると負荷がかかってしまうのでコマ送りになってしまっているそうです。というのも全てこちらのサイトに書いてあったんですよね笑
【Unity】物理演算で動かすオブジェクトが壁を貫通する問題と対策 - テラシュールブログ
というわけでゴリラにRigidbodyコンポーネントをつけてCollision Detectionを[Continuos Dynamic]にします。ちなみにIs KinematicにチェックをつけないとNavMeshAgentと競合を起こします。
f:id:covory10101101276:20190419193117p:plain
そして...
f:id:covory10101101276:20190419193331p:plain
Debug.log()メソッドでゴリラの衝突を確認
成功しました。今回はここまでにしておきます。