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("鼠标离开");
        }
    
  • 相关阅读:
    SpringBoot多数据源启动器
    数据结构模拟器
    mysql5.7查询今天、昨天、本周、上周、本月、上月数据
    SpringBoot项目本地可以发送邮件,部署到阿里云服务器发送邮件失败的解决方法
    Centos7搭建Maven私服-Nexus3.19.1-01
    Linux中部署jar包并指定日志输出文件
    ThreadLocal是什么?谈谈你对他的理解
    leetcode-双指针遍历
    不要再纠结css/js/html有没有必要放在WEB-INF下了
    数据库的表的字段名称与实体类(pojo)不对应解决方案
  • 原文地址:https://www.cnblogs.com/ChanWunsam/p/10018311.html
Copyright © 2011-2022 走看看