- 投稿日:2020-10-09T22:42:45+09:00
【Unity】Fungusでゲーム制作(5) uGUIとの連携でアイテム欄を作る
Fungusの基本的な使い方に関しては第1回の記事で解説しておりますので、まだの方はそちらからお読み下さい。
前回、コレクションを利用し、所持しているアイテムをリストとして管理できるようになりました。次はuGUIと連携するためのコマンドを使い、アイテム欄のUIを作ります。
このページでは画面下端にアイテムを入れるボタンを並べ、クリックするとアイテムの説明文が出る、というものを作ることにします。ボタンを配置する
まずはガワから用意してみましょう。Unity全体のメニューから「GameObject」→「UI」→「Button」を選んでuGUI製のボタンを作ります。
ここでHierarchyビューに注目です。もしすでにステージ(第1回参照)を作っているなら、その中のCanvasというものの下にボタンができると思います。
uGUIではCanvasという下敷きが必ず用意され、その上にボタンなどの部品を置いていくことでUIを作る仕組みになっています。これらは初期状態ではカメラをどこに動かしても同じ場所に表示されるように設定されているため、UIとして使うことができるのです。そしてすでにCanvasがある場合、uGUIの部品を追加するとそこに勝手に入れてくれるようになっています。しかし、ステージ用のCanvasにボタンを置くとどうも動かないようです(原因についてはまだ調べています……)ので、独自にCanvasを作ってそこへボタンを配置することにしましょう。「GameObject」→「UI」→「Canvas」で作れます。
独自のCanvasを作り、7つほどボタンを並べました。ボタンにはデフォルトで文章(Text)がついてきます。これをアイテム名の表記に使います。
(ここで、それぞれのボタンのTextの名前が同じになっているはずなので、変えておいた方がよいです。後で混乱のもとになるので……)ボタンの表記を変更する
続いて、アイテムを拾ったり手放したりした時に各ボタンの表記を変更します。先ほど、アイテムをコレクションの形で管理することにしましたから、例えば下図のようにボタンにも順に0からの番号を振って、アイテムのコレクションと1対1で対応させる……とすれば上手くいきそうですね。
(「ボタンもコレクション化する」わけではないことに注意して下さい。あくまで便宜上番号を振るというだけです)
さっそく処理を作ってみましょう。新しいブロックを用意し、前回説明したElementコマンドでアイテムコレクション0番目のアイテム名を取り出してみてください。
次はその名前を0番目のボタンにあてがいます。FungusからuGUIのテキストを書き換えるにはUI/Set Textコマンドを使います。
Target Text Object欄に変更したいテキストをあてがい、Text欄にその内容を書きます。今回はボタンの中のTextをTarget Text Objectに指定し、Text欄には先ほどコレクションから取り出したアイテム名を入れます。例の変数を文章に埋め込む表記でもいいのですが、「<Value>」と書かれているところをクリックすると変数のメニューが表示され、選択した変数の中身をそのまま渡せますので、そちらを利用するのもよいでしょう。
コマンド一覧は以下のようになると思います。前回解説したように、そもそもない番号を指定してしまった時のエラーを防ぐ機構も入っています。
同様にして、コレクションの1番のアイテム名を1番のボタンに入れる処理、2番のアイテム名を2番のボタンに入れる処理……を作っていきましょう。この時、エクスプローラ(MacならFinder)でやるのと同様にShiftキー+クリックでコマンドをまとめて選択し、その状態でコピペもできるので活用して下さい。さて、この一連の処理はアイテム欄に変化がある度に呼ばれないといけません。アイテムを入手したり捨てたりする度にCallでこのブロックを呼ぶようにもできますが、それだと多分どこかで呼ぶのを忘れてしまうので、いっそ常に呼ばれ続ける状態にしてしまいましょう。BlockのExecute On Event欄をMonoBehaviour/Updateに指定すれば、毎フレームこのブロックが呼ばれるようになります。
終わったらゲームを起動し、動作を確認してみましょう。
ボタンを押した時にイベントを起こす
最後に、アイテムが入っているボタンを押すと説明文が表示されるようにしてみましょう。
まずはやはりガワからです。Unityのメニューの「GameObject」→「UI」→「Panel」で説明文の土台になるパネルを作ります。
しかしこのままだとゲームを開始していきなりパネルが表示されてしまいますので、初めは隠しておくために、パネルを選択してInspectorビューの左上隅あたりにあるチェックを外しておきます(非アクティブ化)。
続いてパネルの中に「GameObject」→「UI」→「Text」で説明文になるテキストと、「GameObject」→「UI」→「Button」でパネルを閉じるためのボタンも作っておきます。
中身作りに進みます。フローチャートで新しいブロックを作って条件をUI/Button Clickedにします。これは指定されたボタンが押された時に起動する条件で、選択すると出てくるTarget Button欄にそのボタンを指定します。ここではとりあえず0番目のボタンを指定します。条件も決まったのでコマンドの組み込みに移ります。まずは先ほど非アクティブ化したパネルをもとに戻し、再び画面に表示させましょう。UI/Set Activeコマンドを使います。
Target Game Object欄にアクティブかどうかを変更したいもの(UIに限らずGameObjectなら全てOK)を指定し、Active State欄でアクティブにするか非アクティブにするかを決めます。ここではチェックを入れておきましょう。次はパネルの中の説明文を書き換えます。アイテムコレクションの中からボタンと対応する番号のものをElementコマンドで取り出して、その種類に応じてIfコマンドで分岐させ、Set Textコマンドでパネルのテキストに説明文を書き込む……というのでいけるでしょう。
ですがアイテムのボタンはいくつもあり、全てが同じ処理をする必要があるので、上記の処理をそれぞれのボタンのブロックに対してコピペするのでは効率が悪いです(例えば一つアイテムを追加するとなったら全部のブロックを直さないといけませんね)。そこで、アイテムの種類に応じた説明文を書き込むブロックを別途作り、各ボタンからはそれをCallコマンドで呼び出すだけという形にしたいと思います(下図)。
Callコマンドで呼び出すだけ、と書きましたが、実際はボタン側でCallする前にアイテムの種類を変数に入れておく必要があり、Callされた説明文書き込みブロックの側でその変数を参照することになります(Callする際になにかデータを渡すということはできないのでそうするしかないようです)。以下のような感じですね。
上記のようにElementコマンドでボタンに割り振った番号のアイテムをコレクションから取ってくるか、あるいは今回ボタンのTextにアイテム名を指定しているのでそれを使うのもアリでしょう。UI/Get TextコマンドでTextに指定した文章を取得できます。
Target Text Object欄で対象のTextを、String Variable欄で取得した文章を入れる変数を指定します。いずれにせよボタンの方のブロックはこれでOKですので、今度は説明文を書き込むブロックを作ります。
説明文ブロックではボタンの方でアイテム名を入れた変数(上記のコマンド一覧ではTempStringというのに入れています)に応じて分岐させ、アイテムに応じた文章をSet Textコマンドで説明文パネルのテキストに書き込む、という形で組んでみます。
今回は分岐が何通りにも及ぶのでElse Ifコマンドを使うと良いです。これは名の通りElseとIfが一体になったコマンドで、Ifで指定した条件に当てはまらない時にさらに条件をかませて分岐させるということができます。使い方はIfコマンドに準じます。
実際に組むと以下のようになると思います。
最後に説明文のパネルを閉じるボタンに機能をもたせましょう。新しいブロックを作り、UI/Button Clicked条件を指定してTarget Button欄にパネルを閉じるボタンをあてがいます。Set Activeコマンドを追加して説明文のパネルを非アクティブ化するようにします。
長くなりましたがこれでようやくアイテム欄も完成です。さっそく試してみましょう。
前回はコレクション、今回はuGUIとの連携と、2回にわたってアイテム欄の実装に必要な処理について解説してきました。
今回、特にプログラミングに触れた経験がある方は回りくどいことをしている気が……と思われたかと思います(特にアイテムコレクションの中身をボタンに反映する所とか)。このような時により効率のいいやり方をするには自らプログラムを書き、(現状の)Fungusのコマンドにない処理を追加していく必要があります。それでも全くゼロからゲームを作るよりは簡単なので気負いすぎることはありませんし、今はとりあえず先にゲームを完成させることを目指した方がよいでしょう(既存のコマンドだけで作っているうちに「こんな機能があったらいいのに」というのが出てくるはずなので、それを実装することを目標にプログラミングを学べばよいと思います)。次回はプレイ時間が長いゲームを作る時のために、セーブ・ロード機能について解説する予定です。
- 投稿日:2020-10-09T21:45:16+09:00
Unityを1ヶ月間勉強後、アプリを一つ製作したのでまとめ
これは何
ゲーム開発がしたくて、Unityの勉強を一ヶ月間行った後、ゲームを一つ製作した。
iPhoneとAndroidの両方にbuildし、実機で遊べるようにした。
その中で詰まった点や学んだことについてまとめたもの。何で勉強したか?
・ドットインストール
・Unityの教科書2019学んだこと
- Scriptファイルに元から記述されているメソッド二つの呼び出しタイミング
- Start(): 起動時に一度呼ばれる
- Update(): ワンフレーム毎に呼ばれる
- publicをつけて変数を宣言すると、Unity上で触れるようになる
- Physics(Rigidbody, Collider)を使った衝突判定の実装方法
- 時間を遅らせてメソッドを呼ぶ方法2つ
- invoke
- Coroutine
詰まったところ
- フリックした際に、フリック方向を上下左右の四方向で取得する方法
- onCollisionEnter内でinvokeを呼び出して、particleを発火させる方法
- 複数ある子オブジェクトを取得する方法
- AddForce()で横方向にPlayerを動かしたいのに、上下にブレる
- Playerが氷の上を滑っているような動きの実装
- iPhoneにbuildしようとした所、2つTheredが表示されできなかった
ここから学んだ事の各詳細
Scriptファイルに元から記述されているメソッド二つの呼び出しタイミング
- Start()メソッド: 起動時に一度呼ばれる
- Update(): ワンフレーム毎に呼ばれる
C#のscriptファイル製作時↓
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewBehaviourScript : MonoBehaviour { // Start is called before the first frame update void Start() //起動時に一度呼ばれる { } // Update is called once per frame void Update() // ワンフレーム毎に呼ばれる { } }publicをつけて変数を宣言すると、Unity上で触れるようになる
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewBehaviourScript : MonoBehaviour { public GameObject Clear; //GameObject型のClear変数が宣言される }上記のように記述すると、下記スクショ赤枠のように、Clearという箇所が新しく表示される。
右側の枠にGameObjectをドラッグアンドドロップすると、選択したオブジェクトをscript上で簡単に使えるようになる。Clearという名称のゲームオブジェクトを、上記の赤枠内右側にドラッグアンドドロップした場合、下記のように記述するとClearのRigidbodyを簡単に使える。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewBehaviourScript : MonoBehaviour { public GameObject Clear; //GameObject型のClear変数が宣言される // Start is called before the first frame update void Start() { Clear.GetComponent<Rigidbody>(); } }Physics(Rigidbody, Collider)を使った物理判定(今回は衝突判定)の実装方法
Physicsとは?
Unityに標準で付属している物理エンジン。
Rigidbodyコンポーネントと、Colliderコンポーネントの二つからなる。今後ゲーム製作する上で滅茶苦茶よく使うと思う。Rigidbodyを使うと?
ゲームオブジェクトを物理特性によって制御する事ができるようになる。
詳しくはこちら→ Rigidbody -Unity マニュアルColliderを使うと?
物体の当たり判定の実装が出来る。
Playerと壁がある場合、下記のようにどちらかにrigidbodyをアタッチし、両方にcolliderをアタッチする必要がある。
Player: rigidbody + colllider
壁: colliderPhysicsを使っている場合、onCollisionEnter()メソッドを使うことによって、衝突した時に処理を走らせる事が出来る。
onCollisionEnterやonCollisionExit等、一通りの衝突を検知するメソッドが見れる→ 【Unity】当たり判定を一通り!OnTrigger・OnCollisionをひとまとめ | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト
時間を遅らせてメソッドを呼ぶ方法2つ
1. invoke
書き方
Invoke("呼び出すメソッド名", 秒数);例: 宝箱のゲームオブジェクトに加えたscriptに記述しているのを想定。
void OnCollisionEnter(Collision collision) { if (collision.gameObject.tag == "Player") { // もし、衝突したゲームオブジェクトにPlayerというタグがついていた場合 Invoke("open", 1.0f); // openメソッドを一秒後に読み込む } } // 宝箱が開いたときの音 void open(){ audSourse.PlayOneShot(openSE); //openSEという名称のaudioファイルを一度鳴らす処理 }Invokeの詳しい内容はこちら→
【Unity】Invokeの使い方!実行タイミングを自在に操ろう | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト※豆知識: ちなみにInvokeは日本語に訳すと呼び出すという意味らしい。
2. Coroutine
書き方
// 呼び出す時 StartCoroutine("ClearGame"); //コルーチン関数を定義 private IEnumerator ClearGame(){ yield return new WaitForSeconds(秒数); SceneManager.LoadScene(0); //走らせたい処理 }例: 宝箱のゲームオブジェクトに加えたscriptに記述しているのを想定。
void OnCollisionEnter(Collision collision) { if (collision.gameObject.tag == "Player") { StartCoroutine("ClearGame"); //ClearGameというコルーチン関数を呼ぶ } } private IEnumerator ClearGame() //ClearGameという名称のコルーチン関数を定義 { yield return new WaitForSeconds(4.0f);//四秒後に以下に書いた処理が走る SceneManager.LoadScene(0); //0番のゲームシーンを読み込む }コルーチンについての詳しい内容はこちら→ Unityのコルーチンの使い方をまとめてみた - WonderPlanet Developers’ Blog
詰まった所の、解決策や学んだことについてはこれから記載
ここから詰まった所の各詳細
フリックした際に、フリック方向を上下左右の四方向で取得する方法
3Dのゲームを製作したのですが、右斜め上や、左斜め下などの斜め方向のスワイプを上下左右のどれかに分類して取得するのにかなり詰まりました。
悩んみに悩み、ぐぐり倒して下記の記事に辿り着きかなり参考にさせていただきました。参考にさせていただいた記事↓
【Unity】フリックとスワイプ入力の同時取得方法【C#】onCollisionEnter内でinvokeを呼び出して、particleを発火させる方法
playerを操作して、宝箱に当たったらゲームクリアという内容で、宝箱に当たったら二秒後に紙吹雪のパーティクルが発生するように処理を書いていました。
何が原因だったのか?
particleにアタッチされているscriptに、下記のコードが記述されている事が原因だった。
ゲームスタートと同時に、三秒後に自分自身を破棄するメソッドが走っていた。[SerializeField] private float timer = 3f; private void Start() { Destroy(gameObject, timer); }このスクリプトのtimerの値を1000に変更することによって解決。
ハマった理由
たまに宝箱に当たると、ちゃんと二秒後にパーティクルが発火する事があり、それにより原因の追求に紆余曲折時間がかかった。
悩んだ流れ
たまにパーティクル発火する事に気づき、発火する条件探しが始まる
↓
Clear(ゲームオブジェクト)
∟ clearParticle(パーティクルのアタッチされたゲームオブジェクト)
∟ clearParticle(パーティクルのアタッチされたゲームオブジェクト)パーティクルは上記のようなヒエラルキーの構成にしており、clearParticleをforeachで取得する事によりパーティクルを発火させていた。foreachの既述の仕方がおかしいのかと思い探る。
Start()メソッドの中に既述するちゃんとパーティクルが発火したため、取得するためのコードに問題がない事を確認する。
↓
壁に一回当たった後だとパーティクルが発火し、二回当たるとパーティクルが発火しないという事に気づく。壁に当たった際にisKinematicをtrueにし、その後falseにする処理を書いていたのでそれが原因かと思い、playerのisKinematicのステータスをDebug.Logで確認しながらゲームをテストしたが問題なさそう。
ここでまず数時間溶けていた。
その後、壁に一回当たった後でもパーティクルが発火しない事がある事に気づく。
↓
壁に当たった回数ではなく、時間ではないか?と思い当たる。
5秒位経ってから宝箱に当たるとパーティクルが発火しない事に気づく。
ここら辺で色々いじっていたら新しくエラーログが出る。
ログの内容は、DestroyされているGameObjectを呼び出そうとしているよという内容だった。そこから、clearParticleオブジェクトor親オブジェクトのClearがDestroyされていると確認する。
しかしどこでパーティクルがDestroyされているのか分からず時間が溶けていく。
色々探った結果、clearParticleにscriptがアタッチされているのに気づき、中を見るとビンゴだった。複数ある子オブジェクトを取得する方法
Clear(ゲームオブジェクト)
∟ clearParticle(パーティクルのアタッチされたゲームオブジェクト)
∟ clearParticle(パーティクルのアタッチされたゲームオブジェクト )上記のようなヒエラルキーの構成で、clearParticleを取得したい場合
foreach文を使う
// Clear配下の、clearParticleにアタッチされたパーティクルを発火させたい場合のコード↓ Transform parentTransform = Clear.transform; foreach (Transform child in parentTransform) { child.GetComponent<ParticleSystem>().Play(); }AddForce()で横方向にPlayerを動かしたいのに、上下にブレる
これから記述
Playerが氷の上を滑っているような動きの実装
これから記述
iPhoneにbuildしようとした所、2つTheredが表示されできなかった
これから記述
- 投稿日:2020-10-09T19:03:04+09:00
点群データをUnityのVFX Graphで使用する
点群データをUnityのVFX Graphから使用できるようにしてみます。
Pcxのインポート
keijiro / Pcxというパッケージを使用するので、READMEに従ってUnityにインポートします。
点群データの作成
今回は静岡県ポイントクラウドデータベースで提供されている点群データを使用させて頂きました。Pcxはbinary形式のplyファイルしか使用できないので、CloudCompareなどのソフトウェアでファイルフォーマットを変換します。その際に、必要であれば点群のマージや間引き、スケールの変更などもしておきます。
点群データのインポート
Unityに作成したplyファイルをインポートします。インポートすると最初はContainer TypeがMeshになっているので、Textureに変更してApplyボタンを押します。そうすると、VFX Graphで使用できる点群の位置と色を書き込んだテクスチャがそれぞれPosition Map、Color Mapという名前で生成されます。
VFX Graphでの表示
作成されたテクスチャを使用して単純にすべての点を表示するVFX Graphは以下のようになります。Single Burstで点群データに含まれる点の数だけパーティクルを作成して、Set Position from MapとSet Color from Mapで先ほど作成したテクスチャから位置と色をそれぞれ割り当てるようにしています。
結果、以下のように点群データをUnityで表示することができました (浜松城です)。
【出典:静岡県ポイントクラウドデータベース】、CCライセンス 表示 4.0 国際
- 投稿日:2020-10-09T09:46:54+09:00
Unity 3Dオブジェクトが伸びて表示される
Unity 3Dオブジェクトが伸びて表示される
Sceneの表示では普通に表示されている3Dオブジェクトが実際にプログラムを動かした時の画面、つまりカメラから見た時の表示では妙に伸びた感じなってしまい、これは一体どういうことかと悩んだ。
解決策
一体どういうことかと悩んでいじっているうちにカメラの視野角(Field of View)に関係があることがわかった。色々触っている中で視野角を大きくしすぎてしまったらしい。人間の視野は大体120度くらいやからそのくらいで良いだろうと思ったのか。
普通になった。よかったよかった。
なお視野角を狭くしただけだと対象が視野に入らなくなってしまうので、カメラを後ろに移動する必要もあります。環境
Unity 2020.1.6f1
- 投稿日:2020-10-09T09:46:33+09:00
Unity テクスチャの繰り返し
Unity テクスチャの繰り返し
マテリアルにテクスチャを繰り返し表示するようにしたかったが、Tilingの値をいくらいじっても1枚分の画像しか表示されず、それ以外の部分は妙に引き伸ばされた画像が表示されるだけだった。
2×2で表示させようとしたが、右上4分の1以外は妙なことになっている。解決方法
ネットで色々調べてもこの現象についての情報が見つからず、一体どういうことかと思って色々いじっているうちにわかった。マテリアルではなくテクスチャ側の設定だった。
Wrap ModeがClampになっていたせい。
デフォルトでClampになってしまうようだが、ネットで見つかる情報ではここを変更する必要があることが書いてないものが多い。
継ぎ目が綺麗に行かないなという場合はMirrorにしてやれば反転した画像を貼り付けてくれるので、綺麗につながる。反転しても良いテクスチャの場合しか使えないが。
環境
Unity 2020.1.6f1
- 投稿日:2020-10-09T01:26:13+09:00
【Unity】他のゲームオブジェクトの子オブジェクトをスクリプトで操作する方法
今回はゲームオブジェクトから他のゲームオブジェクトの子オブジェクトの色を変えてみる。
最初に空のゲームオブジェクトを作る。
キューブを作り、さらにその下にキューブをつける。
名前は「cube1」とした。スクリプトを書く。
今回はインスペクターで指定したオブジェクトの子オブジェクトのRendererにアクセスする。using System.Collections; using System.Collections.Generic; using UnityEngine; public class TestScript : MonoBehaviour { public GameObject obj; Renderer renderercolor; void Start() { renderercolor = obj.transform.Find("cube1").gameObject.GetComponent<Renderer>(); } void Update() { if (Input.GetMouseButtonDown(0)) { renderercolor.material.color = new Color(1f, 0.0f, 0.0f, 0.0f); } } }renderercolor = obj.transform.Find("cube1").gameObject.GetComponent<Renderer>();ここの部分でpublicで指定したゲームオブジェクトの子オブジェクトを見つけてRendererをGetComponentし、変数に入れる。Find("")のカッコの中にはアクセスする子オブジェクトの名前を入れる。
できたら空のゲームオブジェクトにつける。
再生し、クリックすると赤色になる。
キューブにマテリアルをつけるのを忘れずに!
- 投稿日:2020-10-09T00:31:59+09:00
【Unity】インスペクターで指定したオーディオをマウスクリックで再生する方法(自分用)
using System.Collections; using System.Collections.Generic; using UnityEngine; public class TestScript : MonoBehaviour { AudioSource audioSource; public AudioClip sound; void Start() { audioSource = GetComponent<AudioSource>(); } void Update() { if (Input.GetMouseButtonDown(0)) { audioSource.PlayOneShot(sound); } } }AudioSource型の変数とオーディオのデータを格納する変数をpublicで作る。
その後Getcomponentし、左クリック処理の中にaudioSource.PlayOneShot(sound);を入れれば左クリックをしたらインスペクターで指定したオーディオが起動する。
以上。