前回は、画面配置と羊オブジェクトを作成しました。
前回の記事↓
今回は羊から毛を刈り取った時に出る羊毛(Wool)の作成をやっていきます。
羊毛(wool)オブジェクトの作成
次に羊毛(wool)オブジェクトを作成していきます。
Assets/Images/ から 羊毛用の画像 wool をSceneに直接ドラッグアンドドロップします。
woolオブジェクトが作成されますので、Inspectorで、
- Sprite Renderer の Order in Layer を 10(この数字が大きいほど手前に表示される)
にしておきます。
また、新規スクリプトもAddComponentします。 スクリプト名は「Wool」にしましょう。
スクリプトの中身はまだ何も変更しなくてOKです。
次にwoolオブジェクトの動きは物理演算に任せたいので
- Rigidbody 2D
- Circle Collider 2D
をそれぞれ追加します。
この時、ふわふわ感を出すため、若干画像が重なってもいいようにしたいです。
そのため、Circle Collider 2D の Radius はちょっと小さく(0.4くらい?)にしておいてください。
実行してみて、woolオブジェクトが重力に引かれて下に自然落下するようになっていればOKです。
このwoolオブジェクトも画面に大量に作るので、Hierarchyからwoolオブジェクトを Assets/Prefabs にドラッグアンドドロップしてprefab化しておきましょう。
受け皿の作成
このままでは下に落ちて行ってしまうだけなので、刈り取った羊毛を受け取る籠を用意します。
Assets/Images から bucket(籠なら本当はbasketですよね・・・。適当で申し訳ない)をSceneにドラッグアンドロップしてbucketオブジェクトを作成し、
- Transform Scale Y を 2
- 位置をゲーム画面(背景画像)の一番下に配置
- Sprite Renderer の Order in Layer を 100
このように設定します。
この時のbucketオブジェクトの Transform Position Y は大体 -1.2 ぐらいになると思いますので、Inspectorで直接 Position Y に -1.2 と入れても構いません。
そして、このbucketオブジェクトでwoolオブジェクトを受け取るためには、こちらにも当たり判定が必要です。
Polygon Collider 2D をAddComponent します。
すると上記画像のように、Sceneで当たり判定が緑色の線で表示されると思います。
これは、画像の透明じゃない所を自動で計算して当たり判定を作ってくれているのですが、見て分かる通り若干大きめに取られてしまっています。
加えて、籠の縁に羊毛が乗るわけではなく、籠の中に溜まっていくように見せたいので、むしろ当たり判定は画像よりも小さくする必要があります。
そのため、Inspector の Polygon Collider 2D の中の Edit Collider ボタンを押し、Sceneビューの当たり判定の直線と直線のつなぎ目に頂点があるので(マウスを近づけると■が表示されます)それをドラッグして、以下のように調整します。
調整が終わったら、再生してwoolオブジェクトがbucketオブジェクトに乗るか確かめてみましょう。
それっぽくなってきましたね!
sheepからwoolの生成
さて、本来は羊(sheep)を刈る事で羊毛(wool)が取得できるわけですから、sheepを刈る処理→Shaving()
で、woolが生成(Instantiate)されるようにSheepスクリプトを修正します。
Sheepスクリプトをダブルクリックして、編集していきましょう。
まず、生成(Instantiate)する、woolのPrefabをInspectorからセットしたいので
1 2 |
[SerializeField] private Wool woolPrefab; |
をメンバ変数に追加します。
次に、刈り取り(Shaving()) の時にwoolを生成(Instantiate)するので、Shavingメソッドに以下の1行を追加します。
1 |
var wool = Instantiate(woolPrefab, transform.position, transform.rotation); |
Sheepスクリプト全体は以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Sheep : MonoBehaviour { [SerializeField] private SpriteRenderer sheepRenderer; [SerializeField] private Sprite cutSheepSprite; [SerializeField] private Wool woolPrefab; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } private void Shaving() { sheepRenderer.sprite = cutSheepSprite; var wool = Instantiate(woolPrefab, transform.position, transform.rotation); } private void OnMouseOver() { if (Input.GetMouseButton(0) == false) return; Shaving(); } } |
スクリプトを忘れずに保存し、UnityEditorに戻ります。
sheepオブジェクトのInspectorで 先ほど作った変数 Wool Prefab
がセット出来るようになっていますので、Assets/Prefabs の woolをセットします
それでは、再生してsheepオブジェクトからwoolが生成されるか確かめてみましょう。
出てますがーー・・・、最初一列に並んでしまっていてちょっと格好悪いですね?
その他にも色々と目につきます。
Woolスクリプトの修正
課題を挙げていきましょう
- 全てsheepオブジェクトの位置から生成されているので、位置がそろいすぎている。
- ポンッと上にばらけて飛ぶ感じにしたい
- サイズをばらけさせたい
- 籠から漏れたwoolオブジェクトがそのままずっと下に落ち続けてしまっている。
これらを修正するために、既に追加だけしてある Woolスクリプトを以下のように修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Wool : MonoBehaviour { [SerializeField] private Rigidbody2D _rigidbody2D; // Start is called before the first frame update void Start() { _rigidbody2D.AddForce(Quaternion.Euler(0, 0, Random.Range(-15.0f, 15.0f)) * Vector2.up * 4, ForceMode2D.Impulse); transform.localScale = Vector3.one * Random.Range(0.4f, 1.5f); } // Update is called once per frame void Update() { if (transform.position.y < -5) { Destroy(gameObject); } } } |
ポンッと上にばらけて飛ぶ感じにしたい
この対応が以下の部分になります。
7 8 9 10 11 12 13 |
[SerializeField] private Rigidbody2D _rigidbody2D; // Start is called before the first frame update void Start() { _rigidbody2D.AddForce(Quaternion.Euler(0, 0, Random.Range(-15.0f, 15.0f)) * Vector2.up * 4, ForceMode2D.Impulse); |
まず2D物理演算を司る Rigidbody2D
を Inspector でセット出来るよう [SerializeField]
属性(Attribute)で 変数 _rigidbody2D
を宣言したあと、
Start関数でRigidbody2D
のAddForce
という関数を呼んでいます。
このAddForceは
AddForce(Vector2で力を加える方向・強さを指定 , 加える力の種類 );
というRigidbody2Dに力を加える関数です。
まず、加える力の種類ですが、今回は瞬時に起こる力を意味する ForceMode2D.Impulse を指定しています。
次に力を加える方向ですが、基本的には上に最初は飛んで欲しいので
Vector2.up * 4という上方向を示すVector2に最初から用意された定数を4倍したものを使っています。(内部的には new Vector2(0,4)
と書くのと同じです)
ただ、これだとただ真上に飛んでしまうため、このベクトルをちょっと回転させたいです。
Unityでは回転の表現にはQuaternion(クォータニオン)というものを使います。(Transform の rotation なども Quaternion ですし、Instantiate する時に、位置と回転を指定しますが、その時も Quaternion を指定していますね)
この Quaternion というものは数学分野でも中々難しい項目になります。が、別に中身を理解する必要はありません。 「回転を表す便利なもの」ぐらいの理解で大丈夫です。
そして、便利な関数が最初から沢山用意されています(沢山すぎて把握しきれないぐらいあります)
その中の一つ、回転をX軸回転、Y軸回転、Z軸回転 それぞれ0~360度で指定するための関数がここで使われている Quaternion.Euler(オイラー)になります。
Euler(X軸回転,Y軸回転,Z軸回転 );
このX軸回転やZ軸回転 の「軸回転」と言われてもピンとこない方は、UnityのSceneでよく表示されるこのXが赤、Yが緑、Zが青の矢印を思い出してください。
この、矢印を指でつまんで転がすイメージです。 その時つまんだ矢印(軸)に対する回転が「軸回転」です。
今回は上方向 Vector2.up を時計回りまたは逆時計回りにちょっと(±15度の範囲で)回転させたい=Z軸回転になるので
Quaternion.Euler(0,0, Random.Range(-15.0f,15.0f) )
と指定しています(Random.Range(From,To)で、FromからToの間でランダムな値が返却されます)
そして、回転後のベクトルは
回転後のベクトル = Quaternion * ベクトル
で得られるので
Quaternion.Euler(0, 0, Random.Range(-15.0f, 15.0f)) * Vector2.up * 4
としています。(注:ベクトル * Quaternion ではエラーになってしまうので気を付けてください)
サイズをばらけさせたい
14 |
transform.localScale = Vector3.one * Random.Range(0.4f, 1.5f); |
ここは先ほども使った Random.Range関数を使ってlocalScale を 0.4f~1.5f のランダムな値でセットする処理になります。
この時、Vector3.one という定数を使っていますね。これは new Vector3(1,1,1) を表すVector3に最初から用意されている定数で、 0.4f~1.5f のランダムな値 をかけています。
new Vector3(Random.Range(0.4f,1.5f),Random.Range(0.4f,1.5f),1);
で良いのではないか?
と思う方もいるかもしれませんが、これですとVector3のxに指定するランダムな値とyに指定するランダムな値がそれぞれ異なってしまうので、円が楕円になってしまいます。
もちろん
1 2 |
var rnd = Random.Range(0.4f,1.5f); transform.localScale = new Vector3(rnd,rnd,1); |
このように一度変数にランダムの値を取得して、使用するようにすればよいですが、それを1行で(ほぼ)同じ意味にするために
1 |
transform.localScale = Vector3.one * Random.Range(0.4f, 1.5f); |
という書き方をしているというわけです。
(注:2Dのゲームなので、Zの値は何が入っていても構いません)
籠から漏れたwoolオブジェクトがそのままずっと下に落ち続けてしまっている。
ある程度下にまで落ちてしまったwoolオブジェクトは消してしまうようにします。
おさらいと次回予告
今回は羊をクリックすることで羊毛オブジェクトが大量に出るようになりました。
次回は、この羊毛の売却処理とお金の取得処理をやっていきます。
次回の記事↓
コメント
【sheepからwoolの生成】の欄で、woolのPrefabを取得する方法として
[SerializeField]
private Wool woolPrefab;
と書いていますが、型がWoolで上手くいくのが不思議です。
これだとPrefabにアタッチされているWoolコンポーネントを指しているように見えるからです。
生成するのはPrefab自体なので、正しくはGameObjectだと思っていたのですが、間違っていますか?
また、PrefabやSpriteはSerializeFieldではなく、Resourcesを使って取得するのはナンセンスでしょうか?
woolPrefabの型がWoolで上手くいくのは、WoolクラスがMonoBehaviourを継承している場合に限ります。この場合、Woolはプレハブにアタッチされたコンポーネントであり、UnityのInstantiateメソッドはそのコンポーネントを含むゲームオブジェクトを生成します。private GameObject woolPrefab;といった形でプレハブを取得する方がたしかに一般的かもしれません。
Resourcesを使って動的にロードする方法もありますが、Resourcesを頻繁に使用することは推奨されません。理由は以下の通りです:
パフォーマンス: Resources.Loadは実行時にリソースをロードするため、頻繁に呼び出すとパフォーマンスに悪影響を与える可能性があります。
メモリ管理: Resources.Loadでロードしたリソースは明示的にアンロードしない限りメモリに残り続けます。
SerializeFieldを使用することで、インスペクター上で簡単にリファレンスを設定でき、ロード時のパフォーマンスの問題を避けることができます。特別な理由がない限りはPrefabやSpriteの参照はSerializeFieldで設定するのが一般的であり、推奨される方法です。
下記エラーが表示されます。
Assets\Scenes\Scripts\Wool.cs(15,46): error CS0104: ‘Random’ is an ambiguous reference between ‘UnityEngine.Random’ and ‘System.Random’
こちらのエラーはSystem.RandomとUnityEngine.Randomのどっちを使うかエディタ側がわからなくなっていることがエラーの原因です。
恐らくですがusing System.Collections; using System.Collections.Generic;を使うところでusing System;を追加してまとめてしまっていませんか?using Systemを使うとこのエラーが出ます。
using systemでまとめてネームスペース表記する場合はusing Random = UnityEngine.Randomを代わりに追記しておく必要があります。