cocos游戏案例,cocos creator游戏案例

http://www.itjxue.com  2023-01-14 20:07  来源:未知  点击次数: 

如何使用Cocos2d-x完美呈现捕鱼3

捕鱼达人3与前两部相比,技术上有哪些突破和创新?

捕鱼达人3作为前两代成功作品的续作,我们需要从玩法到技术,全面超越前代。所以在技术上,我们主要做了下面一些突破和创新:

1)鱼的3D渲染。 跟2D渲染相比,3D渲染可以让鱼看起来更加立体和真实。 3D骨骼动画可以让动作更加丰富和平滑。另外我们还为鱼增加了表情动作,让鱼儿更具个性化。

2) 纯3D路径。 前两代的2D路径在我们新的3D场景下,表现力要弱很多。为此我们设计了全新的3D路径编辑器,鱼的游动不仅仅在一个面上游动,而是一个立体空间,这样鱼的游动过程会更加平滑和真实。

3) 丰富的武器表现效果。 以前的武器效果,我们一般是结合帧动画和粒子效果实现,在捕鱼3中,我们的武器很多是通过 shader 实现的,让效果更加绚丽和富于变化。比如闪电武器,泡泡武器等。

4) 3D的后处理和灯照效果。大家可以看到我首屏模拟海洋的效果,就用到相关技术。还有鱼身上的波光效果。

2.《捕鱼达人3》使用了最新的Cocos2d-x中3D功能,它有哪些突出的亮点?请举几个游戏里的具体案例说明

1)继承Cocos2d-x的设计思路,只要具备Cocos2d-x的编程经验, 在使用和理解上完全无障碍,可以立马上手写代码。

2) 丰富的材质系统,可以自定义模型不同渲染效果。 比如:我们的鱼身上的渲染效果已经电鳗鱼的闪电效果

3) 强大的模型编辑器,可以自定义模型的材质

3. 引擎3D功能使用的时候感觉和2D部分区别在哪?

设计理念和开发流程和2D差不多

4. 游戏里面的动画效果,是UI实现的,还是Cocos引擎的3D效果?比如那个漩涡,是不是使用引擎的3D效果可以做很多不同的漂亮效果呢?

渲染时用3D的材质系统实现的, 由于材质系统的扩展能力很强,做出各种漂亮的效果完全没问题。

5. Cocos2d-x的3D技术稳定性如何?是否已经到了可以大规模市场化应用的阶段?

挺好的,我们Android版本的适配度高达92%以上。

6. 引擎粒子效果的效率一直备受争议,请问捕鱼达人3有没有专门做优化?

我们对同一种粒子做了批次处理。另外,还通过脚本, 严格控制了粒子的个数,不能超过某个上限值。

7. 在《捕鱼达人3》开发过程中,遇到哪些困难和挑战?你们是如何克服的?

游戏开发都会面临一个资源的管理问题,就是资源如何从美术制作,在到程序使用以及后续的维护。捕鱼3的资源类型比较多。2D层有图片,ui资源,动画资源,字体资源,粒子资源。 3D层又涉及3D模型,材质,纹理,shader等。另外,还有track,策划配置文件等。所以种类非常多。 还因为捕鱼3的适配性要求比较高,所以,我们有些资源按照分辨率有划分了几大类。 还附加一条,资源还分不同的语言版本。 上线之后,资源本身还需要跟随发布版本一起维护。所以中间的问题非常多。 在我们项目中会严格对资源进行规范, 包括命名,目录结构。资源如何从美术维护的目录流动到程序使用的目录等。另外还维护了一套基于ant+python的脚本来去驱动资源的流转。 实现一键生成程序所需要的资源。 无须人工的干预, 非常便捷。

另外我们还有一键打Windows,iOS,Android各种程序包的脚本,方便做版本的daily build。

8. 《捕鱼达人3》的背后一定有一支强大的团队,包括多方面能力的配合,能否分享一下团队协作和管理的经验?

就我们研发而言, 其实最重要是前期做好开发规范,然后打造融洽的团队氛围,明确每个人的开发任务,大家随着项目的发展一起往前进步。

