インベントリ / DEMO (宝箱)

今回は宝箱からアイテムを受け取るデモを見ていきます。
初めに使用する無料アセットを3つインポートします。
まずはスタンダードアセットです。
公式ページに進み「Add to My Assets」をクリックしてください。サインインが求められた場合はそれに従ってください。その後「Open in Unity」ボタンをクリックしてダウンロードしてください。

UnityでPackage Managerを開きStandardAssetをインポートします。
(※必要な物は一部ですが、念のため全てインポートします)


「Assets/StandardAssets/Utility/SimpleActivatorMenu」の(恐らく)11行目のGUITextUnityEngine.UI.Textに変更して下さい。



次はUnity-Chanをインポートします。StandardAssetの時と同様、公式ページからインポートしてください。



最後に宝箱のモデルです。公式ページから 下の図のように一部を選んで インポートしてください。


次に用いる地形をダウンロードします。以下のボタンからプレハブをダウンロードしてください

そしてこのファイルをUnityのプロジェクト上の好きな場所にD&D(インポート)します。


いまインポートした「FlMr_Terrain_Prefabs/」フォルダの「Terrain」プレハブをシーン上にD&Dしてください。


さらに「unity-chan!/Unity-chan! Model/Prefabs/」フォルダの「unitychan_dynamic」プレハブもシーンに追加します。
私の画面では、位置を (x, y, z) = (40, 6.4, 55)に指定しています。


次にUnity-Chanのコンポーネントの調節です。まず初めにTagを「Player」に変更します。そして「IdleChanger」「FaceUpdate」コンポーネントを非アクティブ(チェックを外す)にしてください。そして「ThirdPersonUserControl」を追加します。そして勝手についてきた「ThirdPersonUserCharacter」コンポーネントの「GroundCheckDistance」を0.3に変更します。次に「Animator」コンポーネントのControlerを「ThirdPersonAnimatorController」に変更してください。


最後に当たり判定のサイズを調節します。次の画像のようにUnity-Chanにピッタリのサイズに変更してください。



次にカメラです。シーン上のカメラオブジェクトに「SmoothFollow」コンポーネントを追加し、Targetをシーン上の「unitychan_dynamic/mesh_root/」の子オブジェクトの「hair_front」を指定します。さらにDistanceを5、Heightを2、HeightDampingを5に変更します。


ここで動作確認をしてください。WASD又は矢印キーで操作できるようになったと思います。カメラの向きを変更することはできませんが、脱線が過ぎることを考えてここでは解説しません。


次に宝箱です。「FS Loot Boxes/Assets/Prefabs/」フォルダ内のプレハブをシーンに追加してください。次に追加したオブジェクトのコンポーネントを次の図のように変更します。


さらに、RigidBodyコンポーネントのIs Kinematicにチェックを入れます。


