UnityUnity技术分享unity3D技术分享

Unity3D--UGUI背包系统(三)

2016-04-01  本文已影响8214人  Wei_Boom

之前完成了一个背包以及添加物品,但是物品并不具备“物品”所该有的功能,除开每个游戏所需的特定的效果之外,也缺乏基本的移动拖拽功能等,这篇我们完善一下背包系统。先回顾一下之前的代码,这里我直接贴出来。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GridManager : MonoBehaviour {
    public List<GameObject> grid;//格子列表,用来存储所有个物品格子
    public GameObject item;//物品对象,把prefab添加到这里
    void Update () {
        if(Input.GetKeyDown(KeyCode.K)){
            AddItem(item);//调用添加物品的方法
            }
    }
    public void AddItem(GameObject _item){
     //查找每个格子,寻找空格子的物体。
        for(int i=0;i<grid.Count;i++){
            //这里我们通过查找名字来寻找物体,判断格子内是否有物品存在
            //但这样就只能查找命名为该名字的物体,实际中我们可以通过tag或者其他属性来判断
           bool isHaving= grid[i].transform.FindChild("Sword(Clone)");
           //如果当前格子有物品存在         
           if(isHaving)
                continue;             
           else if(!isHaving){
                //当前的空格子
                Transform _i=grid[i].gameObject.GetComponent<RectTransform>();
                //创建一个物品对象
                GameObject go=(GameObject)Instantiate(_item); 
                //把创建的物品对象添加到格子下                        
                go.transform.SetParent(_i);
                //调整物品的位置位于格子中间
                go.transform.localPosition=Vector3.zero;
               break;
           }      
        }
    }

这段代码实现了根据按顺序给每个格子添加物品,如果我们的格子里面有物品,则会跳过,查看下一个格子是否能存放物品。注意,我们暂时没有写背包格子满载的情况,也没有判断物品是否能够叠加的功能。

那么,当一个背包里的东西杂乱无章的时候,我们就需要对其进行整理。这里我们先添加一个函数。

//在开头添加一个存储列表
public List<GameObject> c_grid;//备用列表,用于存储需要整理位置的物品
//整理物品函数
public void ClearUpGrid(){
        for (int i = 0; i < grid.Count; i++)
        {
            bool isHaving= grid[i].transform.FindChild("Sword(Clone)");
            if(isHaving){
                GameObject go=grid[i].transform.FindChild("Sword(Clone)").gameObject;
               c_grid.Add(go);//将当前格子内的物品存储到后备列表里面
               if(i==grid.Count-1)//当所有格子内的物品都存放到备用列表中后
                grid.Clear();//清空当前的背包
            }
        }
        for (int j = 0; j < c_grid.Count; j++)
        {
            //对于每个存储到后备列表中的物品,我们依次添加到格子列表中
            GameObject go=c_grid[j];
            go.transform.SetParent(grid[j].transform);
                //调整物品的位置位于格子中间
            go.transform.localPosition=Vector3.zero;  
            //如果所有的物品都添加到了物品格子列表中,则清空后备列表
            if(j==c_grid.Count)
              c_grid.Clear();
    } 
        }
         

我们再给Update函数添加一句来调用整理背包的函数

 if(Input.GetKeyDown(KeyCode.Space)){
                ClearUpGrid();
            }

分析一下
1、查询每个背包格子,如果有物品存在,则将该物品复制一份到备用列表中。
2、当查询到最后一个背包格子的时候,则清空当前背包格子内的物品
3、查询每个后备列表中的物品,依次添加到背包格子列表中。
4、当后备列表中所有物品都添加完成了,则清空后备列表。

这里也需要注意一下!!我们发现代码中再一次对整个背包列表进行了查询,与之前添加物品函数一样,总共我们就查询了两次,其实我们也可以当背包格子中有物品存在的时候,就将其添加到后备列表中存储,整理的时候直接清空格子再添加。但是当我们背包过大,物品过多的时候,这样子不一定好,因为这样每个物品就占了2个资源,如果我们按照这里的函数来执行的话,就只会在整理的时候占用资源。我们在做背包的时候,需要考虑如何安排这样的代码。这里提供这样的一个方法来参考。

另外,我们这里所有的物品都是一个,所以不存在类别关系,如果要对物品进行分类优先排序,我们需要对每个物品添加类别属性,通过类别属性来自定义优先级,在排序的时候进行优先排序。

背包整理的功能有了,接下来是对每个物品的处理。

首先,我们确定几项要点:
1、物品能够拖动
2、物品拖动后的层级关系
3、物品拖放的位置
4、拖动失败的处理

其实这是一个很简单的问题,我们事先给gridList添加了一个空物体GridManager作为父物体,先给我们的物品添加一个脚本(该脚本偷懒用的别人的)

using UnityEngine;  
using UnityEngine.UI;  
using UnityEngine.EventSystems;  
using System.Collections;  
  
public class ItemDrag : MonoBehaviour, IPointerDownHandler,IPointerUpHandler,IDragHandler {  
      
    // 鼠标起点  
    private Vector2 originalLocalPointerPosition;     
    // 面板起点  
    private Vector3 originalPanelLocalPosition;  
    // 当前面板  
    private RectTransform panelRectTransform;  
    // 父节点,这个最好是UI父节点,因为它的矩形大小刚好是屏幕大小  
    public RectTransform parentRectTransform;  
    //格子列表
    private GameObject gridManager;//在gridlist之上添加的一个新的空物体作为父节点
    private GameObject originalGrid;//记录物品拖动前的位置
    private static int siblingIndex = 0;  
    void Awake () {  
        panelRectTransform = transform as RectTransform;  
        parentRectTransform = GameObject.FindGameObjectWithTag("ScrollRect").GetComponent<RectTransform>() as RectTransform;  
        gridManager=GameObject.Find("GridManager");   
    }  
      
    // 鼠标按下  
    public void OnPointerDown (PointerEventData data) {  
        //记录物品当前所在的格子信息
        originalGrid=panelRectTransform.parent.gameObject;
       //将物品放置在gridManager下,并设置层级,保证物品显示在整个背包层面之上
        panelRectTransform.SetParent(gridManager.transform);
        siblingIndex++;  //层级管理
        panelRectTransform.transform.SetSiblingIndex(siblingIndex);  
        // 记录当前面板起点  
        originalPanelLocalPosition = panelRectTransform.localPosition;  
        // 通过屏幕中的鼠标点,获取在父节点中的鼠标点  
        // parentRectTransform:父节点  
        // data.position:当前鼠标位置  
        // data.pressEventCamera:当前事件的摄像机  
        // originalLocalPointerPosition:获取当前鼠标起点  
        RectTransformUtility.ScreenPointToLocalPointInRectangle (parentRectTransform, data.position, data.pressEventCamera, out originalLocalPointerPosition);  
    }  
    public void OnPointerUp(PointerEventData data){
       // transform.SetParent(gridlist.transform);
        RaycastHit2D hit = Physics2D.Raycast(Input.mousePosition,-Vector2.up); 
        if (hit.collider != null) { //如果射线检测到的gameobject为grid,就把当前物品放在grid节点下 
            if(hit.collider.gameObject.tag=="Grid"&&hit.collider.gameObject.transform.FindChild("Sword(Clone)")==null) 
               transform.parent=hit.transform;
               else
               {
//如果不是格子或没有检测到物体,则将物品放回到原来的格子内
                   transform.parent=originalGrid.transform;
               }
        }
        else
        {
          transform.parent=originalGrid.transform;
        }
    //重置物品位置
      transform.localPosition=Vector3.zero;
    }
    // 拖动  
    public void OnDrag (PointerEventData data) {  
        if (panelRectTransform == null || parentRectTransform == null){
            return;  
        }
        Vector2 localPointerPosition;  
        // 获取本地鼠标位置  
        if (RectTransformUtility.ScreenPointToLocalPointInRectangle (parentRectTransform, data.position, data.pressEventCamera, out localPointerPosition)) {  
            // 移动位置 = 本地鼠标当前位置 - 本地鼠标起点位置  
            Vector3 offsetToOriginal = localPointerPosition - originalLocalPointerPosition;  
            // 当前物品位置 = 物品起点 + 移动位置  
            panelRectTransform.localPosition = originalPanelLocalPosition + offsetToOriginal;  
        }  
       // ClampToWindow ();  
    } 

本段代码是借用他人的代码做修改来使用的。我们注意一下代码。
首先,在脚本开头我们添加了这样一句

using UnityEngine.EventSystems;

这是引用ugui的响应事件,同时也在MonoBehaviour后添加了三个接口。
同时提一下,因为C#不支持多继承,但我们可以通过实现接口的方法来做类似多继承的效果。

public class ItemDrag : MonoBehaviour, IPointerDownHandler,
IPointerUpHandler,IDragHandler

添加后会发现报错,提示我们没有实现接口。这三个接口名分别对应以下三个函数,分别当鼠标按下时,鼠标松开时,以及物品拖动时。

public void OnPointerDown (PointerEventData data) {  }
public void OnPointerUp (PointerEventData data) {  }
public void OnDrag(PointerEventData data) {  

详细的功能在代码里注释有了。
另外关于RectTransformUtility.ScreenPointToLocalPointInRectangle,雨松的博客里也有讲过,是将一个坐标转换的问题,将鼠标的坐标转换为UI的坐标。这段代码呢可以去官网查询一下api了解一下,不仅是背包,包括血条名字的显示,飙血字效果等都会用到的相关方法。

再次注意,光是这样,我们是检测不到碰撞的,通过射线碰撞需要对每个物品格子添加Collider来检测,选中物品列表中的每个grid,添加BoxCollider2D组件,在Scene下调节Collider的大小。现在可以检测了。

别忘了给格子添加Tag为Grid。
运行一下,已经完成对背包的整理和对物品的拖放了。

如果对本篇内容有什么问题或者意见或不对的地方,请及时指出,欢迎交流~

上一篇下一篇

猜你喜欢

热点阅读