zoukankan      html  css  js  c++  java
  • unity 背包系统

    前言

    背包系统这个地方坑点还是很多的,照着视频做也费了很多劲.这个地方以后肯定是经常要碰到的,所以学到了什么东西就记录下来吧.

    物品信息管理

    物品信息管理的一大要求就是利用txt文件储存物品的属性,这些属性在背包系统的管理中非常有用.物品属性填写的格式可以按照下面这个表格:

    0 1 2 3 4 5 6 7
    id 名称 icon名称 类型(Drug) 加血量 加魔量 出售价 购买价

    代码如下:

    public class ObjectInfoListManager : MonoBehaviour {
    
        public static ObjectInfoListManager _instance;
        public TextAsset objectInfoListText;
    
        public Dictionary<int, ObjectInfo> objectInfoDictById = new Dictionary<int, ObjectInfo>();
        public Dictionary<string, ObjectInfo> objectInfoDictByName = new Dictionary<string, ObjectInfo>();
    
        private void Awake()
        {
            _instance = this;
    
            ReadInfo();
            //print(objectInfoDict.Keys.Count);
        }
    
        public ObjectInfo GetInfoById(int id)
        {
            ObjectInfo info = null;
            objectInfoDictById.TryGetValue(id, out info);
            return info;
        }
    
        public ObjectInfo GetInfoByIconName(string iconName)
        {
            ObjectInfo info = null;
            objectInfoDictByName.TryGetValue(iconName, out info);
            return info;
        }
    
        public void ReadInfo()
        {
            string text = objectInfoListText.text;
            string[] strArray = text.Split('
    ');
            foreach(string str in strArray)
            {
                ObjectInfo info = new ObjectInfo();
                
                ObjectType type = ObjectType.Drug;
    
                string[] proArray = str.Split(',');
                int id = int.Parse(proArray[0]);
                string name = proArray[1];
                string iconName = proArray[2];
                string str_type = proArray[3];
                switch (str_type)
                {
                    case "Drug":
                        type = ObjectType.Drug;
                        break;
                    case "Equip":
                        type = ObjectType.Epuip;
                        break;
                    case "Mat":
                        type = ObjectType.Mat;
                        break;
                }
                
                if (type == ObjectType.Drug)
                {
                    int hp = int.Parse(proArray[4]);
                    int mp = int.Parse(proArray[5]);
                    int price_sell = int.Parse(proArray[6]);
                    int price_buy = int.Parse(proArray[7]);
    
                    info.id = id; info.name = name; info.icon_name = iconName; info.type = type;
                    info.hp = hp; info.mp = mp; info.price_sell = price_sell; info.price_buy = price_buy;
                }
    
                objectInfoDictById.Add(info.id, info);
                objectInfoDictByName.Add(info.icon_name, info);
            }
        }
    
    
    }
    
    public enum ObjectType
    {
        Drug,
        Epuip,
        Mat
    }
    
    
    public class ObjectInfo
    {
        public int id;
        public string name;
        public string icon_name;
        public ObjectType type;
        public int hp;
        public int mp;
        public int price_sell;
        public int price_buy;
    }
    

    背包界面

    格子的设置

    使用GridLayoutGroup来管理格子,可以控制格子的大小以及间隔.

    背包界面的弹出

    背包界面UI的动画,现在一般用DOTween.一般比较常用的是 DoXX 之类的函数.目前我采用下面这段脚本控制Ui动画:

        public void Show()
        {
            Tween tweener = this.transform.DOMove(targetPosition, duration);
            tweener.SetUpdate(true);
            tweener.SetEase(Ease.OutQuad);
        }
    
        public void Hide()
        {
            float beginTime = Time.time;
    
            Tween tweener = this.transform.DOMove(targetPosition + originPosition, duration);
            tweener.SetUpdate(true);
            tweener.SetEase(Ease.OutQuad);
            // 设置延时
            if (Time.time - beginTime > duration)
                this.gameObject.SetActive(false);
        }
    

    其中SetEase是设置动画的播放形式,常用的是近对数曲线,也就是Ease.OutQuad.

    管理背包系统里的物品

    UI拖拽功能

    根据网上教程仿写UI拖拽功能. 拖拽功能要继承三个接口: IBeginDragHandler, IDragHandler, IEndDragHandler,也有另外继承 IPointerDownHandler,IPointerUpHandler 的.
    尤其要注意的是,UI的Position是RectTransform,所以直接用世界坐标会出现问题.常见的解决方法是使用 Camera.main.ScreenToWorldPoint(Input.mousePosition) 或者 RectTransformUtility.ScreenPointToLocalPointInRectangle(imgRect, mouseDown, Camera.current, out mouseUguiPos) .但是在我写的代码中这些都出了点问题,后来发现最简单的方法就行了

    transform.position = Input.mousePosition;
    

    代码如下:

        public void OnBeginDrag(PointerEventData eventData)
        {
            if (canvasTra == null)
                canvasTra = GameObject.Find("Canvas").transform;
    
            lastParent = transform.parent;       // 获取当前的父物体
            transform.SetParent(canvasTra);     // 将canvas设为父物体
            isRaycastLocationValid = false;     // 当前物体随着鼠标移动,需要设为穿透才可以获取被物体覆盖的格子
        }
        
        public void OnDrag(PointerEventData eventData)
        {
            transform.position = Input.mousePosition;
        }
    
        public void OnEndDrag(PointerEventData eventData)
        {
            // 获取终点位置鼠标指向的可能物体
            GameObject Go = eventData.pointerCurrentRaycast.gameObject;
    
            // 如果指向物体存在
            if (Go)
            {
                // 如果指向的是空格子
                if (Go.tag.Equals(Tags.inventory_gird))
                {
                    SetParentAndPosition(transform, Go.transform);
                }
                // 如果指向的是物品
                else if(Go.tag.Equals(Tags.inventory_item))
                {
                    // 鼠标终点下也是一个物体时,我们默认交换位置
                    SetParentAndPosition(transform, Go.transform.parent);
                    SetParentAndPosition(Go.transform, lastParent);
    
                    if (transform.position == Go.transform.position)
                    {
                        Debug.LogError("物体重叠");
                    }
                }
                // 指向的位置无效
                else
                {
                    SetParentAndPosition(transform, lastParent);
                }
            }
            // 如果没有指向任何物体
            else
            {
                SetParentAndPosition(transform, lastParent);
            }
    
            isRaycastLocationValid = true;
        }
        
        public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
        {
            return isRaycastLocationValid;
        }
    
        private void SetParentAndPosition(Transform child, Transform parent)
        {
            child.SetParent(parent);
            child.position = parent.position;
        }
    

    实现拾取物品的功能

    这个标题我觉得取得还是不好.说白了这就是要实现物品数量的显示,物品位置的不重复.
    我将功能再次细分,分为下面三个:CreateNewItemPlusItemNumCheckItem.

    CreateNewItem 主要负责根据预制体添加Item,以及设置创建物体的父节点,名字和位置等信息.
    PlusItemNum 主要负责添加物品的数量.
    CheckItem 主要负责实时检查格子上所存在物体的信息,最好放在Update方法中.

    代码如下:

       public void CheckItem()
        {
            item = this.GetComponentInChildren<InventoryItem>();
            if (item != null) // 如果不为空
            {
                SetGridOnShow(true, item.Id, item.Num);
            }
            else             // 如果是空的,就清空数据
            {
                SetGridOnShow(false, 0, 0);
            }
        }
    
        public void PlusItemNum(int addNum = 1)
        {
            item = this.GetComponentInChildren<InventoryItem>();
            item.Num += addNum;
    
            this.Num += addNum;
            numLabel.text = this.Num.ToString();
        }
    
        public void CreateNewItem(int id, int num = 1)
        {
            // 根据id寻找预制体添加item
            objectInfo = ObjectInfoListManager._instance.GetInfoById(id);
            GameObject inventoryItem = Resources.Load(objectInfo.icon_name) as GameObject;
            GameObject obj = GameObject.Instantiate(inventoryItem);
            item = obj.GetComponent<InventoryItem>();
            // 设置obj的父节点,位置,名字
            obj.transform.SetParent(transform);
            obj.transform.localPosition = Vector3.zero;
            obj.name = objectInfo.icon_name;
            // 设置item的id和num,用于检查
            item.Id = id;
            item.Num = num;
        }
    
        public void SetGridOnShow(bool isShow, int id, int num = 1)
        {
            // 设置Grid和显示
            this.Num = num;
            this.Id = id;
            numLabel.text = num.ToString();
            numLabel.gameObject.SetActive(isShow);
        }
    

    显示物品的信息

    这里需要创建一个信息栏,然后在药品创建的时候将信息栏与该药品关联并隐藏.这里用到了一个小技巧:因为预制体没法在界面直接关联,所以只好用脚本寻找关联;但我们又不能让信息栏一直显示,所以需要将它"隐藏";因此我用到的小技巧是将信息栏的 localScale 设为 zero,这样可以达到目的又不至于药品创建时关联不到.
    信息栏的显示和隐藏用普通的 OnMouseXX 不行,所以还是要继承一些接口重写函数.需要继承的接口是IPointerEnterHandle, IPointerExitHandle.
    代码如下

        private void Update()
        {
            if (this.enabled)
            {
                inventoryDes = GameObject.Find("InventoryDes").GetComponent<InventoryDes>();
            }
        }
    
        public void OnPointerEnter(PointerEventData eventData)
        {
            //Debug.Log("鼠标进入");
            inventoryDes.Show(this.Id);
        }
    
        public void OnPointerExit(PointerEventData eventData)
        {
            inventoryDes.Hide();
            //Debug.Log("鼠标离开");
        }
    
  • 相关阅读:
    HDU 1069 Monkey and Banana
    HDU 1029 Ignatius and the Princess IV
    HDU 1024 Max Sum Plus Plus
    Gym100923H Por Costel and the Match
    Codeforces 682C Alyona and the Tree
    Codeforces 449B Jzzhu and Cities
    Codeforces (ccpc-wannafly camp day2) L. Por Costel and the Semipalindromes
    Codeforces 598D (ccpc-wannafly camp day1) Igor In the Museum
    Codeforces 1167c(ccpc wannafly camp day1) News Distribution 并查集模板
    快乐数问题
  • 原文地址:https://www.cnblogs.com/ChanWunsam/p/10018311.html
Copyright © 2011-2022 走看看