9. 最后,对其它有志进入手游行业或者在摸索中前行的手游开发者,您能否给一些建议?

虽然很多媒体都在高喊:手游已是一片红海。 但这个市场远没有饱和,移动设备的增量仍然很大。而且区别于端游市场的客户端大作,页游市场的各种网游。手游的游戏类型更加多样化,更加丰富。休闲的,棋牌的,各类网游,都有成功的案例。所以,这个市场更加灵活,更适合小团队创造奇迹,仍然有很多的机会。另外从技术上讲,由于存在Cocos2d-x这种几乎处于垄断地位的引擎, 手游也是一个更加开放的市场。 网上有一大堆的学习资料和游戏源码, 只要你有点计算机基础,入门还是很容易的。重要的是自己能够静下心来多看多写些代码,争取独立发布一款产品。我见过不少人自己做一点东西都没坚持下来,做一半就放弃掉。其实在多坚持下,离成功就近了很多。

为什么都用cocos引擎开发游戏?

1)版本比较成熟和稳定。cx 迭代到现在也有好几年时间了,实际应用案例也很多,是一个比较可靠的库。插件也比较多。

2)性能比较高。(ch 使用 jsbind 也能获得较高性能,但我对于这块并不熟悉,而且这块资料也不多,因此不作比较。)

3)debug 更方便。IDE 的成熟,对 debug 很有帮助,此外市面上对于 cx 的实际应用很多,因此网上搜索解决方案资料比较多。

如何用Cocos引擎打造次世代3D画质‘游戏大观

从Cocos 2d-x 3.0起我们已经可以在游戏中使用3D元素。Cocos引擎推出3D功能的时间不算太迟,我们已经可以看到越来越多的手机上能流畅地渲染3D游戏,而且这些机型正在成为主流。在最近两年我们可以看到,高端手机游戏从2D转到3D的倾向很明显。许多游戏开发商试图在竞争激烈的红海里占有一席之地,那么选择开发3D游戏或许会是一个强有力的竞争手段。

上面的视频是我的下一款游戏作品《Food of the Gods》。这游戏使用了Cocos 2d-x 3.3,视频是从我iPhone上录制的实际运行效果。在这篇文章里我将要介绍我是如何制作它、如何把它跑在cocos引擎上的。对于熟悉cocos官方提供的3D示例游戏 《Fantasy Warrior》的开发者,将会看到以下一些主要不同点:

1. 光照贴图(Light Mapping):你将看到每件物体都有被照亮并且投射阴影。光影效果的质量是由你的3D工具软件决定的,用3D软件能烘焙出复杂的光效,包括直接光照,反射光照,以及阴影。

2. 顶点合并(Vertex Blending):请注意看路、草地和悬崖交接的地方,看不到任何可见的接缝。

3. 透明遮罩(Alpha Masks):灌木如果没有透明遮罩就跟纸片一样。

4. 滤色叠加的公告板(Billboards):增加一些光束和其他环境的效果。

所有的模型都是用一个叫Modo的3D 软件建模制作的,贴图则是使用Photoshop。关于3D模型的制作和贴图的绘制在此就不再赘述,网上已经有很多教程,在此主要介绍下跟Cocos 2d-x有关的部分。

模型网格和贴图(Meshes and Textures)

如下图所示,每个模型的贴图都是由几个256 x 256或者更小的贴图组成的。同时你也会注意到我把所有的小图片都合在了一张贴图上,这是减少GPU绘制次数(draw call)最简单的方法之一。贴图是从 或者网上找的。

为了把这些图片拼接起来,我使用的是Photoshop的补偿滤镜(offset filter)然后在接缝的地方用修复画笔来做一些自然的过渡。为了获得一种油画的视觉效果我会先使用cutout滤镜(注意:cutout滤镜也会使得png格式图片的压缩效果更好),然后在需要的地方绘制一些高光和阴影的效果。我发现如果直接拿照片当贴图的话,当你把它尺寸缩小的时候会出现图像噪点。

