一、碎片拖拽
1.透视变换和逆透视变换
鼠标存在于屏幕中属于二维坐标系,而我们想要拖动的碎片存在于三维坐标系内,想要进行拖拽操作必须将二者置于同一坐标系下,这里我们使用逆透视变换的方法,将鼠标光标变换至三维坐标系中。需要注意的是用过下面这种方法得到的光标坐标只是当摄像机处于Orthographic(正交相机)模式下投影得到的世界坐标,当相机处于Perspective(透视相机)模式下,我们将有新的方法进行鼠标坐标的定位,详情请参考第六章-噬星者。
//将屏幕上的光标坐标投影到世界坐标
mouseToWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition);
2.被点击处即为光标的位置
鼠标坐标与碎片坐标统一到同一个坐标系中以后,我们尝试通过拖拽的方法让碎片跟随鼠标进行移动。①坐标突变方案:我们的思路是当鼠标按下时将鼠标的坐标复制到碎片的transform.position,虽然十分简单但是这种方法存在一个缺点,每次拖拽都会导致碎片位置发生瞬间移动,这是因为碎片的中心坐标会从原始位置在鼠标按下的那一帧时间内变化到鼠标的位置,这种情况可能会对玩家操作产生不好的体验,我们对游戏体验追求的是尽可能地流畅顺滑,所以我们需要寻找一种方法让玩家点击碎片任意位置实现拖拽。②坐标不突变方案:这款游戏中碎片的点击判断是通过Unity网格碰撞器实现的,因此点击碎片任意位置都可以发生碰撞,玩家不用关心碎片的哪个位置被点击,在点击的瞬间鼠标位置可能不在碎片的中心,因此存在一定偏差,我们称两个坐标之间的差距为位移(offset),上一个方法中正是因为offset为0才导致碎片的坐标急剧变化使碎片突然移动,知道这个原因我们就想办法固定这个offset,也就是在鼠标点击的瞬间计算偏移值,在拖拽过程中将鼠标坐标加上这个偏移值就可以得到碎片的位置也就是:碎片位置 = 鼠标位置 + 偏移 => 偏移 = 碎片位置 - 鼠标位置。
private void begin_dragging()
{//开始拖拽
IS_PUTDOWN = false;
mouseToWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition);//将屏幕上的光标坐标投影到世界坐标
if (!IS_MOVE_TO_CENTER)
{
offset = transform.position - mouseToWorld;
offset.y += UPY;
return;
}
}
private void do_dragging()
{//正在拖拽
mouseToWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition);//将屏幕上的光标坐标投影到世界坐标
if (!IS_MOVE_TO_CENTER)
{
transform.position = mouseToWorld + offset;
return;
}
transform.position = new Vector3(mouseToWorld.x, mouseToWorld.y, transform.position.z);
}
二、碎片随机放置
1.打乱图片顺序
将n张碎片赋予数字名称,创建一维数组,将其分为长度为根号n(只入不舍)的正方形,计算出中心点坐标将各个点的坐标都储存在数组中,通过随机旋转使得整体图形转动并记录次旋转角度,将随机后的碎片依次赋予计算好的坐标时加上随机数,让碎片分散于整个画面。碎片随机通过对数组中每一个数进行随机赋值,每次得到一个随机数,并判断现存数组中是否含有此数,如有则重新随机,如果没有则将此随机数一次存储在数组中,直至储存在数量为根号n(只入不舍)的平方的数组中,如果数组中为0则代表此节点碎片为空。
2.点击事件
通过为按钮添加点击事件,每次点击则重新执行随机函数。
private void arrrandom(ref int[] strarr) //通过迭代将碎片顺序填入数组
{
int k = 0;
for(int i = 0;i < strarr.Length; i++)
{
k = UnityEngine.Random.Range(1,9);
for(int j = 0;j < i; j++)
{
if (k == strarr[j]) //如果已排好的数据中有此数,则重新寻找
{
arrrandom(ref strarr);
return;
}
}
strarr[i] = k;
}
}