UGUI实现背包系统

UGUI实现背包系统

本次实现过程参考了师兄的博客

制作过程

画布(Cavas)是绘图区域, 同时是 ui 元素的容器。 容器中 ui 元素及其子 UI 元素都将绘制在其上。 拥有Canvas组件的游戏对象都有一个画布,它空间中的子对象,如果是 UI 元素将渲染在画布上。

因此,为了显示背包系统中的众多UI元素,先选择2D视图,而后建立一张画布命名为MyCanvas,该canvas的Render Camera 设置为GUI Camera,这个相机只会渲染UI层,因此将其Culling Mask 设置为UI。其它相关设置如下:
在这里插入图片描述

在这里插入图片描述

为了实现将物品从背包拖动到装备栏的效果,显然,我们需要一个背包(Bag)、一个装备栏(Wear),于是先在Mycanvas下建立两个panel,分别命名为Bag和Wear,设置其锚点为中心,由于我的背包大小是4x4,而装备栏最多只能装4个装备,因此设置二者大小如下:
在这里插入图片描述
在这里插入图片描述
这里两个panel的x轴为800,为什么会是这个数值呢?稍后再解答。
为这两个panel都添加Grid Layout Group组件,使其可以自动布局,然后往这两个panel填充image子对象,分别填充16个和4个,每个image的大小设置为100,通过设置Grid Layout Group中的Spacing属性,可以设置image子对象的间隔,我设置了每个子对象间的间隔为5。
在这里插入图片描述
效果如下:
在这里插入图片描述
现在有了背包和装备栏,还需要有装备,装备可以放在背包或装备栏上,因此,再建立一个大小为4x4的panel,命名为Items,同样设置16个image子对象,为每一个image设置相应的装备图片,需要注意的是,需要先将图片格式从Default修改为Sprite
在这里插入图片描述
效果如下:
在这里插入图片描述
接下来实现panel随鼠标摆动,代码如下,代码逻辑是先获取鼠标在屏幕中的坐标,panel根据鼠标在x轴和y轴的坐标绕x轴和y轴转动相应的幅度。将该脚本分别拖到3个panel上。

using UnityEngine;
public class TiltWindow : MonoBehaviour
{
	public Vector2 range = new Vector2(5f, 3f);
	Transform mTrans;
	Quaternion mStart;
	Vector2 mRot = Vector2.zero;
	void Start ()
	{
		mTrans = transform;
		mStart = mTrans.localRotation;
	}
	void Update ()
	{
		Vector3 pos = Input.mousePosition;
		float halfWidth = Screen.width * 0.5f;
		float halfHeight = Screen.height * 0.5f;
		float x = Mathf.Clamp((pos.x - halfWidth) / halfWidth, -1f, 1f);
		float y = Mathf.Clamp((pos.y - halfHeight) / halfHeight, -1f, 1f);
		mRot = Vector2.Lerp(mRot, new Vector2(x, y), Time.deltaTime * 5f);
		mTrans.localRotation = mStart * Quaternion.Euler(-mRot.y * range.y, mRot.x * range.x, 0f);
	}
}
物品的拖动