另一种方案是为每一个模型网格制作一整张独立的贴图。当网格比较小或者摄像机不是很靠近网格的时候这种方法是可行的。如果你的photoshop技术过硬的话,出来的效果会更好。附带的好处是,因为只使用一张贴图因此只有一次GPU绘制调用。但我不建议采用这种方法来制作第一人称射击游戏(FPS)中的建筑,因为当你走得很靠近建筑物的时候,贴图分辨率过低的问题就会显露出来。我不喜欢用这种整张贴图方法,因为这实在太费时耗力了。这个场景的制作花了我足足四天时间。

光照贴图(Light Maps

当你做好模型和贴图之后,现在就可以来烘焙光照贴图了。Cocos 2d-x目前还不像Unreal或Unity一样在官方编辑器里提供烘焙光照贴图的功能,但是别失望,大部分的制作3D模型的软件都可以烘焙光照贴图,并且效果比市面上任何游戏引擎的效果还好。首先,在你的3D工具软件里,先给场景打好灯光,照亮场景,然后为每份网格制作第二张UV map。每份网格的表面都必须被映射在0到1范围内的UV 平面上。这听起来好像很复杂且耗时,但在Modo里这是非常简单的。我先后使用 “Atlas map”的UV 工具和“Pack UV”工具,这两个工具会自动将网格展开成一个相当不错的排布图。

这些都完成之后,设置3D工具软件的渲染器为“只渲染烘焙的光照”,然后开始渲染。当然了,如果你想做一些环境光遮罩的效果也是可以的。

你也可以使用一些分辨率较低的光照贴图。有时候这样的效果反而会看起来更好,因为相互混叠的模糊像素会让阴影看起来更柔和。上面的这些建筑都映射到一张512 x 512的光照贴图上。整个场景总共使用了4 张512 x 512的光照贴图。请确保每个小图块之间有一定的空隙,且让你的渲染范围比这些图块的边界多出几个像素。这样可以防止当较低的mip-maps(一种纹理采样)起作用时黑边出现在网格周围的角落里。

最后一点听起来像是3D技术的行话。如果是对Texture Packer熟悉的话,那么其中的“Extrude”值起到的作用就是刚刚我所描述的。对贴图的边缘接缝做一些涂抹处理,这样在精灵之间就不会有那些烦人的缝隙了,那些缝隙在这里会变成多边形边缘的黑边。

如果你想牺牲内存和包大小来提高性能的话,你可以把颜色和光照信息都烘焙到一张贴图上并避免共同使用一张光照贴图。但是这样做的话,同样的像素密度,贴图的大小至少得翻一倍。这完全取决于你个人、以及你游戏的要求。

接下来,添加顶点颜色。我在地形上提供了顶点颜色,这可以让着色器在合成悬崖顶上的草地贴图时,不会有任何可见的接缝。下图中涂成白色的顶点部分可以合成你指定的贴图。在这个例子里实际上我只使用红色通道,当然了根据实际需要你可以使用4个通道(RGBA)去合成不同的贴图。

最后,我把整个场景分成了很多独立的网格(mesh):每个建筑都有自己独立的网格,地形独立一个网格,水也是独立一个。带透明遮罩的贴图也会有一个网格——比如视频中看到的植物叶子和小旗子。我这样做有两个原因,首先,让地形、建筑、水和带透明遮罩的贴图各自使用不同的着色器。其次,我们打算通过不渲染摄像机范围外的对象来减少性能开支。很重要的一点是摄像机会根据网格的包围盒来决定对象是否可见,因此尽量把网格弄成小块,这样包围盒会比较小。

导出

完成了模型和贴图之后,我们需要把每个mesh导出为一个.fbx文件。幸运的是,大多数的3D建模软件都支持这个功能。Autodesk为此格式提供了一个免费SDK。但不幸的是,Modo 701在导出fbx格式时会出现相当多的错误。因此我必须自己写一些脚本来保证第二组贴图坐标和顶点颜色的正确导出。你可以从我个人网站上的“Modo Scripts”部分下载这个导出脚本。搞定fbx之后,你将需要用到Cocos 2d-x自带的fbx-conv.exe命令行工具,它位于Cocos 2d-x根目录的/tools下。

fbx-conv.exe -a your_mesh_name_here.fbx

使用“-a”参数后,工具会同时导出mesh的二进制文件(.c3b)和文本格式文件(.c3t)。文本格式的文件非常的有用,你可以利用它来查看所有的东西是否被正确导出,但千万不要把它放到resource目录下。如果所有的都被正确地导出的话,你将在c3t文件的开头看到以下的内容:

“attributes”: [{

“size”: 3,

“type”: “GL_FLOAT”,

“attribute”: “VERTEX_ATTRIB_POSITION”

}, {

“size”: 3,

“type”: “GL_FLOAT”,

“attribute”: “VERTEX_ATTRIB_NORMAL”

}, {

“size”: 2,

“type”: “GL_FLOAT”,

“attribute”: “VERTEX_ATTRIB_TEX_COORD”

}, {

“size”: 2,

“type”: “GL_FLOAT”,

“attribute”: “VERTEX_ATTRIB_TEX_COORD1″

}]

注意VERTEX_ATTRIB_TEX_COORD1这个属性。如果没有它光照贴图将无法显示。如果你导出了一张带顶点颜色的mesh,你也应该要看到一个类似的属性才行。还有一点很重要,贴图的坐标也必须按正确的顺序才行。我通常采用的是第一个tex_coord是瓦片贴图,最后一个tex_coord是光照贴图。使用Modo的话,uv maps会按照字母顺序排列。

着色器(Shaders)

我花了很长的一段时间来搞懂GLSL和着色器,但正如编程中经常遇到的,有时候一个点通了,其他的就都好理解了。一旦理解了其中的原理,你便会发现着色器真的很简单。如果你不只是想用Cocos 2d-x来把贴图套到模型网格上的话,你需要学会如何写着色器。目前Cocos 2d-x没有Unreal那样好用的着色器可视化编辑器(visual shader editor),所以我们只能自己动手焊代码。

本节我将讲解我为视频中的游戏场景所写的着色器,并说明我做了什么、为什么这样做。如果你对着色器已经非常熟悉了,那么可以快速跳过本节。

首先,先来看一下如何将着色器应用到模型网格上。

这段代码摘自Cocos 2d-x的测试集cpp-tests工程。如果你用不同的着色器来加载大量的meshes,那么最好根据功能来进行,这样可以避免冗余。那么现在我们只关心如下的代码段,来看下这个着色器。

GLProgram* shader =GLProgram::createWithFilenames(“shaders/lightmap1.vert”,”shaders/lightmap2.frag”);

GLProgramState* state = GLProgramState::create(shader);

mesh-setGLProgramState(state);

Texture2D* lightmap =Director::getInstance()-getTextureCache()-addImage(“lightmap.png”);

state-setUniformTexture(“lightmap”,lightmap);

“lightmap1.vert”是顶点着色器(vertex shader)。如果将其应用到网格上,那么每个顶点的每一帧都将执行这个操作。而“lightmap2.frag”是片段着色器(fragment shader),网格上贴图的每个像素的每一帧都将执行这个操作。我不太确定为什么将其命名为“片段着色器”,我一直认为应叫做“像素”着色器(pixel shader)。从这段描述,我们可以很容易理解为什么大量着色器指令会降低帧率,尤其是你用片段着色器的话。

接下来我们详细地分解顶点着色器:

attribute vec4 a_position;

attribute vec2 a_texCoord;

attribute vec2 a_texCoord1;

这些属性是由渲染器提供的。“a_position”是顶点的位置。“a_texCoord” 和 “a_texCoord1”对应你那两个UV坐标。还记得在.cbt文本格式文件中开头部分的“VERTEX_ATTRIB_TEX_COORD”么?这些值与属性对应起来了。你可以在渲染器中获取更多其他的属性,包括顶点法线(vertexnormal)和顶点颜色(vertex color)。请在cocos引擎的CCGLProgram.cpp中查看完整属性列表。

varying vec2 v_texture_coord;

varying vec2 v_texture_coord1;

“varying”值将被传到片段着色器中(fragment shader)。片段着色器所需要的任何变量前都需要添加“varying”限定符。这个例子中,我们仅需要知道这两个贴图的坐标。

void main(void)

{

gl_Position = CC_MVPMatrix * a_position;

v_texture_coord.x = a_texCoord.x;

v_texture_coord.y = (1.0 – a_texCoord.y);

v_texture_coord1.x = a_texCoord1.x;

v_texture_coord1.y = (1.0 – a_texCoord1.y);

}

设置顶点位置,拷贝贴图的坐标给varying values,这样片段着色器就可以使用这些值。现在我们一起来分解片段着色器。

#ifdef GL_ES

varying mediump vec2 v_texture_coord;

varying mediump vec2 v_texture_coord1;

#else

varying vec2 v_texture_coord;

varying vec2 v_texture_coord1;

#endif

声明从顶点着色器传递过来的“varying” 值

uniform sampler2D lightmap;

还记得在将着色器应用到网格时所使用的 state-setUniformTexture(“lightmap“,light map); 语句么?这个值就是对应语句中的那个贴图。

void main(void)

{

gl_FragColor = texture2D(CC_Texture0, v_texture_coord) *(texture2D(lightmap, v_texture_coord1) * 2.0);

}

这个语句设置像素颜色。首先你会注意到从未声明过的 CC_Texture0变量。Cocos 2d-x中有大量可在着色器中使用的默认统一变量。再次强调,可在CCGLProgram.cpp中查看完整属性列表。这个例子中,CC_Texture0对应在3D模型中所应用到网格中的贴图。texture2D命令会在给定的贴图坐标中去查找贴图的像素颜色和透明度。它会返回一个包含了那个像素的RGBA值的vec4值 。所以这里我会在UV1中查找到瓦片贴图的颜色值,然后在UV2中查到光照贴图的颜色值,最后把两个值相乘。

你应该注意到了我先是把光照贴图的颜色值两两相乘了。因为贴图颜色值范围为0.0-1.0,所以很显然,如果用白色值vec4(1.0, 1.0, 1.0, 1.0)去乘中间灰值vec4( 0.5, 0.5, 0.5,1.0 ),那么你仍是得到一个中间灰值vec4( 0.5, 0.5, 0.5,1.0 )。将两个值相乘可以使贴图更亮,同时也可以使贴图更暗,这将使你获得一个很好的可变的亮度范围。

Cocos Creator开发游戏消灭星星——星星消除

上一篇文章写了星星生成的逻辑,详情请看 Cocos Creator开发游戏消灭星星——星星生成

星星消除是发生在用户点击之后,所以需要处理用户触摸操作。在上一篇制作星星预制时有提及,在脚本组件 starCtr.js 的start函数里监听触摸。

消除星星是消除上下左右相连的星星,所以需要根据用户点击的星星找到其他相连的星星。在Utils中增加方法needRemoveList:

现在来完成触摸处理逻辑:

通过用户点击的星星坐标找到与其相连的星星们,然后发射delete_stars事件,通知地图消除星星。关于监听和发射时间参考官方文档 监听和发射事件 。

在matrixCtr.js的onLoad方法中添加事件监听

先添加几个属性来记录消除数据

在回调函数中处理消除逻辑

上一篇 说过,动画和特效主要放在节点 ActionRoot 中处理。如图,combo特效就在combNode节点中播放。

asset、atlasAsset分别存储骨骼动画资源,combName中存储骨骼动画的名字,和资源数组一一对应,_anim是dragonBones组件。

playComb即是播放特效的方法。

combCtr是脚本组件matrixCtr中的属性,即是场景中ActionRoot节点的脚本组件。

将需要消除的星星对应的坐标清空(赋值-1)

按规则星星是一个一个消除的,所以bomb会递归调用,直到所有星星都消除。在消除星星的同时,有分数计算和动画逻辑。

星星的移除是在方法 bombStar 中处理的,在创建星星的时候使用了对象池,所以移除时把它重新放入对象池。

在移除星星的同时,伴随有星星爆炸的特效。 starParticle 是一个预制,层级很简单,在一个空节点中,添加Particle System组件和脚本组件particleCtr。

Particle System组件设置自动移除,在属性检查器中勾选 Auto Remove On Finish 选项。

我们知道一次消除星星方块越多,得分越高。

分数动画有几种:

动画在actionCtr.js中处理:

因为分数也会被频繁的创建和移除,所以也使用了对象池,分数的预制制作后面介绍。

与单个方块的分数动画一样,消除总得分动画:

层级结构很简单,都是空节点下加一个Label节点。父节点上都有一个脚本组件partScore、totalScore。

脚本也很简单,setScore方法给Label赋值。

与单个分数不同的,总得分的Label动画使用Creator的Animation编辑器制作。所以,预制中需要在节点label中添加Animation组件,在这里我们在添加一个脚本组件totalScoreLabel,这个脚本主要处理Animation动画的事件回调方法。

cocos creator制作一个简单的拼图游戏

简介使用cocos creator2.x版本制作的拼图游戏, 移动图块, 还原最终的样子

开始, 我们分析一下这个游戏的几个要点 1, 是如何将一张完整的图片分成3*3 5*5个小图, 并且这些小图要可以保存自己的位置信息, 等一些属性 2, 是如何表示小图合集的位置, 用什么数据结构保存, 且怎么让图片逻辑, 与数据逻辑对应 3, 是如何判断游戏结束?

上图是游戏的场景结构, 可以看到2.x版本和1.x版本有一些区别, 最突出的就是新增了一个默认的carmera结点 这个我会在别的帖子内仔细介绍, 这里不再多说?

首先我们解决第一个问题, 如何将一个大图切成一个个小图, 并让这个小图保存一些属性值, 我们先新建一个脚本, puzzlePiece.ts,?

const{ ccclass, property } = cc._decorator;

@ccclass

exportclassPieceextendscc.Component{

@property({

type: cc.Texture2D

})

privatetexture: cc.Texture2D =null;

publicoriCol: number;

publicoriRow: number;

publiccurCol: number;

publiccurRow: number;

publicisBlank:boolean;

publicgetisRight(){

returnthis.curCol ===this.oriCol this.curRow ===this.oriRow;

}

publicinit(col: number, row: number, colNum: number, colWidth: number){

this.oriCol = col;

this.oriRow = row;

this.curCol = col;

this.curRow = row;

let sprite =this.node.addComponent(cc.Sprite);

// 升级2.0后setRect失效

// sprite.spriteFrame = new cc.SpriteFrame(this.texture);

// let rect = sprite.spriteFrame.getRect();

let rect = cc.rect(0,0,this.texture.width,this.texture.height);

let newRectWidth = rect.width / colNum;

let newRectHeight = rect.height / colNum;

let newRectX = col * newRectWidth;

let newRectY = (colNum - row -1) * newRectHeight;

let newRect = cc.rect(newRectX, newRectY, newRectWidth, newRectHeight);

// sprite.spriteFrame.setRect(newRect);

sprite.spriteFrame =newcc.SpriteFrame(this.texture, newRect);

this.node.width = colWidth;

this.node.height = colWidth;

this.isBlank =this.oriCol === colNum -1this.oriRow ===0;

if(this.isBlank) {

this.node.active =false;

}

}

}

将小图看做一个类, 使用texture保存图片纹理,在通过new cc.SpriteFrame(this.texture, newRect);获取某一个矩形区域内的纹理, 这样就可以把一张大图切成一张张小图, 并添加几个属性, 保存位置和其他的信息?

那么开始解决第二个问题, 我们可以采取二维数组的数据结构保存数据信息private pieceMap: Array; 讲一个个切好的小图保存在内, 然后随机移动(为了保证图片可以还原, 我采取的方法是将一个正确摆放好的小图数组, 按照游戏规定的移动方式随机移动1000次), 这样图片一定可以被还原

import{ Piece } from"./PuzzlePiece";

import{ PuzzleScene } from"./PuzzleScene";

const{ ccclass, property, executeInEditMode } = cc._decorator;

@ccclass

// @executeInEditMode

exportclassPuzzleBoardextendscc.Component{

@property(cc.Prefab)

privatepiecePrefab: cc.Prefab =null;

@property(cc.Integer)

privatecolNum: number =5;

@property(cc.Integer)

privatecolSpace: number =5;

privatecolWidth: number =0;

privatepieceMap: Array;

privateblankPiece: Piece =null;

privatepuzzleScene: PuzzleScene =null;

init(puzzleScene: PuzzleScene) {

this.puzzleScene = puzzleScene;

this.addListeners();

}

publicreset(colNum?: number){

this.colNum = colNum;

this.colWidth = (this.node.width -this.colSpace * (this.colNum +1)) /this.colNum;

this.node.removeAllChildren();

this.pieceMap = [];

for(let x =0; x

this.pieceMap[x] = [];

for(let y =0; y

let pieceNode = cc.instantiate(this.piecePrefab);

this.node.addChild(pieceNode);

pieceNode.x = x * (this.colWidth +this.colSpace) +this.colSpace;

pieceNode.y = y * (this.colWidth +this.colSpace) +this.colSpace;

this.pieceMap[x][y] = pieceNode.getComponent(Piece);

this.pieceMap[x][y].init(x, y,this.colNum,this.colWidth);

if(this.pieceMap[x][y].isBlank) {

this.blankPiece =this.pieceMap[x][y];

}

}

}

this.shuffle();

}

privateshuffle(){

for(let i =0; i 1000; i++) {

let nearPieces =this.getNearPieces(this.blankPiece);

let n = Math.floor(Math.random() * nearPieces.length);

this.exchangeTwoPiece(this.blankPiece, nearPieces[n]);

}

}

privateonBoadTouch(event: cc.Event.EventTouch){

let worldPos = event.getLocation();

let localPos =this.node.convertToNodeSpaceAR(worldPos);

let x = Math.floor((localPos.x -this.colSpace) / (this.colWidth +this.colSpace));

let y = Math.floor((localPos.y -this.colSpace) / (this.colWidth +this.colSpace));

this.puzzleScene.onBoardTouch(x, y);

}

publicmovePiece(x, y):boolean{

let piece =this.pieceMap[x][y];

let nearPieces =this.getNearPieces(piece);

for(let nearPiece of nearPieces) {

if(nearPiece.isBlank) {

this.exchangeTwoPiece(piece, nearPiece);

returntrue;

}

}

returnfalse;

}

publicjudgeWin():boolean{

for(let x =0; x

for(let y =0; y

if(!this.pieceMap[x][y].isRight) {

returnfalse;

}

}

}

this.blankPiece.node.active =true;

returntrue;

}

privategetNearPieces(piece: Piece): Array {

let nearPieces = [];

if(piece.curCol 0) {// left

nearPieces.push(this.pieceMap[piece.curCol -1][piece.curRow]);

}

if(piece.curCol

nearPieces.push(this.pieceMap[piece.curCol +1][piece.curRow]);

}

if(piece.curRow 0) {// bottom

nearPieces.push(this.pieceMap[piece.curCol][piece.curRow -1]);

}

if(piece.curRow

nearPieces.push(this.pieceMap[piece.curCol][piece.curRow +1]);

}

returnnearPieces;

}

publicexchangeTwoPiece(piece1: Piece, piece2: Piece){

this.pieceMap[piece2.curCol][piece2.curRow] = piece1;

this.pieceMap[piece1.curCol][piece1.curRow] = piece2;

[piece1.curCol, piece2.curCol] = [piece2.curCol, piece1.curCol];

[piece1.curRow, piece2.curRow] = [piece2.curRow, piece1.curRow];

[piece1.node.position, piece2.node.position] = [piece2.node.position, piece1.node.position];

}

privateaddListeners(){

this.node.on(cc.Node.EventType.TOUCH_END,this.onBoadTouch,this);

}

privateremoveListeners(){

}

}

解决第三个问题, 其实这个很简单, 因为我们已经在小图类中保存了小图本身的位置信息, 我们只需要,每次移动图片 都遍历一个二维数组判断其是否在正确的位置, 判断成功, 结束游戏? 点击链接加入群聊【cocos/unity交流群】

声明 :发布此文是出于传递更多知识以供交流学习之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与我们联系,我们将及时更正、删除,谢谢

(责任编辑:IT教学网)

更多

推荐Mail服务器文章