次にスクリプトをいじります。「FS Loot Boxes/Assets/Scripts/」フォルダのLootBoxスクリプトを、次のコードで置き換えます。
LootBox.cs
1// © 2019 Flying Saci Game Studio
2// written by Sylker Teles
3
4using System;
5using UnityEngine;
6using FlMr_Inventory;
7
8/// <summary>
9/// These are the ways you can open the box.
10/// </summary>
11public enum OpeningMethods { OpenOnCollision, OpenOnKeyPress, OpenOnTouch }
12
13/// <summary>
14/// Loot Box class.
15/// </summary>
16public class LootBox : MonoBehaviour, ISerializationCallbackReceiver
17{
18    [SerializeField] private string lootId;
19
20    /// <summary>
21    /// The loot GameObject prefab.
22    /// </summary>
23    [SerializeField] private  ItemBase boxContents;
24
25
26    public void OnBeforeSerialize()
27    {
28        if(string.IsNullOrEmpty(lootId))
29        {
30            lootId = Guid.NewGuid().ToString();
31        }
32    }
33
34    public void OnAfterDeserialize()
35    {
36        if (string.IsNullOrEmpty(lootId))
37        {
38            lootId = Guid.NewGuid().ToString();
39        }
40    }
41
42
43    /// <summary>
44    /// How should the player open the box?
45    /// </summary>
46    public OpeningMethods openingMethod;
47
48    /// <summary>
49    /// You can use a tag for the player that can open the box.
50    /// </summary>
51    public string playerTag = "Player";
52
53    /// <summary>
54    /// You can also use a key to open the box. Player must be close.
55    /// </summary>
56    public KeyCode keyCode = KeyCode.Space;
57
58    /// <summary>
59    /// Activates the leaping animation.
60    /// </summary>
61    public bool bouncingBox = true;
62
63    /// <summary>
64    /// Close the box after player goes away
65    /// </summary>
66    public bool closeOnExit;
67
68    /// <summary>
69    /// Flags when player is close enough to open the box.
70    /// </summary>
71    private bool isPlayerAround;
72
73    /// <summary>
74    /// Flags when the box is open avoiding opening it twice.
75    /// </summary>
76    /// <value><c>true</c> if is open; otherwise, <c>false</c>.</value>
77    public bool isOpen { get; set; }
78
79    /// <summary>
80    /// The box animator for leaping, open and close animations.
81    /// </summary>
82    Animator animator;
83
84    /// <summary>
85    /// You can call OnBoxOpen and OnBoxClose as events, for
86    /// instance, you can get what's inside the box.
87    /// </summary>
88    protected virtual void OnBoxOpen()
89    {
90        if (boxContents == null) return;
91        var bag = GameObject.FindGameObjectWithTag("playerBag").GetComponent<ItemBag>();
92        bag.AddItem(boxContents.UniqueId, 1);
93    }
94
95    // Start is called before the first frame update
96    void Start()
97    {
98        // gets the animator
99        animator = GetComponent<Animator>();
100
101        // set the animation to bounce or not
102        BounceBox(bouncingBox);
103    }
104
105    /// <summary>
106    /// Update this instance.
107    /// </summary>
108    private void Update()
109    {
110        // when player is close enough to open the box
111        if (isPlayerAround)
112        {
113            // in case of Key Press method for opening the box,
114            // waits for a key to be pressed
115            if (Input.GetKey(keyCode)) Open();
116        }
117    }
118
119    /// <summary>
120    /// Bounces the box.
121    /// </summary>
122    /// <param name="bounceIt">If set to <c>true</c> bounce it.</param>
123    public void BounceBox (bool bounceIt)
124    {
125        // flag the animator property "bounce" accordingly
126        if (animator) animator.SetBool("bounce", bounceIt);
127    }
128
129    /// <summary>
130    /// Open the box.
131    /// </summary>
132    public void Open ()
133    {
134        // avoid opening when it's already open
135        if (isOpen) return;
136        isOpen = true;
137
138        // play the open animation
139        if (animator) animator.Play("Open");
140
141        // calls the OnBoxOpen event and deliver the
142        // earned GameObjects on temp list
143        OnBoxOpen();
144    }
145
146    /// <summary>
147    /// Close the box
148    /// </summary>
149    public void Close()
150    {
151        // avoid closing when it's already open
152        if (!isOpen) return;
153        isOpen = false;
154
155        // play the close animation
156        if (animator) animator.Play("Close");
157    }
158
159    /// <summary>
160    /// When player touches or click on the treasure box
161    /// </summary>
162    private void OnMouseDown()
163    {
164        // checks if the opening method is OpenOnTouch
165        if (openingMethod != OpeningMethods.OpenOnTouch) return;
166
167        // Open the box.
168        Open();
169    }
170
171    /// <summary>
172    /// When something hits our treasure box.
173    /// </summary>
174    /// <param name="collision">Collision.</param>
175    private void OnCollisionEnter(Collision collision)
176    {
177        // OnCollisionMethod is not for OpenOnTouch method
178        if (openingMethod == OpeningMethods.OpenOnTouch) return;
179
180        // check if the hitting object is our player
181        if (collision.gameObject.tag == playerTag)
182        {
183            // if the method is OpenOnKeyPress, let's just flag the player as close
184            if (openingMethod == OpeningMethods.OpenOnKeyPress) isPlayerAround = true;
185
186            // otherwise, open the box.
187            else Open();
188        }
189    }
190
191    /// <summary>
192    /// When player goes away from box.
193    /// </summary>
194    /// <param name="collision">Collision.</param>
195    private void OnCollisionExit(Collision collision)
196    {
197        // flag the player as away.
198        isPlayerAround = false;
199
200        // if the box is suppose to close on exit, close it
201        if (closeOnExit) Close();
202    }
203}
204
今回はソースコードの説明は省きますが、基本的に変更した箇所は 88-93 行目です。このメソッドは宝箱が開かれた際に呼ばれます。boxContentsは宝箱で得られるアイテムです。これがnullの場合は何も起きません。nullでは無い場合は「playerBag」タグが付いたアイテムバッグを探し、アイテムを追加します。

最後に、シーン上の「Canvas/ItemBag」オブジェクトのタグを変更します。まずは新しく「playerBag」タグを追加してください。


そして「Canvas/ItemBag」オブジェクトのタグを「playerBag」に変更します。


実行して宝箱に近づいてください。自動的に宝箱が開くことが確認できると思います。


そして宝箱にコンポーネントの「Box Contents」に今まで作成してきたアイテムを追加してください。


シーン上のInventoryTestを非アクティブにしたあと、再度宝箱に近づいてみて下さい。ゲーム開始時の持ち物は空ですが、宝箱を開けるとアイテムが一つ獲得できるはずです。