No.002 進捗状況15[ゼルダっぽいタイトル画面]
みなさんこんにちは🌚
GWは魔の1日バイト9連勤を果たしたブルーノです。今年のGWは天気もそんなによくなかったので遊びにはどっちみち行けなかったから良いんですけどね😠😭
では早速作っていきましょう🌻
今回はタイトルにもある通りゼルダっぽいゲームのタイトル画面を実装してみたいと思います。
ちなみにこんな感じ
www.youtube.com
特徴としては実際のマップを読み込んでおり、その上にタイトルメニューがあることですね。この形式の良いところは最初にマップデータを読み込むのでそこからゲームシーンに移ればマップのオブジェクトを隠したり一度消したりしなくて良いところだと思います。私からしてもこれは簡単だと思い、今回つくろうと思いました。
タイトルのUIの実装
タイトルとゲームスタートのボタンを作りたいと思います。例によってかめくめさんのサイトに良い記事があったのでこれを真似させていただきます。
gametukurikata.com
地味にこのサイトに貼ってある無料日本語フォントサイトのリンクが嬉しかったです。AssetStore有料だし日本語フォント少ないし🌝
こんな感じにしました!!タイトルはどーしよーかなー
そしてGameStart()メソッドを持ったTitleSystemスクリプトを空のゲームオブジェクトにアタッチし、StartButtonに指定しますただこれだけでは暗転するアニメーションだけでシーンが変わったりする機能は持っていません。そこで今度はシーン遷移の機能も実装します。今回のゼルダっぽいシーン遷移の方法の利点である先に建物等のロードを済ませるという作業は次のシーンにロードした建物たちを引き継ぐことでそのメリットを活かせます。ということで今のTitleスクリプトにその作業も追加しなければなりません。そこで、またかめくめ氏のこのページを参考にしました。
gametukurikata.com
データを残す方法としてシングルトンという方法を昔少し使ったのですが、いまいち覚えてなかったのでまた勉強です。はい、というわけで10行目およびAwakeメソッドの中身がシングルトンの実装になります。
10行目で自身のスクリプトをstaticな形で宣言し、共用で使うオブジェクトを12行目のShareObjectに格納しておきます。
最初にテストシーンから起動すると10行目のシングルトンインスタンスには何も入っていないのでこのスクリプトが割り当てられ、25行目にあるように決められたオブジェクトたちをDontDestroyOnLoad()メソッドで壊されなくします。
この状態でこのスクリプトをもつテストシーンから同じくこのスクリプトを持ったオブジェクトがあるゲームシーンに遷移すると、staticな変数にもうインスタンスが割り当てられているということで23行目にあるように新しくAwake()を通ったゲームシーンの方のスクリプトは格納されている同じオブジェクトたちを削除し始めるんですね。おかげで各シーンに同じオブジェクトがあっても共存することなく、かつどのシーンからプレイしても同じ状況でプレイできるということですね。
要はこの唯一無二のインスタンスを保持するためのデザインパターンがシングルトンというようですね。
個人的に一つ気になったのは、ゲームシーンには一応全てのマップ用のオブジェクトが配置してあるわけですがAwake()メソッドを通る前に結局同じオブジェクトたちを一度シーンに作り出すのではないか?ということです。Awake()以前ならレンダリングもされていないから負荷はかからないのでしょうか。ここだけ少し疑問が残りました。
タイトル画面で動く背景
では最後に、タイトル画面のカメラワークを作りたいと思います。「時のオカリナ」から続く3Dゼルダはみんなタイトル画面で実際のマップを使っているのが特徴ですよね。時にRTAやTASでそれが悪用されることもありますが...
カメラワークの作り方はこちらのサイトを参考にしました。
sirohood.exp.jp
動画付きでわかりやすい!!シロフードさんありがとう!!😽
こうして...画像なのでわかりにくいですが、ちゃんとタイトルメニューの後ろでカメラが町中を飛んでいます!!成功です!
今回はここまでにしておきます。、。、
No.002 進捗状況14[人に話しかける挙動]
みなさんこんにちは🌚
さて今回は以前作ったメッセージスクリプトと組み合わせてゲームのような話しかける処理を実装していこうと思います。しかしこの話しかける処理を考えてみたのですがやることが
まず
・会話可能範囲内で対象にカーソルを表示 ←この時複数のキャラが被った場合の処理も考える
そして
・カメラを会話用の位置に移動
・メッセージウィンドウを表示
・キャラ同士を向かいあわせる
と、意外に多いのでとりあえずまだわからないカメラの移動をおいといて他の機能をどうにか実装しようと思います。
カーソルの実装
話しかけることのできるキャラクターをわかりやすくするためにカーソルをまず設けました。
カーソルという空のオブジェクトの子にそれぞれ上部の逆三角錐と下部の円を置いています。地味にこの逆三角錐は初めてのProBuilderで製作したものだったりします。
unity3d.com
最近標準搭載された3Dモデリングツールです。とても簡単!
そしてこれにMessageManagerスクリプトと、対象のオブジェクトに話の内容と位置情報を格納したActiveMessageスクリプトを作成
ActiveMessageスクリプトは以前作成したメッセージウィンドウのものをちょっといじっただけですがMessageManagerスクリプトは一から書いたためスパゲッティになっています笑笑
わかりにくいので解説するとMessageManagerスクリプトでその名の通りActiveMessageスクリプトを持つオブジェクトを一括管理し、一番プレイヤーに近い会話オブジェクトと同じ座標にカーソルが常にいます。そしてカーソルオブジェクトのトリガー範囲内に入った時だけカーソルが可視化&話すことができるというわけです。製作過程でキャラの範囲が被った場合はどうしようか悩みましたがOnTriggerEnterとExitで実装すると被っている場合でもすんなり処理してくれたのでラッキーでした。そもそも実際のゲームプレイではNPCは離して配置すれば良いだけですしね。
キャラ同士を向かいあわせる
この動きがなかなか大変でした。
数学なんてここ最近ちっともやっていないので一応実装に成功した今でもベクトルだとかQuaternionだとかの理解がちゃんとできていないと思いまする。どうしてもお互い向き切った後の角度の参照の仕方がわからなかったので時間で管理しました。一応2秒あればどんな角度でも向けるはずなので問題ないです。実行すると...なんとかできました!!
ずいぶん時間が空いた&リアルが忙しかったですがなんとかまた一つ実装に成功しました。次回以降はいよいよゲームシステムの大枠に移っていきたいと思います。
No.002 進捗状況13[ジャンプする箱]
みなさんこんにちは🌚
では今回は現在進行中のプロジェクトの最後(の予定)のオブジェクトを作っていきたいと思います。次回以降はゲームの中身的なものを作っていきます。
ジャンプする箱
速く動いてやりたいスタイリッシュなことというわけで考えた結果、落ちる崖やガレキの上を飛んでわたるというアクションを実装することにしました。イメージとしては下の感じまあ加速とは関係ないですがイメージということで
まずはいつも通りBoxを用意
目の前には飛び乗るためのジャンプパネルを配置
さらに飛んで渡るためにボックスを連続して配置し、その上にジャンプパネルこの長いボックスはプレハブ化しておきます。そしてジャンプパネルはもうあるのでこのボックスの動きを制御するスクリプトを作成
重力の適用を遅くするという挙動はなかなか実装方法が思い浮かばなかったので代わりにこの箱のみRigidbodyではなくAddforceによる重力の実装をし、この値を変えることで重力の変化を表現しました。しかし絶対にもっと良い方法があるはずなので少し不満が残りますね。まあさっさと作るために今回は仕方ないです。また今度勉強しましょう。さあ動きを確認すると...
だいぶイメージっぽい感じにできました!!無駄に3つのアングルから撮ってみました。
でもジャンプパネルに乗った時の挙動が一回ごとにやけに前に飛んだり逆に飛ばなかったり安定しないんですよね。一番健闘できた動きがこれなので今回はこれまでにしておきますがまだまだ改善の余地はありそうです。
No.002 進捗状況12[RPG的なメッセージウィンドウ]
みなさんこんにちは🌚
今回はオブジェクトではなく、RPGゲームでよくある会話ウィンドウを実装したいと思います。といっても、例によってこちらのサイトの方法を実践するだけなので、こちらをみてもらった方がとてもわかりやすいです。かめくめちゃんさんには毎回お世話になってます😊
gametukurikata.com
まずは最初にある通りキャンバスに色々配置。
MobileJoyStickもキャンバスなんだけどいいのか。いや、とりあえずやってみよう。操作とメッセージは同時に表示しないからあとで切り替えれば大丈夫だろう
次にメッセージスクリプト作成。勉強のため中身を謄写することを試みましたがUpdate()メソッドの中で迷子になったためそこだけコピペしたのは内緒。
これを全部手で写しましたなどと言えばコメントアウトまで綺麗に写したのかと褒められるか見破られるかの2択でしょう。
Updateメソッドの途中でこんがらがってしまったのですが用意した変数や他のメソッドの中身とサイトの記述から大体の仕組みは理解できたと思います。特に最後のSetMessagePanelはこのスクリプト内では呼び出されないものの他のスクリプトから呼び出す際に便利なよう設計されているのは勉強になりました。普通のことかもしれませんが私の中にはなかった概念です。
そしてこれを呼び出すゲームオブジェクトを作ります。
空のゲームオブジェクトに次のスクリプトをアタッチし、今回はmキーで会話を呼び出してみます。実行してみると...いやすごいですね。ですが今回作りたいゲームは縦画面なのでUIの構成とかを調整していく必要がありそうです。では今回はここまでにしておきます。
No.002 進捗状況11[NavMesh]
みなさんこんにちは🌚
一口サイズに材料を切るのが苦手なブルーノです。
では今回も新しいオブジェクトを作っていきたいと思います。
ところで最近Youtubeで仮面ライダーカブトの動画を見たんですが、これも今私が作っているゲームと同じく時間を圧縮した中で敵との戦いを繰り広げるといった内容で、とてもかっこいいんですね。というか、このゲームの元ネタはぶっちゃけこの仮面ライダーカブトです。そして仮面ライダーには敵がつきもので、このカブトの敵も超高速で動くため常人には捉えられないんですね。というわけで、今回作るオブジェクトは「超高速で動き回る敵」にしたいと思います。そして動かす敵は、街を作った際にノリでストアから手に入れたこの人にしようと思います。
にしてもなんでこのゴリラおしゃぶりしてるんだろう。では頑張っていきます。
NavMesh
昔Unityの勉強の記事で少しだけ触れたNavMesh、自動で歩ける道を判別しその上をAIが勝手に動いてくれるという代物を今回は使いたいと思います。こちらのサイトを参考にしました。というか、まんまこのサイト通りです。毎回お世話になっています。kanさん。
kan-kikuchi.hatenablog.com
まずはテスト用のフィールドと、ゴリラを配置。
Window→AI→Navigationを開き、Objectタブから歩かせたいエリアに該当するオブジェクトを選択し、NavigationStaticにチェック。
NavigationAreaは[Walkable]を選択しておきます。
準備ができたらBakeしましょう。そしたら今度は、ゴリラにNavMeshAgentというコンポーネントをつける。これだけ。なんて簡単なんだ。
動きを速くするため、SteeringのSpeed, AngulerSpeed, Accelerationを、アホみたいに速くしてみました。これらはそれぞれマリカでいうスピード、曲がりやすさ、加速ですね。
あ、さきほどこれだけと言いましたが、最後にスクリプトから目的地を設定してあげる必要がありました。嘘ついてごめんなさい。
しかし目的地の設定も、NavMeshAgent.destinationにVector3型の座標を指定するだけ。そこで今回は、目的地をこんなオブジェクトにしてみました。
コライダー だけのCubeを用意し、時間経過もしくはゴリラが触れることで特定の座標を移動する目的地です。
これでゴリラが常にある目的地を目指して走り出すはず.....とおもったんですが......
ゴリラが恥ずかしいことになったまま走り回ってるんですが.....原因を調べてみると、どうやらこのアセットにもともとついていたAnimationが、NavMeshと競合してしまっているようです。
NavMesh Agent を他のコンポーネントと共に使う - Unity マニュアル
NavMeshAgentは独自のPhysicsとAnimationコンポーネントを持っているらしく、そのまま一緒に使うと競合を起こしてしまい正常に機能しないようです。ちなみに競合というのは複数のプログラムなんかが同じデータを使おうとして競り合ってしまうことです。まあゴリラがブルブルするアニメーションは今回いらないのでアニメーションは今回は捨てることにしました。Animationコンポーネントを外すと...
直りました。が、今度はまた別の問題が。ゴリラが速すぎてプレイヤーの当たり判定が貫通してしまいました。
どうやらこれは物理演算の関係上速い動き全てを計算すると負荷がかかってしまうのでコマ送りになってしまっているそうです。というのも全てこちらのサイトに書いてあったんですよね笑
【Unity】物理演算で動かすオブジェクトが壁を貫通する問題と対策 - テラシュールブログ
というわけでゴリラにRigidbodyコンポーネントをつけてCollision Detectionを[Continuos Dynamic]にします。ちなみにIs KinematicにチェックをつけないとNavMeshAgentと競合を起こします。
そして...成功しました。今回はここまでにしておきます。
No.002 進捗状況10[オブジェクトプーリング][ビルボード]
みなさんこんにちは🌚
ステファノス・チチパスってなんか口に出したくなっちゃうよね。ブルーノです。
では今回もオブジェクトを製作していきます。
今回作ろうと考えたのはズバリ「雷」。雷を避けるなんてかっこいいですよね。というわけでこれを作成していこうと思います。
雷ってぐにゃぐにゃしてるから3Dモデルを用意したらその表現がとても難しいしめんどくさそうですよね。というわけで今回はまずこの雷の表現に「ビルボード」という技法を使いたいと思います。
ビルボード
ちょっと昔のゲームをやっていると時々かなりグラフィックが写実的なのにどこからみてもその面しか見えないオブジェクトとかありますよね?これはビルボードという技術で2Dの見た目のものを常にカメラの方を向かせることで立体感を出す技法で、3Dモデルの節約ができます。
nn-hokuson.hatenablog.com
こちらの方がとても詳しく解説しています。というかこれ見て作りました🌝
まずは背景を透過させた雷の画像をインポート
背景の透過仕方は
mtimsno.hatenablog.jp
こちらに詳しく載っています。
そして新しく作ったQuadオブジェクトに適用。名前はLightningにしときました。見た目はペラペラですがコライダーは普通にCapsule Colliderを使っているので当たり判定はあります。
そしてビルボードのスクリプトを作り、Lightningにアタッチ。
常にカメラの方を向くようになりました。良かった良かった。
しかしこの雷、実際に光を発しているわけではないので日陰だととても暗くなってしまうんですよね。
オブジェクトへの光の当たり具合や見た目の影響を管理するのはマテリアルのエミッション(Emission)が関係しているようなのですがこちらのサイトを参考にしてみたもののUnityのバージョンが違うためかメニューが変わっていて記事通りにいかなかったです。
[Unity]色を失わないオブジェクト | KENスクールブログ
しかたないのでオブジェクトの目に前に光源を置くという強硬手段に。そして下に降る動きと地面の下に入ったら消えるようスクリプトで制御します。ちゃんとタイマーで遅くできるようpublicなスピードの値も導入。ただ落ちるだけだと物足りない気もしたので34行目で地面に落ちた時に光っぽいパーティクルも発生させました。
しかし、これが後述するオブジェクトプーリングというあらたな壁に大きく関わってくることになるのです。
オブジェクトプーリング
さあさっき作ったLightningを生成するスクリプトを作ってたくさん雷を降らせるぞーと思い、こちらのジェネレータースクリプトを作ったところ....
お、重い。。。。
全くゲームプレイができないわけではないですが結構な頻度で画面が角つきました。パソコンでこれならスマホはどうなってしまうんだ....
どうやら先ほどスクリプト内で頻出していたInstantiate()メソッドが原因で、新たにオブジェクトを生成する作業は負荷が大きいそうです。でもこの量でもこれだけカクつくのにシューティングゲームとかはどうやってあれだけの弾を動かしているのでしょうか。
そこでオブジェクトプーリングという技法の出番というわけです。
【Unity】オブジェクトプーリングでオブジェクト節約術 | Unity | DoRuby
こちらのサイトを参考にしました。どうやらオブジェクトをゲーム開始時にたくさん作っておき、そのアクティブ/非アクティブを切り替え使い回す方法のようです。なるほど。
では実際にスクリプトを書いていきます。var型とかforeachとか自分では全然使わないから理解が大変だ😵😵😵
オブジェクトプールのスクリプトはリストの生成と取り出し周りだけなので全く同じ記述で良いですね。
こちらの雷生成スクリプトは大幅な変更が余儀なくされましたが、おかげでオブジェクトプールの仕組みと使い方がよくわかりました。使用する際はGetObject()メソッドのみなので楽ですね!
一応雷のスクリプトもオブジェクトプールの仕様により用済みになったら非アクティブにするように変更。このスクリプトの22行目で世界時間をかけているので加速に伴って雷のスピードが1/10になるのは当然なのですが雷の生成も遅くなるように前の雷生成スクリプトの51行目でも工夫してあります。
結果は....
成功!!
雷の中を加速して避けるのはなかなか面白いですよ。さあゲーム内容が面白くなってきた!!今回はここまでにしておきます。
No.002 進捗状況09[ワープするチェックポイント]
ペルー/クスコ -レインボーマウンテン-
みなさんこんにちは🌚
隻狼というゲームが気になっているもののゲームしている暇もお金もなくて動画をみてしまいたい欲と葛藤するブルーノです。ゲーム動画って100%ネタバレだから見るかどうかすごく悩みます。
では前回に続き今回は通過したら次の地点まで移動して現れるタイプのチェックポイントの作成に移りたいと思います。
前回のものとの違いは
- 触れてもオブジェクトは消えない
- 出現するための座標・角度を複数格納できる
- 触れると次の指定された座標に再び現れる(最終地点の場合は消える)
といったところでしょうか
このチェックポイントは前回のCheckPoint02の中身を改良してその結果を分岐させようと思います。
長くなってしまいましたがようやくできた...!!
本当に時間がかかってしまいました。この記事の投稿が遅れた原因の8割はこのスクリプトで詰まっていたからといっても過言ではないです。気分転換すると突然解決策が浮かぶことってありますよね。
当初の予定通り、次に現れる位置と向きを格納する配列を2つ作り、コライダーがプレイヤーに触れると次の予定された座標に移動、回転し、もし次の座標がなければクリアフラグを立てる。といった風になりました。
52行目のMoveCheckPoint()関数はアニメーションから呼び出しています。今回はこのアニメーションの途中で関数を呼び出せることを学ぶことができました。結果は。。回転もうまく反映させてることがわかるようにイーサンに対して違う向きを向いている写真を撮ってみました。イーサンも感動のあまり立ち尽くしてますね。
試行錯誤の途中はアニメーションが変なことになったりスクリプトが無駄に3枚に増えたり大変でした。こうして一つのスクリプトに収められたのはとても良かったです。
オブジェクト一つ作るのにこれだけ時間がかかってしまうのはとてもまずいですね。もっと簡単な形にしてペースを上げていこうと思います。