亞寵展、全球寵物產業風向標——亞洲寵物展覽會深度解析
1259
2022-05-29
推薦閱讀
CSDN主頁
GitHub開源地址
Unity3D插件分享
簡書地址
我的個人博客
QQ群:1040082875
一、前言
發現就喜歡研究這些插件,為什么呢,因為方便快捷啊。基本不用研究源代碼怎么實現的,只要會有就行了。但是,光這樣也不行,還是要多去看看底層代碼是怎么實現的,還有人家的框架是怎么搭的。
要不說Unity3D入門容易,提升難呢,因為提升全是靠苦功夫,去研究底層代碼。算了,不絮叨了
二、參考文章
Unity3D 裝備系統學習Inventory Pro 2.1.2 總結
Unity3D 裝備系統學習Inventory Pro 2.1.2 基礎篇
三、正文
先上一張效果圖
下載鏈接:
https://github.com/764424567/Unity-plugin/tree/master/Menu/Unity3D-S-Inventory
無效了記得跟博主說一聲哈
1、總體結構
非UI相關InventoryItem 物品體系類,具體如裝備,消耗品,商店物品等
UI相關InventoryUIItemWrapper 物品體系
UIItem的UI包裝的Item繼承體系
ItemCollection這樣的類,因為簡單的增、刪和改肯定是逃不了,復雜的如交換,容器間的交換等操作
UIWindow體系的窗口類,具體有角色,銀行,技能,店鋪等窗口
InventoryUIDialog系統下的對話框類,具體有確認框,買賣,通用提示
特殊窗口(非繼承體系窗口),如上下菜單,通知窗口等
配置管理
InvertoryManager
ItemManger
數據庫操作
應該是一些輔助類,有UI部分的,事件輔助,定義接口等等吧,這部分還沒有深入去閱讀應該也是挺復雜的
一些第三方插件,Unity3D特性及Edit擴展等等
2、使用教程
在你的Assets/Demos/Scenes 你會發現這些演示場景,這些演示場景會包含所有特性,一定要仔細的看一下哦
選擇1(自動)
打開設置向導,它可以發現在Tools/Inventory Pro/Setup wizard
對于錯誤“沒有管理者發現的對象”;單擊確定按鈕和一個管理對象命名為"_managers"將自動添加到你的場景里。
選擇2(手動)
創建一個空的游戲對象將inventorymanager組成,你可以找到在 庫存/經理/ inventorymanager你會得到幾個管理器組件包括inventorymanager,inventorysettingsmanager和更多的,我們不需要現在。
itemmanager包含所有項目的項目數據庫,你可以創建管理每個場景,并使用不同的數據庫,每一個場景。
1. 去你的項目窗口中,找到一個地方,你想創建項目數據庫。
2. 右鍵單擊(或點擊創建,或者去創建/ 庫存親 /項目數據庫)來創建一個新的項目數據庫。
3. 一旦創建的數據庫將在你的項目中選定。
4. 將項目拖到itemmanager的試題數據庫槽。
現在也讓我們創建 語言數據庫 ,做完全相同的操作,并將它分配給inventorymanager的 lang 。
首先打開主編輯器可以發現在 工具/庫存親/主編。當你打開編輯會給你一個錯誤消息的第一時間(見下圖)。
這是因為庫存親需要知道它應該保存新項目。單擊“設置”按鈕,選擇一個文件夾 文件夾里面你的項目。
現在讓我們打開整合經理再次看到我們必須配置。
第一個選項卡“UI”包含用于渲染UI的基本要素。為了做到這一點需要至少2個項目的 項目按鈕預置 Item Button Prefab和 UIROOT。
這個項目按鈕預置是的預置 /槽包含的項目。根是包含所有用戶界面窗口的UI畫布。
幾個演示器可以用來快速上手。你可以在庫存/演示/資產/用戶界面/ ui_prefabs / ui_item_pfb找到默認的包裝
并新建一個UGUI,把UI Canvas 賦值給 GUI ROOT。
項目編輯
主編輯器,包括項目編輯器可以打開通過 Tools / Inventory Pro / Main editor
首先,單擊“創建項目”按鈕,一旦我們做了一個對象選擇器將顯示。
在對象選擇器,我們可以選擇的物品類型。每一項類型具有不同的行為或目的,例如當消耗品被使用時將減少1個的堆棧大小,而當武器裝備被裝備時出現人物在場景中得到呈現。
你可以使用鍵盤來選擇類型來創建,使用向上和向下箭頭,選擇一個你想要的并回車確認。
讓我們抓住一個consummableinventoryitem (可消耗品)現在,并配置。
接下來你會被提示步驟2。在這里你可以選擇一個模型,以此為基礎的新項目。例如,你已經有了一個預置,例如一把劍,它擁有的紋理圖片、可以使用碰撞器和自定義組件,那么只需拖動物品到“Drag object here”字段位置上,或者選擇使用“Select model”按鈕。
假設你沒有一個預先定義的模型,只是想創建一個新的對象選擇“No model”,或“2D sprite with trigger“用于2D游戲。
一旦創建的項目將顯示一個按鍵在列表的底部,點擊并生成物品。
一旦項目(預制)是創造了你可以修改它通過改變紋理,模型,添加自定義組件,等。
1.2 類別Categories
使用類別編輯器你可以定義項目的具體類別,例如食物,藥水,劍,斧,等你可以定義每一類的冷卻時間,這樣你可以使用消耗品,同一類別中的所有其他項目也將在冷卻。就是說,你可以重寫每個項目的冷卻時間在項目編輯器”選項卡。
1.3 性能Properties
使編輯更強大的性能也增加,屬性允許您創建自定義的“變量”。一旦創建,你可以指定的屬性項目編輯器內的任何項目,它提供一個值。 屬性也可以通過 自定義代碼。
值的字符串格式: 格式允許您使用該屬性的值在UI格式。 使用{ NR }符號來定義自己的格式。
基準值: 基值的初始值的屬性。例如,你可能有5的強度默認和允許它從那里成長。
1.4 稀有性編輯 Rarity editor
最后但并非最不重要的你可以定義你的項目多種多樣,各有它的顏色,在工具提示中顯示的用戶界面元素。
定義物品的稀有程度分級,在UI中顯示的顏色區別,還有物品掉落時出現在場景中的預置顯示(這里默認是一個袋子)
——
設備系統非常靈活,可用于幾乎任何裝備/附件系統。人物屬性是通過選擇一個或多個項目類型的定義,可以使用編輯器選擇哪些數據將被計算,最終顯示。
除了統計也有裝備的類型,這些可以被定義在“裝備類型編輯器選項卡 Equip type editor tab ”。裝備類型可以限制避免某些組合。例如,當裝備單手劍,我確信雙手的劍和斧子和匕首雙手不兼容。當單手劍裝備,裝備一把單手匕首,匕首將被裝備。
——
3 貨幣編輯Currency editor
貨幣 編輯器允許你定義,可以用在你的項目,制定藍圖,供應商的貨幣,等。
每一種貨幣可以包含一組轉換。這些轉換允許你將一個美元兌歐元。
自動轉換的定義是否可用于汽車。貨幣之間轉換。例如,許多游戲使用系統的金,銀和銅。當跑出來的銅系統可以轉換到銅銀貨幣。基本上重新填充它從一個更高的貨幣,可以轉換成。
自動轉換可以在編輯器底部的定義。
自動轉換最大:允許你轉換到一個更值錢的貨幣一旦你達到定義的最大值。例如,你不能擁有超過100的銅,所以一旦系統發現你有100以上的銅將被轉換為1銀。
自動轉換分數: 當“讓分數”(在頂部)不啟用的分數也需要轉換,或丟棄。例如,當你有1.1銀(這是不允許的)系統會把它降到1銀10銅。
4 制作編輯
制作經理類,允許你創建“鍛造”任何時候,無論是烹飪,鍛造或皮。
讓我們創建一個新的 category,并開始創造一些藍圖。
1. 默認情況下,結果項目名稱–項目成功后,工藝–將作為藍圖的名稱,但你當然可以,像往常一樣,配置。
2. 機會因素表明工藝成功的可能性有多大,0.5種工藝的機會有50%的機會,和1的機會的因素有100%的成功機會。
1. 加速因子是成對的制作時間,在許多游戲(哇)制作一項變快時,創建一個完整的批一次。例如,當制作10爛蘋果,第一項需要5秒,第二,1.1(10%)更快,這歸結為5 /(1.1 ^ N)。
3. 所需物品說明很多項目所需的工藝給定的項目,這是從布局上分,可以在底部的定義。
一旦你定義的藍圖,你可以創建一個標準或布局的基礎工藝窗口允許用戶做他的 事。
語言編輯器允許你定義一個特定的動作發生在顯示時的庫存支持信息。
目前只有1個語言數據庫的支持,為多語言數據庫支持將來會增加。
如果你喜歡一個特定的消息不出現只是讓它空著。
搜索
請注意,您還可以使用搜索欄搜索名稱的變量設置/你想改變。
3、Demo解析
首先說一下Demo1的功能,其實很簡單主要是建立起來Inventory Pro的運行環境,首先項目的Demo是3d的所以創建項目時,選擇是3D工程。運行環境中,使用標準插件庫建立一個第三方視角跟隨的角色,角色可以在Panel中自由的移動跑跳;然后才是Inventroy Pro的基礎配置,主要是引入Setting,在Setting中進行一些基礎的配置。具體的運行后的界面如下圖所示
第一步在Scene中添加一個Panel,然后把它設置大點,不然角色會掉下去
第二步,找到圖中的角色prefab然后直接拖到場景中,reset一下即可
第三方視角相機跟隨
第三方視角相機跟隨,也是按照標準過程進行
1,刪除原來的MainCarmar攝像機
2,從Asset中拖拽我們需要的Prefab到場景中來
第三步,設置相機的Target為我們的控制角色,這里拖拽即可
基礎環境創建好了,下面我們需要創建下裝備系統的自身的基礎環境了,涉及到了Srcript,Manage文件夾中的四大基礎類
裝備系統配置類,
裝備系統管理類
Item管理類(工廠可能不準確,歡迎指正)
裝備數據Asset類
Demo1中其實要實現的就兩步
第一步,創建空游戲對象,配置InventorySetting類
第二步,初始化游戲Item數據Asset
4、實例
功能點:
1、實現了兩個窗口,通過點擊鍵盤I來,打開或者關閉窗口也就是Toggle功能
2、裝備窗口中的物品欄空格數量動態生成可控,可以在屬性窗口手動配置
3、窗口具有拖拽功能
4、窗口物品具有拖拽,及窗口間拖拽
5、可以在窗口使用物品的功能,物品有消耗扇形顯示功能
具體效果圖如下所示:
1、具體在UGUI 中的Canvas中創建一個InventoryWindow
2、在InventoryWindow下創建空GameObject并命名Container,賦予Grid LayOut 插件
3、給InventoryWindow添加InventoryUI組件,插件將自動添加WindowUI也就是通用窗口輔助插件
4、添加拖拽功能組件DraggableWindow,這樣窗口就有了拖拽功能了
至此簡單的點擊I鍵可以打開和關閉的裝備窗口做好了
最后總結下實現通用窗口的三個類,分別是WindowHelper文件夾下的,UIWindow,UIWindowPage和DraggableWindow
1、DraggableWindow有就是拖拽窗口的組件,這里還是比較贊的,也是插件編程的簡單例子,這里學過UGui的同學都知道要實現拖拽功能實現IBeginDragHandler和IDargHandler接口即可,原理很簡單, 源碼如下
using UnityEngine; using System.Collections; using UnityEngine.EventSystems; namespace Devdog.InventorySystem { [AddComponentMenu("InventorySystem/UI Helpers/DraggableWindow")] public partial class DraggableWindow : MonoBehaviour, IBeginDragHandler, IDragHandler { public float dragSpeed = 1.0f; private Vector2 dragOffset; public void OnBeginDrag(PointerEventData eventData) { if (InventorySettingsManager.instance.isUIWorldSpace) dragOffset = transform.position - eventData.worldPosition; else dragOffset = new Vector2(transform.position.x, transform.position.y) - eventData.position; } void IDragHandler.OnDrag(PointerEventData eventData) { transform.position = new Vector3(eventData.position.x + dragOffset.x * dragSpeed, eventData.position.y + dragOffset.y * dragSpeed, 0.0f); } } }
2、UIWindow這個類是窗口的公共類,先上類圖主要功能點在類圖上標注了,這里就不廢話了,主要就是控制的窗口的顯示關閉,及組合動畫效果比較難的是實現了類似組合窗口的功能(這部分有后有機會再仔細分析)
源碼就不全上了,上點有亮點的部分如下:
public virtual void Hide() { if (isVisible == false) return; isVisible = false; if (OnHide != null) OnHide(); if (hideAudioClip != null) InventoryUIUtility.AudioPlayOneShot(hideAudioClip); if (hideAnimation != null) { animator.enabled = true; animator.Play(hideAnimation.name); if (hideCoroutine != null) { StopCoroutine(hideCoroutine); } hideCoroutine = _Hide(hideAnimation); StartCoroutine(hideCoroutine); } else { animator.enabled = false; SetChildrenActive(false); } } ///
以上部分是通過協程實現的具有延時效果的動畫關閉窗口的代碼,有代表意義。
3、UIWindowPage類,該類是UIWindow的子類,在UIWindow有一個Page的集合用于組合顯示UIWindowPage,這塊Demo1中沒有涉及到該功能這里就不仔細分析了,等后面的例子中出現了再研究,亮點代碼如下:
///
總體來說目前的UIWinow和UIWindowPage更像是容器Panel或者Group不像是窗口,等以后的Demo中有復雜的再學習吧。
4、如何通過鍵盤喚起窗口
這個比較簡單用到了U3D的輸入輸出模塊,關鍵代碼如下:
///
功能點:
1、實現了兩個窗口,通過點擊鍵盤I來,打開或者關閉窗口也就是Toggle功能
2、裝備窗口中的物品欄空格數量動態生成可控,可以在屬性窗口手動配置
3、窗口具有拖拽功能
4、窗口物品具有拖拽,及窗口間拖拽
5、可以在窗口使用物品的功能,物品有消耗扇形顯示功能
6、通用窗口的類體系結構
具體的插件使用和功能已經在上篇中說明了這里就不多說了
1、本篇重點分析 6通用窗口的類體系結構,類組織和類圖如下所示:
類的繼承體系結構這里就說了,在第一篇有可以自行查閱
類的引用關系、核心字段和方法已經在類圖中標記的很清楚,用簡單的幾句話說明下,裝備窗口中的每個格子是由一個空格子具有背景的UIItem和InventoryItemBase Model組成的,而整個裝備窗口是一個InventoryUI,該類繼承了ItemCollectionBase類,也就是說它是具有一組UIItem的裝備集合窗口,添加上UIWindow組件、DraggableWindow就具有了普通窗口的拖拽移動和顯示關閉的功能了。
2、裝備窗口中的物品欄空格數量動態生成可控,可以在屬性窗口手動配置
如何實現動態裝備窗口主要有兩個核心技術:一個是UI中的自適應排列,也就是Grid layout Group組件;一個是U3D的prefab實例化技術
動態初始化Cell數據核心代碼如下
protected virtual void FillUI() { if (manuallyDefineCollection == false) { items = new InventoryUIItemWrapperBase[initialCollectionSize]; // Fill the container on startup, can add / remove later on for (uint i = 0; i < initialCollectionSize; i++) { items[i] = CreateUIItem
是不是很簡單 initailCollectionSize是InventoryUI基類的一個共有field也就是說這個裝備格子的數量,這個可以根據自己設計的裝備窗口手動設置,然后根據這個循環調用CreateUIItem
自己在學習的過程中,雖然讀了源碼過了幾天來寫教程,還是有點不清楚,不能親車熟路(這也許就是讀與寫的區別吧),給自己提出了幾個問題,在重新去翻代碼之前先給自己提出幾個問題:
1、拖拽的事件發起者應該是那個類?
2、拖拽的事件的Drag是如何確定下方的UI元素的?
3、拖拽后的邏輯操作,應該由哪個類來承接?
4、窗口與窗口之間的拖拽,既有Drag又有Drop,如何更加合理的解決這個問題?
5、窗口間物品拖拽的以及同窗口物品拖拽的邏輯流程是什么?
A1 拖拽的事件發起者應該是那個類?:拖拽的發起者必須是UI元素這點是必須的,目前涉及的UI元素有兩種一種是窗口容器,一種數據裝備格元素,顯然裝備格元素更有優勢,因為少了一層定位邏輯判斷,Drag事件直接發起,還可以做Move的相關邏輯,這里Inventory Pro2也確實是怎么做的(這里初次接觸UGUI的同學可能要去學習下相關的事件系統,這里就不多說了),這里InventoryUIItemWrapper就是裝備格的基類,這里繼承了UGUI的相關UI事件,IBeginDragHandler, IEndDragHandler, IDragHandler, IPointerUpHandler, IPointerDownHandler, IPointerEnterHandler, IPointerExitHandler,這么多接口也預示著代碼并不簡單
這些就是接口實現函數,把他們都弄明白了也就明白了如何實現拖拽
A5 窗口間物品拖拽的以及同窗口物品拖拽的邏輯流程是什么?:先回答這個問題比較合理,在過去的winform的拖拽中并沒有這么多接口可以實現,但是我相信拖拽操作的本身邏輯應該是不變的,也就是幾個步驟,
1)在物品上點擊鼠標左鍵(記錄鼠標點擊的元素)->
2)在鼠標不up,且move事件中確認了拖拽開始(Drag事件) –>
3) mouse Move事件中獲得鼠標下的元素->
4)mouse up 事件觸發Drop,判斷鼠標位置及鼠標下元素是否可以drop如果可以進行Drop邏輯至此,這個拖拽操作結束
技術原型就是這么簡單。下面看看Uintiy3d ugui Inventory Pro是如何實現的,又讀了一遍代碼,深深的有體會到了一把,“原理很簡單,現實很殘酷”,這還是在ugui為我們做了一些封裝的情況下,這里其實涉及的函數其實有5個
OnPointerEnter:確定了點擊了那個UI元素,對應1)
OnBeginDrag:開始拖拽,對應2)
OnDrag:拖拽中,對應3)
OnEndDrag:結束拖拽,對應4)
OnPointExit:清空選中元素,恢復默認值
具體代碼比較多這里不再展開說了,這里慶幸的是,Inventory Pro對拖拽的邏輯進行了封裝,在InventoryUIItemWrapper中接口實現函數中,主要做的參數舒適化,主要關于UI的邏輯代碼封裝在了InventoryUIUtility類中,
以下是主要接口實現函數的代碼
public virtual void OnBeginDrag(PointerEventData eventData) { if (itemCollection == null) return; if (item != null && eventData.button == PointerEventData.InputButton.Left && itemCollection.canDragInCollection) { // Create a copy var copy = GameObject.Instantiate
以下是InventoryUIUtility類封裝的靜態函數代碼
public static InventoryUIDragLookup BeginDrag(InventoryUIItemWrapper toDrag, uint startIndex, ItemCollectionBase collection, PointerEventData eventData) { if (draggingItem != null) { Debug.LogWarning("Item still attached to cursor, can only drag one item at a time", draggingItem.gameObject); return null; // Can only drag one item at a time } if (eventData.button != PointerEventData.InputButton.Left) return null; draggingItem = toDrag; //draggingButtonCollection = collection; // Canvas group allows object to ignore raycasts. CanvasGroup group = draggingItem.gameObject.GetComponent
A2 拖拽的事件的Drag是如何確定下方的UI元素的?
這里DragDrop中的元素由 OnPointerEnter 來觸發獲得(有點像Mouse Move事件),具體保存在InventoryUIUtility類的一個靜態變量中
public static InventoryUIItemWrapper hoveringItem { get; private set; }
A3 拖拽后的邏輯操作,應該由哪個類來承接?
也就是Drop的操作由誰來完成,首先回憶下職責InventoryUIItemWrapper類負責事件的觸發,InventoryUIUtility類負責UI相關的邏輯(選中,射線,坐標系統)
再看一遍OnDragEnd函數,具體的Drop邏輯是有Drop的后查找的lookup集合類(裝備格集合基類ItemCollectionBase)來處理的,具體又有交換\合并兩個邏輯,觸發代碼如下:
if (lookup.endOnButton) { // Place on a slot lookup.startItemCollection.SwapOrMerge((uint)lookup.startIndex, lookup.endItemCollection, (uint)lookup.endIndex); }
當然還有一種復雜的邏輯,就是扔掉物品的操作,這個是有具體的Item裝備模型類(InventoryItemBase)來處理,核心代碼在TriggerDrop方法中來調用,具體如下:
public override void TriggerDrop(bool useRaycast = true) { if (item == null || itemCollection.canDropFromCollection == false) return; if(item.isDroppable == false) { InventoryManager.instance.lang.itemCannotBeDropped.Show(item.name, item.description); return; } Vector3 dropPosition = InventorySettingsManager.instance.playerObject.transform.position; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, InventorySettingsManager.instance.maxDropDistance, InventorySettingsManager.instance.layersWhenDropping)) { dropPosition = hit.point; } else { return; // Couldn't drop item } var s = InventorySettingsManager.instance; if (useRaycast && s.showConfirmationDialogWhenDroppingItem && s.showConfirmationDialogMinRarity.ID <= item.rarity.ID) { // Not on a button, drop it var tempItem = item; // Capture list stuff var msg = InventoryManager.instance.lang.confirmationDialogDrop; s.confirmationDialog.ShowDialog(msg.title, msg.message, s.defaultDialogPositiveButtonText, s.defaultDialogNegativeButtonText, item, (dialog) => { ItemCollectionBase startCollection = tempItem.itemCollection; uint startIndex = tempItem.index; var d = tempItem.Drop(dropPosition); if (d != null) { startCollection[startIndex].Repaint(); } }, (dialog) => { //Debug.Log("No clicked"); }); } else { var d = item.Drop(dropPosition); if (d != null) { Repaint(); } } }
A4 窗口與窗口之間的拖拽,既有Drag又有Drop,如何更加合理的解決這個問題?
這個問題比較繞,其實也涉及到了問題2,其實無論怎么拖拽也就是兩個東西,一個是被拖拽的物體from,一個是要放的地方to,這里其實都是窗口中的格子,只要有了這兩個格子類也就確定了from和to的容器,比較特殊的一種情況也就是 from和to兩個容器相等,也就是同窗口拖拽了,具體這些對象InventoryUIUtilty類中都做了封裝,還是很贊的具體代碼如下:
public class InventoryUIDragLookup { public int startIndex = -1; public ItemCollectionBase startItemCollection; public int endIndex = -1; public ItemCollectionBase endItemCollection; public bool endOnButton { get { return endItemCollection != null; } } } #region Variables private static InventoryUIItemWrapper draggingItem; public static InventoryUIItemWrapper hoveringItem { get; private set; } public static bool isDraggingItem { get { return draggingItem != null; } } public static bool clickedUIElement { get { return EventSystem.current.IsPointerOverGameObject(); } } public static bool isFocusedOnInput { get { if (EventSystem.current.currentSelectedGameObject != null) if (EventSystem.current.currentSelectedGameObject.GetComponent
復雜的物品拖拽邏輯總結完畢,再次向我們印證了,從helloworld到現實是多么的困難,實際的情況可能更復雜比如要加入動畫效果,要做網絡延時驗證,數據同步等等吧
5G游戲 unity
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。