以后都按照插件使用,提出問題,回答問題的方式來進行總結和學習
效果圖
1、運行相關的例子,場景中出現4個矩形,這4個矩形是用來模擬物品掉落的包裹,移動Player靠近物品
2、使用鼠標點擊物品正方體,點擊I鍵打開包裹,這里看到3個掉落包裹正方體已經點沒有了,相應的背包里多出三個蘋果,至此例子演示完畢
插件使用
使用Inventory Pro進行裝備的拾取,有很簡單的例子
1、點擊菜單Tool,InventorySystem,Main editor 打開自定義Editor
2、在Main manager對話框中點擊Items editor選項卡,選中Item editor 點擊Create Item 綠色按鈕,會彈出左側的選擇子對話框,其中列出了可以創建的Item種類,第一項就是消耗品,當然還有很多別的東西,這里先試驗最簡單的消耗品
3、點擊創建消耗品以后會出來一個Item的詳情界面,這里需要添加Item的Name,Icon,Behavier里有類型,冷卻時間,價格,是否可以疊放,是否可以丟棄等等屬性吧,同時Editor會創建出相應的物品prefab,直接拖拽到場景中即可
問題
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 可視化環境來驗證一下
通過選中預設我們看到其綁定的相關插件有碰撞和剛體屬性,最后綁定的腳本是Consumable Inventory Item (Script),這樣也驗證了上面代碼加入的Type,也就是在類選擇所對應的InventoryItemBase 類型
這里需要注意的是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這個特性就可以不用手動進行相關插件的綁定了。
上圖是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回答完畢走人