拼图设计思路与源码二[自动吸附篇]
通过上一篇的画图篇,我们就可以使拼图在游戏舞台上随意的铺放了,但是到目前为止还不会吸附,也就是当两张图案正确的相邻两个拼图拼在一起的时候不会吸在一起.
我们先来回顾一下上一节中所定义的碎片类Piece
package net.conanlwl
{
import flash.display.Sprite;
public class Piece extends Sprite
{
private var m_row:uint;
private var m_col:uint;
public var Up:Object;
public var Right:Object;
public var Buttom:Object;
public var Left:Object;
private var m_virtualX:Number;
private var m_virtualY:Number;
public function Piece(_row:uint,_col:uint,_virtualX:Number,_virtualY:Number)
{
this.m_row = _row;
this.m_col = _col;
m_virtualX = _virtualX;
m_virtualY = _virtualY;
}
public function get virtualX():Number{
return m_virtualX;
}
public function get virtualY():Number{
return m_virtualY;
}
public function get Row():uint{
return m_row;
}
public function get Col():uint{
return m_col;
}
}
}
其中的virtualX,virtualY,Row,Col都是只读的,也即是说,在声明之后,这四个值就是固定的了.
我们给一张拼好的图的每个小碎片做一个编号.如图1:

图1
那么A的Row与Col就为0,0;相对拼图的左上角的坐标virtualX=0,virtualY=0
再L的Row与Col就为2,1;相对拼图的左上角的坐标就为virtualX=1*m_pieceW,virtualY=2*m_pieceH
再M的Row与Col就为2,2;相对拼图的左上角的坐标就为virtualX=2*m_pieceW,virtualY=2*m_pieceH
所以,当我们图中的碎片打乱以后,这些数值都是不会变的.我们在吸附判断中,先不考虑旋转以后的吸附判断.假设所有的图片都是正放的,都是0度.

图2
当我们拿着L靠近M并产生碰撞时(如图2),我们要如何判断他们是正确的相对位置呢?
使用他们的行列差值来判断?也就是Row相等,且L的Col等于M的Col-1?当然,这在两上碎片都是一小块的时候,这种方法是成立的,但如果当我们L与M拼好,再拿个N或者K来拼时,就未必能成了.
所以我们采用坐标差值法来比较.L与M在舞台中都有属于自己的坐标.分别设为L.x,L.y与M.x,M.y,当我们移动L或M时,他们的坐标就相应的变化.但L.virtualX,L.virtualY与M.virtualX,M.virtualY是不变的.
因此,我们认为,当L与M的坐标差与他们的绝对虚拟坐标差相等或小于一个误差值时,吸附成立.
var inaccuracy:Number = 8; //吸附的误差值
if(Math.abs((L.x - M.x) - (L.virtualX - M.virtualX)) <=inaccuracy ){//当前X坐标的差值,与他们的虚坐标的差值接近时
if(Math.abs((L.y - M.y) - (L.virtualY - M.virtualY)) <=inaccuracy){
//吸附条件成立
}
}
当我们用鼠标拿着L与M碰撞并吸附条件成立时,就将L从舞台转移到M里面去,也即M.addChild(L),并修正L的坐标与M能够准确拼合.至此L与M成为一个整体M,如图3

图3
注意,因为我们将L移到M里面时,只修正L的坐标,所以添加后的大M碎片的坐标原点还是跟原来一样,仍然如图3中箭头所指的点.所以当我们把箭头所指的点移到舞台的0,0点的时候,大M碎片的坐标才是0,0

图4
接差我们拿着H与M靠近发生碰撞并判断吸附条件成立时(注意M.x,M.y指的是哪个点),H又加入到了M之中.相反,如果我们拿着大M与H靠近发生碰撞并吸附条件成立时,M将先自己里面的L拿出来搬到H里面去,最后再自己搬到H里面去,也即先H.addChild(L)再H.addChild(M),此时大碎片即为H
接着,我们先把大H放一边,将R与W拼一起,成为一个大W.如图5

图5
然后再拿着大H与大W靠近,如图6

图6
仍然按照下面的公式判断吸附条件,注意H.x,H.y与W.x,W.y指的是哪两个点的坐标
if(Math.abs((H.x - W.x) - (H.virtualX - W.virtualX)) <=inaccuracy ){//当前X坐标的差值,与他们的虚坐标的差值接近时
if(Math.abs((H.y - W.y) - (H.virtualY - W.virtualY)) <=inaccuracy){
//吸附条件成立
}
}
如果是拿H碰W,则将H里的三个小碎片一片一片地搬到W里面去,反之,则W搬H.如果此时仍用Row与Col的差值来判断,就会发现吸附条件不成立了.因为H的Row与Col为1,2,而W的则为4,2,所以虽然他们处于同列,但行数却差了4-1=3.所以行列差值比较法就不成立了.
为什么要将大H的三个小碎片一片一片地搬到大W里面,而不直接将大H搬到大W里面去就行了呢?这是为了保证舞台中碎片的层次关系只有二层而为避免对角吸附也会成立的情况做准备(如图7).也即大碎片里面就是小碎片了,小碎片里不会再有小碎片.

