Inventory Pro 裝備拾取的實現


以后都按照插件使用,提出問題,回答問題的方式來進行總結和學習

效果圖

1、運行相關的例子,場景中出現4個矩形,這4個矩形是用來模擬物品掉落的包裹,移動Player靠近物品

i4

2、使用鼠標點擊物品正方體,點擊I鍵打開包裹,這里看到3個掉落包裹正方體已經點沒有了,相應的背包里多出三個蘋果,至此例子演示完畢

i5

插件使用

使用Inventory Pro進行裝備的拾取,有很簡單的例子

1、點擊菜單Tool,InventorySystem,Main editor 打開自定義Editor

i6

2、在Main manager對話框中點擊Items editor選項卡,選中Item editor 點擊Create Item 綠色按鈕,會彈出左側的選擇子對話框,其中列出了可以創建的Item種類,第一項就是消耗品,當然還有很多別的東西,這里先試驗最簡單的消耗品

i8

3、點擊創建消耗品以后會出來一個Item的詳情界面,這里需要添加Item的Name,Icon,Behavier里有類型,冷卻時間,價格,是否可以疊放,是否可以丟棄等等屬性吧,同時Editor會創建出相應的物品prefab,直接拖拽到場景中即可

 i9

問題

1、地上的包裹(Item容器)是如何產生的?

2、地上的包裹是如何對應Item model的,且之間的關系是什么?

3、拾取的過程是怎么樣的?

答案

1、地上的包裹(Item容器)是如何產生的?

A1、這里例子里面是通過Unity3d Editor擴展進行添加地上的包裹的且是Cude,其中使用了動態生成預設的技術,這里涉及到很多關於Editor擴展的知識,基本與本章主題無關,Inventory Pro的作者也只是通過例子進行了展示(我想如果是實際游戲可能需要動態生成,比如生長點,怪物掉落什么的),這里列出一個動態生成Prefab的核心代碼,也就是類似選擇生成消耗品后點擊的事件的處理函數,代碼如下:

        protected override void CreateNewItem()
        {
            var picker = EditorWindow.GetWindow<InventoryItemTypePicker>(true);
            picker.Show(InventoryEditorUtil.selectedDatabase);

            picker.OnPickObject += (type) =>
            {
                string prefabPath = EditorPrefs.GetString("InventorySystem_ItemPrefabPath") + "/item_" + System.DateTime.Now.ToFileTimeUtc() + "_PFB.prefab";

                var obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                var prefab = PrefabUtility.CreatePrefab(prefabPath, obj);

                AssetDatabase.SetLabels(prefab, new string[] { "InventoryItemPrefab" });

                var comp = (InventoryItemBase)prefab.AddComponent(type);
                comp.ID = crudList.Count == 0 ? 0 : crudList[crudList.Count - 1].ID + 1;
                Object.DestroyImmediate(obj);

                AddItem(comp, true);
            };
        }

2、地方的包裹是如何對應Item model的,且之間的關系是什么?

我們看到1中的代碼創建好剛體,碰撞后的正方體后

var comp = (InventoryItemBase)prefab.AddComponent(type);

通過AddComponent添加了用戶選擇的Type,通過類型轉換(InventoryItemBase)我們知道這個類型是Item類型。

然后我們再通過Unity3d 可視化環境來驗證一下

i1

通過選中預設我們看到其綁定的相關插件有碰撞和剛體屬性,最后綁定的腳本是Consumable Inventory Item (Script),這樣也驗證了上面代碼加入的Type,也就是在類選擇所對應的InventoryItemBase 類型

i2

這里需要注意的是Object Triggerer Item腳本是如何綁定到對象上的,這塊是一個小技巧,我找了半天才發現的

/// <summary>
/// The base item of all the inventory items, contains some default behaviour for items, which can (almost) all be overriden.
/// </summary>
[RequireComponent(typeof(ObjectTriggererItem))]
public partial class InventoryItemBase : MonoBehaviour

媽蛋的終於有解決上帝之手的方法了,用RequireComponent這個特性就可以不用手動進行相關插件的綁定了。

i3

上圖是Consumable Inventory Item(script)的public field詳情了,是不是似曾相識,就是和Item Editor工具創建Prefab的時候是一樣的。

到此第二個問題回答完畢。

3、拾取的過程是怎么樣的?

在正式回答前,我先yy下,既然拾取必須是雙向,而且應該是觸發的(當點擊鼠標觸發),然后Play接收到觸發的調用進行拾取,最后清理空背包(立方體消失),下面去看看代碼是否是這么實現的。

觸發器這里在2的答案里已經有了,看了下做法出奇的簡單,可能這就是框架的力量吧,下面貼出源碼

    /// <summary>
    /// Used to trigger item pickup, modify the settings in ShowObjectTriggerer.
    /// </summary>
    [AddComponentMenu("InventorySystem/Triggers/Object triggerer item")]
    [RequireComponent(typeof(InventoryItemBase))]
    [RequireComponent(typeof(Rigidbody))]
    public partial class ObjectTriggererItem : MonoBehaviour
    {

        public InventoryItemBase item { get; protected set; }

        public bool inRange
        {
            get
            {
                return Vector3.Distance(InventorySettingsManager.instance.playerObject.transform.position, transform.position) < InventorySettingsManager.instance.useObjectDistance;
            }
        }


        public void Awake()
        {
            item = GetComponent<InventoryItemBase>();            
        }

        public virtual void OnMouseDown()
        {
            if (ShowObjectTriggerer.instance != null && ShowObjectTriggerer.instance.itemTriggerMouseClick && InventoryUIUtility.clickedUIElement == false)
            {
                if (inRange)
                    Use();
                else
                {
                    InventoryManager.instance.lang.itemCannotBePickedUpToFarAway.Show(item.name, item.description);
                }
            }
        }

        protected virtual void Use()
        {
            item.PickupItem();
        }
    }

這里我們很容易看到了OnMouseDown觸發的物品拾取邏輯,直接調用綁定的InventoryBaseItem的PickupItem()拾取函數即可,這里再次重溫下自動綁定的威力

[RequireComponent(typeof(InventoryItemBase))]

當需要使用的時候記得在Awake()函數中設置相關屬性即可

public void Awake()
{
    item = GetComponent<InventoryItemBase>();           
}

        /// <summary>
        /// Pickups the item and stores it in the Inventory.
        /// </summary>
        /// <returns>Returns 0 if item was stored, -1 if not, -2 for some other unknown reason.</returns>
        public virtual bool PickupItem(bool addToInventory = true)
        {
            if(addToInventory)
                return InventoryManager.AddItem(this);
        
            return true;
        }

上面是InventoryItemBase中的PickupItem方法,只用調用InventoryMannager類的靜態方法AddItem方法即可,就是這么簡單

       /// <summary>
        /// Add an item to an inventory.
        /// </summary>
        /// <param name="item">The item to add</param>
        /// <returns></returns>
        public static bool AddItem(InventoryItemBase item, bool repaint = true)
        {
            if (CanAddItem(item) == false)
            {
                instance.lang.collectionFull.Show(item.name, item.description, instance.inventory.collectionName);
                return false;
            }

            //// All items fit in 1 collection
            //if (item.currentStackSize <= item.maxStackSize)
            //    return best.collection.AddItem(item, repaint);

            // Not all items fit in 1 collection, divide them, grab best collection after each iteration
            // Keep going until stack is divided over collections.
            while (item.currentStackSize > 0)
            {
                var bestCollection = instance.GetBestLootCollectionForItem(item, false).collection;
                uint canStoreInCollection = bestCollection.CanAddItemCount(item);

                var copy = GameObject.Instantiate<InventoryItemBase>(item);
                copy.currentStackSize = (uint)Mathf.Min(Mathf.Min(item.currentStackSize, item.maxStackSize), canStoreInCollection);
                bestCollection.AddItem(copy);

                item.currentStackSize -= copy.currentStackSize;
                //item.currentStackSize = (uint)Mathf.Max(item.currentStackSize, 0); // Make sure it's positive
            }

            Destroy(item.gameObject); // Item is divided over collections, no longer need it.

            return true;
        }

InventoryMannager中的AddItem,代碼看似簡單,卻有四個邏輯,是否可以放置,選擇最佳容器,疊放邏輯,銷毀。

問題3回答完畢走人


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM