Roll a Ball を改造していく
改造Roll a Ballを第8・9・10回の課題(中間課題2)としてWebclassに提出する
ステージをクリアしたら次の面に進むように修正する
シーン遷移のコードを追加
シーン管理と登録
一定時間後にプログラムを実行するコールバック関数の利用
を以下で扱う
作業内容:
コピー元のシーン MiniGame をProjectのAssetsから探す
シーンをコピー
Assets の Scenes フォルダから
MiniGame
を選択してメニューの Edit → Duplicate または、Ctrl+D で複製してMiniGame1 を作成する
MiniGame1を編集する
例)
PickUpやWallの数や位置を変更
Materialを新規作成してGroundやWallの色を変更
同様の手順でMiniGame2を作成する
ビルド設定の修正
File -> Build Profiles から MiniGame と MiniGame1と MiniGame2 をプロジェクトに追加する
Scene List または Open Scene List を押す
Add Open Scene ボタン
または
Projectビューから追加シーンをドラッグ&ドロップ
シーンの順番を以下の様に並べ替える。
シーン編集をMiniGameに切り替える
PlayerController.csを修正
MiniGame をプレイして動作を確認する
【不具合発生】 クリアメッセージ「You Win!」が表示されない(一瞬画面に表示されて消える)
【Unity独自の手法で解決】
クリア条件達成時に一定時間の経過を待ってからシーン遷移するようにコードを修正する
Unityに用意されている遅延実行メソッド Invokeを利用する
Invokeは引数に遅延実行するメソッド名を文字列で指定する
Invoke("Load", 5.0f); // メソッドLoadを5秒後に実行
シーン読み込みのコードを別のメソッドを新規に宣言して移動する
void Load () {
int sceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(sceneIndex + 1);
}
【一般的な手法で解決】
クリア条件達成時に一定時間の経過を待ってからシーン遷移するようにコードを修正する
Unityの処理を止めないように時間の経過待ちをするためにコルーチンで非同期処理する
ステップ(1/2)
コードの先頭の部分に1行追加する。
using System;
SetCountText() に StartCoroutineでコルーチンを実行するコードを追加する。
// シーンを切り替えるコードを次のものに変更
StartCoroutine(DelayMethod(5, () =>
{
SceneManager.LoadScene(sceneIndex + 1);
}));
※ () =>{ } の表記法について: ラムダ式 あるいは クロージャと呼ばれるコードの中にコードを埋め込む表記法。
参照: https://qiita.com/rawr/items/11790e9ea08a29d028a4
※ using Systemで System.Randomが有効になって UnityEngine.Randomと名前が競合してエラーになる場合は、コード中のRandom をUnityEngine.Randomに修正する。
ステップ(2/2)
新しいメソッドとして DelayMethod()を追加する。
DelayMethodはコードを実行するまでの待ち時間 waitTime と登録するコード action を引数として受け取り、受け取ったコードを指定した時間経過後に実行するメソッド。
yield returnに付いてはこちらの記事を参照。 https://www.fenet.jp/dotnet/column/language/1612/
C#のforeach構文などでジェネレータとして利用できるようにする構文。
private IEnumerator DelayMethod(float waitTime, Action action)
{
yield return new WaitForSeconds(waitTime);
action();
}
※StartCoroutine や WaitForSeconds について:
https://docs.unity3d.com/ja/2018.4/ScriptReference/WaitForSeconds.html
・Unityのステータスバーのエラーメッセージを確認する。
C#スクリプトのファイル名と行を把握してスクリプトのエラーを修正
・ゲームが異常な挙動をする。エラーが起きないこともある。
C#スクリプトを適切なゲームオブジェクトにアタッチしているか確認
C#スクリプトを右クリック→ Find References In Scene でアタッチ先のゲームオブジェクトを検索
C#スクリプトが重複してアタッチされていないか確認
・GameObjectにアタッチしたスクリプトを削除すると警告が表示される。
警告を消すには、Hierarchyの ゲームオブジェクトのコンポーネントをインスペクターで確認して、黄色い警告の!マークが付いているScriptコンポーネントをメニューからremoveして削除する。
・エラーが発生するタイミングを確認する。
TriggerやCollisionなどのイベント発生時点でエラーが発生するなら対応するメソッドのコードを確認
実行時の変数の値をコンソールで調べる。
Debug.Log("動作確認のメッセージ");
Debug.Log(実行時の値を観察したい変数);
各シーンごとにPick Upの数が異なる場合、ステージクリアの判定用コードを書き換えて対応する必要になる。
各シーンにPlayerController 1 2 3...のように異なるScriptを用意してクリア判定の数を変えることでも対応できるが、メンテナンス性が悪い。
(問題1)ステージのアイテム数を変えると、コードも修正する必要がある
(問題2)コードに機能追加や修正が生じると全シーンのPlayerControllerも修正せねばならない
PlayerControllerを改良してこのような問題に対処する
ステージのクリア条件のアイテム数を設定するメンバーを追加。
public int item_count;
クリア条件のコードを修正する。
if (count >= item_count)
各シーンの Player ゲームオブジェクトにアタッチした C# スクリプト
PlayerController の item_count スロットの数値
をステージのクリア条件に応じて修正する。
各ステージの
PlayerのSpeed を修正
してみてもよい(ステージが進むにつれ早くなるなど)
ここまでのPlayerController.cs のコード修正状況
ステージのクリア条件
if (count >= item_count)
を満たした状態でステージ上にまだPickupが残っている場合、次のステージが非同期処理により5秒後に読み込まれるまでの間に残りのPickupを拾うたびにステージのクリア処理が重複して実行される。
そのためトータルスコアの記録やLoadSceneが無駄に重複して実行されてしまう。
このバグには別のページで対応する。
フリーなサウンドファイルを検索して、ダウンロードする。ダウンロード後にプロジェクトにインポートする
例)
UnityのAsset Store の Cartoon Game Sound
AssetStoreを利用しなくてもネットでゲーム用効果音のフリー素材を検索してダウンロードして利用してよい
例)
THE MATCH-MAKERS 最終更新10/22/2008
Bluezone レポートフォルダにデモSEをDL済み
演習ではBluezone社のデモSEファイルをサイズでソートして短め(小さめ)のものから2つ選ぶ
アイテム取得音 壁衝突音 にふさわしいものを各自で選択する
Unityのプロジェクトに Sounds フォルダーを作成する
ダウンロードしたファイルをUnityのProjectのSoundsフォルダーにドラッグ&ドロップしてインポートする
(ヒント)SEフォルダのファイルをサイズで並べ替えて小さい物から探すとよい
他のインポート方法としてドラッグ&ドロップせずに Projectウインドウのフォルダを右クリックして Import New Asset からファイルを選んでもよい
参照:
【Unity】Audioの基本と扱いについて 少し古い記事。BGMとSEの再生方法の差異について記載あり。BGMのスムーズな切り替え方法に関するコード例あり
UnityでサイドチェーンしてSEとBGMの自動バランス調整する話
効果音をUnityで再生してみる
N101情報実習室のモニターの音量を調整する
※ボリュームに注意※ 効果音のボリュームは未調整なので大音量。PCのボリュームを下げて様子をみるとよい
モニターのタッチボタンでメニュー表示
タッチボタンで音量調整
Unityのオーディオミキサー Audio Mixer を利用したサウンドの確認方法:
メニュー Window → Audio → Audio Mixer を開く。
+ ボタンから Mixerを1つ追加する。
このパネルでサウンドの出力レベルを確認することができる。
※ この後の作業: Pick Upに追加するオーディオクリップ(AudioClip)の出力先としてこのAudio Mixerを指定する必要がある
ゲームオブジェクトPickUpに効果音を設定する。
ステップ(1/4)
PickUp(要注意:PickUp Parentではない)にコンポーネントを追加
Audio -> Audio Source
ステップ(2/4)
Audio Sourceの
Play on Awake を off
※ここで on にしたまま Prefab を Overrides → Apply All すると全てのPickUpからシーンが開始した瞬間にSEが再生されるので音量に注意が必要。
ステップ(3/4)
AudioClip にサウンドファイルを設定する。
ステップ(4/4)
Output(出力先)にオーディオミキサー(Master)を設定する。
InspectorのPrefabメニューから Overrides -> Apply all でプレファブに設定を反映する。
Rotator.csを修正してItem取得時に効果音(SE)を再生するコードを追加する。
Playerゲームオブジェクトに Player タグを設定する。
現状の確認: シーンを再生して動作を確認する。 この段階では音はならない。
参考:https://qiita.com/amano-kiyoyuki/items/43fa92cce1a44a8030b5
コライダーとトリガーの判定の対応関係を確認する https://docs.unity3d.com/ja/2018.3/Manual/CollidersOverview.html
実験用コード: OnCollisionEnter は反応しない。このコードはWallとPlayerの衝突判定で利用するとよい。
// コライダーとの接触時に呼ばれるコールバック
void OnCollisionEnter(Collision hit)
{
// 接触対象はPlayerタグですか?
if (hit.gameObject.CompareTag("Player"))
{
audioSource = gameObject.GetComponent<AudioSource>();
audioSource.Play();
Debug.Log("bbb");
}
}
音が再生されない問題への対処:
PlayerController の コードを修正する。PickUpでTriggerした際に、PickUpを無効化するコードをコメントアウトする。
//other.gameObject.SetActive (false);
この状態では、SEが再生されるがPickUpは消えずに何度も通過できる。
以上から、Play()でオーディオクリップが再生されるタイミングで既にPickUpが無効化されているのでオーディオクリップも無効となり再生されないことが判明した。
PlayerController に以下を追加する。(↑でコメントアウトした行の下に続けて)
other.gameObject.GetComponent<Renderer>().enabled = false;
other.gameObject.GetComponent<BoxCollider>().enabled = false;
↑のコードの1行目はPickUpを非表示(レンダリングの対象としない)にするコード。
2行目は衝突判定(Trigger含む)の対象から外すコード。
応用のヒント
レンダラーだけをdisable(スイッチオフ)にしたゲームオブジェクト = 透明な通過判定用エリア
PlayerController.cs
補足
クリア条件達成と同様に遅延処理でother.gameObject.SetActive(false)する方法もある
UnityのInvokeで遅延実行するメソッドには引数は渡せないので衝突したアイテムを渡せない。
Colliderを引数に持つメソッドを宣言してコルーチンで呼び出して対応すればよい。
遅延時間は効果音の再生時間に合わせる。その際にotherのAudioSourceコンポーネントからAudioClipの再生時間を取得するとスマート。
アイテムが非アクティブになるまでの時間内に再度衝突するとOnTriggerEnterが重複して呼び出されることに注意する。対処するコードを書いてもよいだろう。
アイテムのカウント処理だけをPlayerControllerで行い、効果音の再生やアイテム非表示の処理をRotator側に移動してもよいだろう。
壁(Wall)の衝突で効果音を出す設定を行う
NorthWall に衝突音のファイル (AudioClip)をDnDで追加
AudioSourceコンポーネントのPlay on Awake を off
C#スクリプト WallBump を作成する
HierarchyのNorthwallにDnDで追加する(または Add ComponentからScriptを追加)
WallBumpのコードはRotatorとは以下の点が異なる
Startメソッドでシーンの開始時にAudioSourceを初期化
OnCollisionEnter で衝突判定
東西南北の壁はTriggerオブジェクトではないためCollision系のメソッドで処理する必要がある
考察:
音を出すのはどこか?
PickUp や Wall から音が出ていると考えれば、AudioSource(音の発生源)はアイテムや壁ということになる。各ゲームオブジェクトにAudioSourceを持たせ個別の効果音を設定することになる。
この場合、各ゲームオブジェクトのライフサイクル(有効期間)に注意を払ってコードを作成する必要がある。
Playerから音が出ていると考えれば、AudioSourceをPlayerに追加することになる。この場合、Unityのコンポーネントでは再生するAudioClipが1つに限定されるのでC#スクリプトを追加して複数のAudioClipを登録してコードで切り替える(衝突対象を判定して)必要がある。
https://www.sejuku.net/blog/83535
この手法を採用する場合、シーンに新しいAudioClipを持つゲームオブジェクトを追加するたびにPlayerControllerの修正も必要となる。多数のAudioClipを管理する際に注意が必要となる。
音を聞くのは何か?
Main Camera の コンポーネント Audio Listener が担当している。
課題の提出は一通りRoll a Ball を修正して各受講生のオリジナル作品のゲームとなってからWebclassに提出する予定。
今回はAssetメニューからバックアップとしてUnityPackageをExportしておくに留める。