这里的代码主要参考了利用UGUI制作的包裹系统(一)
先给出代码:

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class DragItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    private Transform myTransform;
    private RectTransform myRectTransform;
    // 用于event trigger对自身检测的开关
    private CanvasGroup canvasGroup;
    // 拖拽操作前的有效位置,拖拽到有效位置时更新
    public Vector3 originalPosition;
    // 记录上一帧所在物品格子
    private GameObject lastEnter = null;
    // 记录上一帧所在物品格子的正常颜色
    private Color lastEnterNormalColor;
    // 拖拽至新的物品格子时,该物品格子的高亮颜色
    private Color highLightColor = Color.cyan;
    void Start()
    {
        myTransform = this.transform;
        myRectTransform = this.transform as RectTransform;
        canvasGroup = GetComponent<CanvasGroup>();
        originalPosition = myTransform.position;
    }
    void Update()
    {
    }
    public void OnBeginDrag(PointerEventData eventData)
    {
        canvasGroup.blocksRaycasts = false;//让event trigger忽略自身,这样才可以让event trigger检测到它下面一层的对象,如包裹或物品格子等
        lastEnter = eventData.pointerEnter;
        lastEnterNormalColor = lastEnter.GetComponent<Image>().color;
        originalPosition = myTransform.position;//拖拽前记录起始位置
        //originalPosition = eventData.pointerEnter.transform.position;//拖拽前记录起始位置
        gameObject.transform.SetAsLastSibling();//保证当前操作的对象能够优先渲染,即不会被其它对象遮挡住
    }
    public void OnDrag(PointerEventData eventData)
    {
        Vector3 globalMousePos;
        if (RectTransformUtility.ScreenPointToWorldPointInRectangle(myRectTransform, eventData.position, eventData.pressEventCamera, out globalMousePos))
        {
            myRectTransform.position = globalMousePos;
        }
        GameObject curEnter = eventData.pointerEnter;
        bool inItemGrid = EnterItemGrid(curEnter);
        if (inItemGrid)
        {
            Image img = curEnter.GetComponent<Image>();
			lastEnter.GetComponent<Image>().color = lastEnterNormalColor;
            if (lastEnter != curEnter)
            {
				lastEnter.GetComponent<Image>().color = lastEnterNormalColor;
                lastEnter = curEnter;//记录当前物品格子以供下一帧调用
            }
            //当前格子设置高亮
            img.color = highLightColor;
        }
    }
    public void OnEndDrag(PointerEventData eventData)
    {
        GameObject curEnter = eventData.pointerEnter;
        //拖拽到的空区域中(如包裹外),恢复原位
        if (curEnter == null)
        {
            myTransform.position = originalPosition;
        }
        else
        {
            //移动至物品格子上
            if (curEnter.name == "ItemGrid")
            {
                myTransform.position = curEnter.transform.position;
                originalPosition = myTransform.position;
				curEnter.GetComponent<Image>().color = lastEnterNormalColor;//当前格子恢复正常颜色
            }
            else
            {
                //移动至包裹中的其它物品上
                if (curEnter.name == eventData.pointerDrag.name && curEnter != eventData.pointerDrag)
                {
                    
                    Vector3 targetPostion = curEnter.transform.position;
                    //curEnter.transform.position = originalPosition;
                    curEnter.transform.position = new Vector3(originalPosition.x,originalPosition.y,0);
                    myTransform.position = targetPostion;
                    originalPosition = myTransform.position;
                }
                else//拖拽至其它对象上面(包裹上的其它区域)
                {
                    //myTransform.position = originalPosition;
                    myTransform.position = curEnter.transform.position;
                }
            }
        }
		lastEnter.GetComponent<Image>().color = lastEnterNormalColor;//上一帧的格子恢复正常颜色
        canvasGroup.blocksRaycasts = true;//确保event trigger下次能检测到当前对象
    }
    // 判断鼠标指针是否指向包裹中的物品格子
    // <param name="go">鼠标指向的对象</param>
    bool EnterItemGrid(GameObject go)
    {
        if (go == null)
        {
            return false;
        }
        return go.name == "ItemGrid";
    }
}
代码分析及遇到的问题

代码将物品的拖动过程分为三部分,分别是开始拖动,拖动中,释放。并设置了几个变量,分别记录了物品之前的位置,物品格子的颜色(正常/高亮)。在拖动开始时,先记录下物品的位置,使得物品拖到无效位置可以返回。在物品跟随鼠标移动的过程中,鼠标一般是不会检测到它所经过的格子区域的,因为鼠标视线被你的物品挡住了。为了解决这个问题,代码作者给出了解决方案。

这其中的关键就在于一个组件CanvasGroup,其里面有一个属性blocksRaycasts,将它的值设置为false就可以让鼠标透过物品,看到物品下面的格子了。

因此,需要先为每个装备物品添加Canvas group组件:
在这里插入图片描述

在拖动时设置blocksRaycasts = false,使其可以忽略物品并检测到格子。然后就是在移动过程中,若经过了格子区域(装备栏、背包)则会将当前格子区域设置为高亮,在释放装备时,若位置是空位置,则装备会返回原位,若位置是其他装备的位置,则会交换二者位置,若位置是背包中的空位置,则会将装备放入空位置中。
在第三种情况,即“释放装备的位置是背包中的空位置,则会将装备放入空位置中”,一开始遇到了一点问题,这部分的代码如下:

myTransform.position = curEnter.transform.position;

按理说,装备的位置是会到鼠标所指对象的位置,即装备应该会被放入空位置中,但是实际结果是装备放入了背包的正中间,经过分析,这是因为当背包栏为空时,鼠标就会指向整个背包,而不是背包中的某个栏位,这时松开鼠标,装备会回到整个背包的位置,因此装备放入了背包的正中间。为了解决这个问题,我将Item panel的位置移到远处并通过手动调整16个装备image子对象的坐标,将其位置设回原处。这就是刚才Item panel的x轴为800的原因,这是为了避免当背包有空位置时,鼠标会指向整个Item panel。通过这个调整,现在若释放装备的位置是背包中的空位置,则会将装备放入空位置中,因为没有了Item panel的遮挡,此时鼠标指向的是背包中的栏位。

而刚才调整了两个panel的位置,其x轴都为800,为什么还要调整Bag panel的x轴呢?这是因为二者panel位置不同则panel随鼠标摆动的幅度会不同。因此只有设置panel相同,摆动的幅度才会一致。

人物模型和背景

创建一个空对象用来放人物、背景图、主相机。该相机用来渲染人物和背景。结构如下:在这里插入图片描述
为人物添加动画。
在这里插入图片描述

截图

在这里插入图片描述
在这里插入图片描述

游戏视频
项目地址
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值