图7
按照我们前面的碰撞检测吸附判断条件,上面的情部也是成立的,所以他也会被吸附,所以我们必须保证W以下W里面的小碎片和D以及D下面的小碎片(如果有)之间,必须至少有两个小碎片是相邻的.吸附才会真正的成立
//检查p1与p2里是否相邻连接,避免出现对角连接
private function testHit(p1:Piece,p2:Piece):Boolean{
if(Math.abs(p1.Row - p2.Row) + Math.abs(p1.Col - p2.Col) == 1) return true;
var p:Piece;
var i:uint = 0;
var j:uint = 0;
for(i=0;i<p2.numChildren;i++){ //p1自身与p2的child碰撞
p = p2.getChildAt(i) as Piece;
if(Math.abs(p1.Row - p.Row) + Math.abs(p1.Col - p.Col) == 1) return true;
}
for(i=0;i<p1.numChildren;i++){ //p2自身与p1的child碰撞
p = p1.getChildAt(i) as Piece;
if(Math.abs(p2.Row - p.Row) + Math.abs(p2.Col - p.Col) == 1) return true;
}
for(i=0;i<p1.numChildren;i++){//p1的child与p2的child碰撞
for(j=0;j<p2.numChildren;j++){
p = p1.getChildAt(i) as Piece;
var pp:Piece = p2.getChildAt(j) as Piece;
if(Math.abs(pp.Row - p.Row) + Math.abs(pp.Col - p.Col) == 1) return true;
}
}
return false;
}
所以绕了这么大的一个圈,就是为了避免出现这样的一个问题,所以如果你的游戏是允许对角拼接的话,那么你大可不用这么麻烦,仍然按照上面的吸附判断公式,然后直接A.addChild(B)或者B.addChild(A)就行了..无论是大A碎片或者是小A碎片,总之将一个大碎片直接搬到另一块碎片里面去就行了.
至此我们整个吸附判断的过程就完成了.所以当我们扩展到多角度的吸附判断时,只是上面的公式中,x与y,virtualX与virtualY之间的相对关系反生了一些变化而已.下面就是扩展多角度吸附判断的公式,
private function testPos(p1:Piece,p2:Piece):Boolean{
var inaccuracy:Number = 8; //吸附的误差值
if(p1.rotation == p2.rotation){
//trace(p1.rotation);
if(p1.rotation==0 && Math.abs((p1.x - p2.x) - (p1.virtualX - p2.virtualX)) <=inaccuracy ){//当前X坐标的差值,与他们的虚坐标的差值接近时
if(Math.abs((p1.y - p2.y) - (p1.virtualY - p2.virtualY)) <=inaccuracy){
if(testHit(p1,p2)){
moveChild(p1,p2);
m_pieceLayer.addChild(p2);//移到最上面
}
}
}
else if(p1.rotation==90 && Math.abs((p1.x - p2.x) - (p2.virtualY - p1.virtualY)) <=inaccuracy ){//当前X坐标的差值,与他们的虚坐标的差值接近时
if(Math.abs((p1.y - p2.y) - (p1.virtualX - p2.virtualX)) <=inaccuracy){
if(testHit(p1,p2)){
moveChild(p1,p2);
m_pieceLayer.addChild(p2);//移到最上面
}
}
}
else if(p1.rotation==180 && Math.abs((p2.x - p1.x) - (p1.virtualX - p2.virtualX)) <=inaccuracy ){//当前X坐标的差值,与他们的虚坐标的差值接近时
if(Math.abs((p2.y - p1.y) - (p1.virtualY - p2.virtualY)) <=inaccuracy){
if(testHit(p1,p2)){
moveChild(p1,p2);
m_pieceLayer.addChild(p2);//移到最上面
}
}
}
else if(p1.rotation==-90 && Math.abs((p1.x - p2.x) - (p1.virtualY - p2.virtualY)) <=inaccuracy ){//当前X坐标的差值,与他们的虚坐标的差值接近时
if(Math.abs((p1.y - p2.y) - (p2.virtualX - p1.virtualX)) <=inaccuracy){
if(testHit(p1,p2)){
moveChild(p1,p2);
m_pieceLayer.addChild(p2);//移到最上面
}
}
}
}
return true;
}
任何物体在舞台中旋转的角度都在-180~180之间.所以我们必须在旋转一个物体之后再加上360度,才会避免因为P1的角度为-180度与P2的角度为180度拼接不成功的状况.
在本DEMO中,物体绕着鼠标旋转使用了L4CD(Http://L4cd.Net)的绕任意点旋转类iPointRegister.
评论Feed: http://www.conanlwl.net/Feed/Comment/182.aspx
引用链接: http://www.conanlwl.net/TrackBack/Save/182.aspx
加载评论中...
加载引用中...
加载相关文章中...
