Unity2D中的三种卡牌拖拽模型

背景

        系个人经验和网络学习总结。

         前不久正好在做卡牌类型游戏,遂对拖拽卡牌的实现方式做了分析和记录(复制和抄袭)。

以下模型脚本的共性是都继承MonoBehaviour,是针对GUI的操作,都需要挂载在想要拖拽的GUI上。

正文

一:Drag接口(一般式)

        摘要:通过IDragHandler接口实现。

using UnityEngine;
using UnityEngine.EventSystems;

public class Drag_1 : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
{
    RectTransform rectTransform;
    private void Start()
    {
        rectTransform = GetComponent<RectTransform>();
    }
    
    public void OnBeginDrag(PointerEventData eventData)
    {
        //to do
    }
    public void OnDrag(PointerEventData eventData)
    {
        Vector3 pos;//用以接收世界空间中的点
        if (RectTransformUtility.ScreenPointToWorldPointInRectangle
            (
            rectTransform, //要在其中查找点的 RectTransform
            eventData.position, //屏幕空间位置
            eventData.pressEventCamera, //与屏幕空间位置关联的摄像机
            out pos //世界空间中的点
            )
            )
        {
            rectTransform.position = pos;
        }
    }
    public void OnEndDrag(PointerEventData eventData)
    {
        //to do
    }
}

        应是一目了然,主要逻辑在IDragHandler接口提供的OnDrag()方法中实现。剩下两个接口可以省略。比较需要关注的应是RectTransformUtility这个类及其中方法。

RectTransformUtility - Unity 脚本 API

下面专注了解下这个ScreenPointToWorldPointInRectangle方法。字面意思,“将位于矩形中的屏幕坐标转换为世界坐标”,而这个“位于矩形中”的“矩形”应该是带有RectTransform组件的物体。比如一般的GUI物体。因此该方法的第一个参数我们传入一个RectTransform,也就是卡牌的RectTransform。

第二第三参数应传入被转换屏幕坐标和及其关联摄像机。这里借助了eventData填入对应参数。

最后一个参数为转换后生成的世界坐标,因此前面声明了一个Vector3 pos以接收这个坐标。

具体规范请看官方文档。

效果:

        因为直接使物体坐标与鼠标坐标配对,所以在点击并拖拽开始时,物体坐标中心会直接瞬移到鼠标位置。

二:Pointer接口(偏移式)

        摘要:通过IPointerDownHandler, IPointerUpHandler等系列接口与周期函数实现。

using UnityEngine;
using UnityEngine.EventSystems;

public class Drag_2 : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
    public bool isDrag;//用来判断是否应该跟随鼠标
    private float Xoffset;//XY偏移
    private float Yoffset;
    private Vector2 mousePos;//鼠标坐标(像素/屏幕坐标)
    private Vector2 targetPos;//目标坐标
    private void Update()//或者FixedUpdate(),依情况而定
    {
        Drag();
    }
    private void Drag()
    {
        if(isDrag)
        {
            mousePos = Input.mousePosition;
            targetPos = new Vector2(mousePos.x + Xoffset,mousePos.y + Yoffset);//加上偏移量以恒等
            transform.localPosition = targetPos;
        }
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        mousePos = Input.mousePosition;

        //每次点击后,开始计算当次的偏移
        //每次拖拽过程(Down -> Up)只计算一次偏移
        Xoffset = transform.localPosition.x - mousePos.x;
        Yoffset = transform.localPosition.y - mousePos.y;
        
        isDrag = true;//按下鼠标则开始跟随
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        isDrag = false;//松开鼠标结束跟随
    }
}

        这套模型的大致思路就是:

1.在按下鼠标时,计算坐标系偏移量。

2.在按住并拖动鼠标时,让物体位置与矫正后的鼠标位置相同。

3.在松开鼠标时,结束拖拽。

         我个人自己在理解偏移量的时候经常绕晕,所以下面自己总结下:

在这里的偏移量是指两个坐标系的偏移量,分别是鼠标对应的屏幕坐标系和物体对应的世界坐标系。对于unity坐标系的概念就先不详述了,简单理解就是,假设你有一块1920X1080的屏幕,屏幕上一片空白,在unity场景中,你看到的正中心的那个点设为世界坐标原点,即(0,0)(2D世界),但是在屏幕坐标系中,位于正中心的点是长宽分辨率的各一半,即(960,540),因此虽然在我们看来是同一个点,但是在不同坐标系下却是不同表达。偏移就是坐标系的偏移

按上述所说偏移量就是(-960,-540),等等,为什么不能是正的?为什么不能用屏幕坐标减去世界坐标?

好吧,其实是可以的。只要确保等式不变即可。

啥意思呢,就比如说上面代码中的:

Xoffset = transform.localPosition.x - mousePos.x;

移项得到:

transform.localPosition.x = mousePos.x + Xoffset;

再加上Y的偏移就能构成:

targetPos = new Vector2(mousePos.x + Xoffset,mousePos.y + Yoffset);

即最终目标坐标。

如果一开始是用 Xoffset = mousePos.x - transform.localPosition.x,那么最终结果就应该是

targetPos = new Vector2(mousePos.x - Xoffset,mousePos.y - Yoffset);

        另外需要注意,每次拖拽开始只计算一次偏移,这样才能保持鼠标和物体相对静止。效果也不会像模型一里面一样突然瞬移。当然具体效果应该与实际需要为准。

效果:

三:EventTrigger组件(懒汉式)

        摘要:通过为对象添加EventTrigger组件快速实现各种操作。

using UnityEngine;
//仅适用于Canvas的Render Mode为Overlay下
public class Drag_3 : MonoBehaviour
{
    public void Drag()
    {
        transform.position = Input.mousePosition;
    }
    
}

就没了...... 当然不要忘了给EventTrigger加上。然后引用的话就直接引用自身。

        这里为什么只适用于Overlay呢,因为在这个条件下GUI的位置坐标是和屏幕坐标一致的,因此可以直接用position而不是localPosition。可以留意到Canvas的Render Mode为Overlay时,画布中心已经自动位于屏幕中心,即GUI们的世界坐标与屏幕坐标相同了。或者说已经算好帮我们计算好偏移量了。

至于上面的第二个模型为什么采用localPosition是因为我在做测试的时候采用了另一种画布渲染方式Camera,所以才要根据摄像机自己计算偏移量,不过像模型二这样子写不论什么渲染方式都是可以正常运行的。

        这个的效果和模型一一致,这里就不做演示了。其实模型三可以包含前两个模型的用法,具体实现方式也取决于在EventTrigger中选择了哪些事件。

总结

        三个模型的实现都离不开Unity的内置接口,虽然可以自己写独立的接口不依赖于Unity框架,但是既然有了为什么不用呢?因为这是关于卡牌游戏的三种模型,其实算上碰撞体碰撞物之类的还可以有更多方式,比如 OnMouse 那些方法。实际上具体的思路区别集中体现在不同的渲染方式和设计要求上。

比方说做一个拼图游戏,只能上下左右移动拼凑出正确的图案。这时就不能选择模型一那样的,鼠标点击后物体中心位置会瞬移到鼠标位置,这样就无法限制拼图移动了。而应该选择模型二那样的,以偏移量为核心的设计思路。再加上碰撞体或其他约束,来实现效果。

        反正因地制宜吧。

纯属个人笔记,不具备可信度

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Moweiii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值