上一篇忘说道,该系列严格意义上对纯新手不太友好,因为不会较系统的过一遍怎么开发拼图小游戏,更多的只是提下思路,说一下里面的关键实现方法。如果纯新手有需求麻烦私聊在下。
上一篇说道,做一款拼图小游戏,需要解决的基础问题有:
1)图片的处理,是一开始就将图片用切图软件切好,还是通过代码来实现图片切割效果;
2)块图片的顺序打乱;
3)如何判断图片拼回了原样。
那么顺序了解下上面说的问题。
一、图片的处理,是一开始就将图片用切图软件切好,还是通过代码来实现图片切割效果。
首先是根据项目需求来看,如果需求是:
“要求同样的图片,可以按照2x2,3x3等格式进行切割”,那程序能实现的话就尽可能程序实现,可以稍微减轻美术的压力(当然你和美术有仇就随意了)
“块图片不是矩形,则是带凹凸形状的”,这种如果不要求随机性的话,那美术能实现就让美术实现吧。(当然如果要求随机性,那就还是只能程序上了,至于怎么实现,shader,mask之类的套路可能可行,菜鸡本鸡没试也没想过)
功能怎么实现,很大程度上要根据策划要求来写,除非开发者有丰富经验,否则不建议自己脑补这个功能以后可能会怎么扩展,不但增加了开发难度,还可能发现这个功能最后还被舍弃了。
在这个demo中,我们假设块图片是规则的矩形,故我们直接采用“代码切割图片”的方法来实现功能。
通过百度(百度大法好),得知CocosCreator中,SpriteFrame类中,提供了一个方法
通过传入一个Rect类型参数,该方法可以直接修剪图片的可显示范围。从而实现我们的目的,下面附上代码实现。
interface SfGridInfo {
x: number,
y: number,
width: number,
height: number
sf: cc.SpriteFrame,
}
/** 切割SpriteFrame */
private _cutSpriteFrame ( sf: cc.SpriteFrame, col: number, row: number ) {
let texture = sf.getTexture();
// 并不知道什么尺寸情况下会报错
// let maxValue = 2048;
// if ( texture.width > maxValue || texture.height > maxValue ) {
// return console.error ( 'texture size over size', texture.width, texture.height );
// }
let rect = sf.getRect();
let count = col * row;
let totalWidth = rect.width
let totalHeight = rect.height;
let gridWidth = totalWidth / row;
let gridHeight = totalHeight / col;
let half_gridWidth = gridWidth / 2;
let half_gridHeight = gridHeight / 2
let curX = rect.x;
let curY = rect.y;
let curNodeX = -rect.width / 2;
let curNodeY = rect.height / 2 - half_gridHeight;
let sfGridInfoArr: SfGridInfo[] = [];
for ( let i = 0; i < count; i++ ) {
let sf = new cc.SpriteFrame();
let tmpGridWidth = gridWidth;
let tmpGridHeight = gridHeight;
// 若分割后取的像素位置超出了原图像素位置 会有报错 但下面这种处理有很大问题 有空再看
// if ( curX + gridWidth > totalWidth ) {
// tmpGridWidth = totalWidth - ( curX + gridWidth );
// }
// if ( curY + gridHeight > totalHeight ) {
// tmpGridHeight = totalHeight - ( curY + gridHeight );
// }
sf.setTexture ( texture );
sf.setRect ( cc.rect ( curX, curY, tmpGridWidth, tmpGridHeight ) )
curNodeX += half_gridWidth;
let sfGridInfo: SfGridInfo = { x: curNodeX, y: curNodeY, width: gridWidth, height: gridHeight, sf: sf }
sfGridInfoArr.push ( sfGridInfo );
if ( ( i + 1 ) % row == 0 ) {
curNodeX = -rect.width / 2;
curNodeY -= gridHeight;
curX = rect.x;
curY += gridHeight;
}
else {
curNodeX += half_gridWidth;
curX += gridWidth;
}
}
return sfGridInfoArr;
}
如果希望返回值是cc.Node类型,或cc.Rect类型,可以自行修改,此处问题不大。那么进入下一个问题。
二、块图片的顺序打乱。
既然是拼图小游戏,单纯把图片切割完却不打乱,是想让玩家欣赏画作是么。
打乱图片顺序也有好几种方式:
1)两两交换位置;
2)随机显示在屏幕范围内
3)按照随机顺序,插在一个Layout节点中。
4)其它。(开发世界这么大,实现功能的套路自然不少,至于功能好不好用,最后会不会被开除,就让时间来检验吧。)
在这里为了节省时间,我直接采用了方法一,两两交换位置,下面附上代码实现。
/** 打乱节点间的顺序 */
private _randomPos ( nodeArr: cc.Node[], nodeStatusArr?: boolean[] ) {
if ( nodeArr.length <= 1 ) {
return;
}
let count = nodeArr.length;
for ( let i = 0; i < count; i++ ) {
let go = nodeArr [ i ];
let randomIndex = Math.floor ( Math.random() * count );
if ( i == randomIndex ) {
continue;
}
if ( !nodeStatusArr [ i ] || !nodeStatusArr [ randomIndex ] ) {
continue;
}
let randomNode = nodeArr [ randomIndex ];
let frontPos = go.getPosition();
let backPos = randomNode.getPosition();
go.setPosition ( backPos );
randomNode.setPosition ( frontPos );
}
if ( this._isFinished ( nodeArr ) ) {
this._randomPos ( nodeArr, nodeStatusArr );
}
}
_randomPos方法中的参数二,是用来判断哪一个索引的块图片不进行处理的,其目的是避免不可移动的块图片不在原本位置而导致游戏无法正常进行。
三、如何判断图片拼回了原样。
实现套路依旧有好几个,比如:
1)一维数组
2)二维数组
3)三维数组位置排序并判断全部块图片左下角到右上角的宽高差。
方法一和方法二这种,适用于块图片只能移动到指定位置上,每当块图片发生位置移动或交换时,需要同时替换对应数组位置上的值。
因此这个项目中我用的是方法三,每次玩家松开手指时,判断一次结果,若符合条件则游戏完成。
private _sortPos ( sortArr: cc.Node[] ) {
let tmpArr = [];
for ( let i = 0; i < sortArr.length; i++ ) {
tmpArr.push ( [ i, sortArr [ i ].getPosition() ] );
}
tmpArr = tmpArr.sort ( ( a, b ) => {
if ( a [ 1 ].x > b [ 1 ].x ) {
return 1;
}
if ( a [ 1 ].x < b [ 1 ].x ) {
return -1;
}
return 0;
} )
tmpArr = tmpArr.sort ( ( a, b ) => {
if ( a [ 1 ].y < b [ 1 ].y ) {
return 1;
}
if ( a [ 1 ].y > b [ 1 ].y ) {
return -1;
}
return 0;
} )
for ( let i = 0; i < tmpArr.length; i++ ) {
if ( tmpArr [ i ] [ 0 ] != i ) {
return false;
}
}
return true;
}
private _isFinished ( checkArr: cc.Node[] ) {
let isFinish = this._sortPos ( checkArr );
let rect = this._sf.getRect();
let sfWidth = rect.width;
let sfhHeight = rect.height;
if ( isFinish ) {
let firstChild = checkArr [ 0 ];
let minX = firstChild.x;
let maxX = firstChild.x;
let minY = firstChild.y;
let maxY = firstChild.y;
for ( let i = 1; i < checkArr.length; i++ ) {
let child = checkArr [ i ];
if ( child.x < minX ) {
minX = child.x;
}
if ( child.x > maxX ) {
maxX = child.x;
}
if ( child.y < minY ) {
minY = child.y;
}
if ( child.y > maxY ) {
maxY = child.y;
}
}
let width = maxX - minX + firstChild.width;
let height = maxY - minY + firstChild.height;
if ( width > sfWidth + 1 || height > sfhHeight + 1) {
return false;
}
return true;
}
return false;
}
至此,开发一款拼图小游戏所涉及的基础问题就讲完了。如果有讲的不详细或者不对的地方,请告知下我。我就不再重复检查自己写了啥了,为啥?因为我饿。