基于unity3d和leap motion的拼图游戏

最近用unity3d引擎做了一个拼图游戏,会分几次写完,以此作为总结。本文基本查找了网上能查到的所有资料作为参考。也算是大家节省了时间。

目前只完成了拼图部分,leap motion手势控制部分会在后续完成,不过说实话不太看好LM。

首先是整个游戏需要的模块。

一个拼图游戏大致需要如下几个控制模块:碎片显示,碎片随机打乱,随机排序后碎片顺序的合法性检查,移动控制,拼图是否完成的检查。

游戏实现的思路为,建立N*N个plane,通过控制每个plane的材质球贴图偏移形成碎片,最后一个plane使用透明贴图。使用数组纪录每个碎片的偏移位置,和碎片的排列顺序。移动碎片时,plane位置不发生变化,变化的是此plane贴图的偏移(详见第一部分碎片显示)。

一、碎片显示。

此部分可以有几种选择,第一个就是将每部分碎片单独做成图片,比如这个

好处是处理比较方便,可以使用GUI处理,缺点是每个图片都需要前期处理,而且由于前期分片的份数固定,游戏难度不能随意调整,除非每个图都准备很多不同难度的分割后的小图。

第二是使用NGUI中的Atlas,对图集中的sprite信息进行重定义。具体参考小夭的程序。其中用到了UISprite类中的outer结构体,outer记录了sprite在图集中的位置信息。但是在NGUI3.6版本中outer已经不是UISprite的成员变量,是否还能用文中的方法进行修改没有尝试,如果有人尝试请留言告知,在此谢过。

第三是在材质球中设置纹理偏移和缩放,具体做法参考上面cube454517408的帖子。本人也是用的此种方法。

第二和第三种实现方法的好处是可以随意调整图片分成的份数,因此可以很方便的调整游戏难度。

<span style="font-size:18px;">Vector2 offset;\\记录每一块碎片的偏移offset.x = origin.x + piecesLength * j; offset.y = origin.y – piecesLength * i;temp.transform.localPosition = new Vector3 (offset.x*10f,offset.y*10f);texOffset[k].x = j*transform.localScale.x/row;\\计算纹理偏移texOffset[k].y = (row-1-i)*transform.localScale.x/row;temp.renderer.material.mainTextureOffset = texOffset[k];\\设置纹理偏移temp.renderer.material.mainTextureScale = new Vector2(transform.localScale.x/row,transform.localScale.x/row);\\设置纹理缩放</span>

因为图源为正方形,所以只考虑了将其分为N*N块的分法,因此偏移和缩放的计算较为简单。

void Display(){for (int i = 0; i < pieces.Length; i++) {pieces[i].renderer.material.mainTextureOffset = texOffset[squence[i]];pieces[i].renderer.enabled = isReander[squence[i]];}}void Update ()的最后调用Display(),isRender数组保存了该碎片贴图是否显示,texOffset数组保存了每个碎片贴图的偏移位置。

碎片显示部分就这么多内容,下面是碎片打乱算法。

二、碎片打乱算法

这里有两种不同的思路,第一种是小夭采用的,将正确排列的碎片随机移动若干次,以达到打乱碎片顺序的目的,该方法的好处是,用这种方法生成的随机序列一定可以还原,缺点是实现起来较为复杂。具体实现见小夭的文章。

第二种思路是采用洗牌算法,使用一个数组保存第N个碎片的纹理偏移,从第一个碎片开始与随机一个碎片交换内容,直到数组结束。算法实现起来比较简单,但是并不一定保证可以正确还原。

void Shuffle(){Random.seed = System.Environment.TickCount;for (int i = 0; i < squence.Length – 1; i++) {int temp = squence[i];int randomIndex = Random.Range(0, squence.Length-1);squence[i] = squence[randomIndex];squence[randomIndex] = temp;} }三、逆序和检验

通过洗牌算法得到的随机序列,并不一定可以还原成初始的顺序,这是因为在洗牌时改变了数组的逆序和。参加百度百科(不可还原的拼图)……?url=2ajCBRlh6Ox1I1SPK8gEayd-aAaCITNNQjVSA09qDHDLXZM9Ndrp-thdWdjg-Xt_sRk3PCABt-3LUPDKfTZDy_

lemene对此进行了证明,喜欢数学证明的同学请见

shaomn的讲解比较容易明白,

对于数组squence[],定义其逆序和为sum += (i – j) * (squence [i] – squence [j]) > 0 ? 0 : 1;对于初始序列其逆序和为0,左右移动碎片时sum不变,上下移动时sum +2、-2或不变,但是无论怎样移动,逆序和奇偶性是不变的。因此在洗牌算法之后,检验数组逆序和是否为偶数即可。

bool Check(){int sum = 0;for (int i = 0; i < squence.Length; i++)for (int j = 0; j < i; j++)sum += (i – j) * (squence [i] – squence [j]) > 0 ? 0 : 1;return sum % 2 == 1;}

四、移动控制

移动控制有两种思路,第一种是鼠标点击想要移动的碎片,检测碎片周围是否有空位置,如果有交换位置。第二种是按方向键,检测空位置周围是否有可以向按键方向移动的碎片,如果有交换位置。完全可以同时实现,本文只实现了第二种,,因为要结合leap motion,第二种操作方式和手势控制比较接近。

if (Input.GetKeyDown ("left")) {MoveLeft();}if (Input.GetKeyDown ("right")) {MoveRight();}if (Input.GetKeyDown ("down")) {MoveDown();}if (Input.GetKeyDown ("up")) {MoveUp();}其他移动函数与之类似,不一样的是判断条件。首先找到空白碎片(也就是开局时最后一个碎片)当前在的碎片队列中的顺序,将之与要移动的碎片交换在队列中的位置。void MoveLeft(){int last,temp;last = FindLastPiece();if(last%row < row-1){temp = squence[last];squence[last] = squence[last+1];squence[last+1] = temp;}}int FindLastPiece(){int i=0;while(squence[i]!=squence.Length-1){i++;}return i;}使用leap motion进行手势控制在实现时采用了比较简单的逻辑,只适用于本游戏,如果同时需要进行其他手势的判断则需要设计其他的约束条件。此处逻辑为判断手掌移动速度,超过某个方向的最大速度则判断为使碎片向该方向移动。leap motion 与unity结合开发的设置不再介绍。为了减少误判,设置了一个控制是否启用手势控制的变量update,在检测到手势的0.5s内暂停手势控制。Leap.Hand hand = LeapControl.Hand;if (hand != null && update) {if (hand.PalmVelocity.x > minVelocity){MoveRight();update = false;Invoke("SetUpdate",0.5f);}if (hand.PalmVelocity.x < -minVelocity){MoveLeft();update = false;Invoke("SetUpdate",0.5f);}if (hand.PalmVelocity.y > minVelocity){MoveUp();update = false;Invoke("SetUpdate",0.5f);}if (hand.PalmVelocity.y < -minVelocity){MoveDown();update = false;Invoke("SetUpdate",0.5f);}} 五、游戏结束检测穷则思变,差则思勤!没有比人更高的山没有比脚更长的路。

基于unity3d和leap motion的拼图游戏

相关文章:

你感兴趣的文章:

标签云: