天天看点

如何用Cocos Creator做一个胶体(果冻效果)小游戏(三)+JavaScript

游戏实现部分

一、游戏关卡实现

这里就只放两个关卡(其余大部分重复性,还不好看:( ),关卡设计图的草纸找不到了,就不放出来了。

初始情况下,玩家控制的胶体方块角色出现在屏幕左下角的盒子中,上方的白字是游戏当前关卡的倒计时,左上方的房子图标是返回主界面的按钮,右上方是本关卡的重新开始按钮。在这个关卡中,四周的边界被刚体覆盖,地图中有障碍物的箱子以及刚体性质的绿球,以及右下角的通关旗帜。在第一关,主要是帮助玩家熟悉各种操作,了解胶体角色的运动方式和操控效果,观察胶体角色与刚体球的碰撞效果。总的来说,第一关是所有关卡中,最简单的一关。

如何用Cocos Creator做一个胶体(果冻效果)小游戏(三)+JavaScript

这关是特别设置的特殊关卡。因为游戏引用的第三方LiquidFun库也有对流体的模拟,所以又增加了一个粒子组,用来模拟水粒子,制造出水池。水池的特点是,距离水面越远,受到的阻力就越大。在本关中,玩家需操控胶体角色,进入模拟的水池中,通过水粒子的阻挡,到达右下角的终点。此关的设置,不仅仅是为了提高游戏的可玩性,还能在游戏中看到胶体和流体粒子的碰撞效果,是设置较耗费时间的一个关卡。

如何用Cocos Creator做一个胶体(果冻效果)小游戏(三)+JavaScript

二、详细实现

2.1 定义重力

要创建物理世界,我们首先需要定义物理世界的重力值,定义一个力向量作为物理世界的重力值。其次,因为CocosCreator游戏引擎的结构限制,为了保证world为全局变量,能在各个场景都能使用同一个物理世界,所以将物理世界的定义放入word.js中,并将其作为插件导入项目中。代码实现如下:

var gravity = new b2Vec2(0, -10);//方向沿着Y轴竖直向下,大小为10N

window.world=new b2World(gravity,true);

var world=window.world;
           

2.2 物理刚体的创建

见下图所示。在物理世界已存在的情况下,所有刚体创建的瞬间即具有物理特性,成为物理世界的一部分。

如何用Cocos Creator做一个胶体(果冻效果)小游戏(三)+JavaScript

2.3 物理刚体的创建

刚体又被叫做物理实体,是物理世界的基本单元。要想创建一个刚体,就需要先创建一个刚体定义类来定义这个刚体,代码实现如下:

var bd = new b2BodyDef();
var body = world.CreateBody(bd);//创建刚体
           

通过刚体定义类,我们能够设置刚体的初始位置,类型,旋转角度等其他属性。刚体类型有b2_staticBody、b2_kinematicBody和b2_dynamicBody三种,详细内容见下面的表格所示。

刚体定义类内容表:

刚体定义类 类型内容
b2_kinematicBody 运动学物体,具有无限大的质量,会进行移动并与自身的移速有关。且不受力的作用,不能和同类或静态物体发生碰撞。
b2_dynamicBody 动态物体,是拥有有限的非零质量。此类物体可以和任何物体碰撞。
b2_staticBody 静态物体,不能移动,但是拥有无限大的质量。此类物体不能和同类或运动学物体碰撞。

在刚体定义类设置完成后,我们就可以创建出刚体。然后通过创建形状定义类来设置刚体的形状。形状定义类包括b2PolygonShape、b2Circle、b2EdgeShape和b2ChainShape,详细内容见下面表格所示。

形状定义类内容表:

形状定义类 类型内容
b2PolygonShape 多边形形状。只能表示凸多边形,可以通过添加节点的方式定制多边形形状,默认顶点数不超过8个。
b2Circle 圆形形状。圆形不能是空心的。
b2EdgeShape 边缘形状。边缘形状由多条线段组成,不能与边缘形状与链形状发生碰撞。
b2ChainShape 链形状。链形状是一个自由形式序列的线段,具有双面碰撞,但不能与边缘形状与链形状发生碰撞。它提供了一种高效的方法创建多边形和环,链形状不能自身交叉,那样可能会出现未定义行为。

在形状定义完成后,即可将其设置为刚体设置形状,刚体即创建完成,代码实现如下:

body.CreateFixtureFromShape(shape, 1);
           

函数的第一个参数为形状定义类,第二个参数为该刚体的密度。

在刚体创建完毕后,因为物理世界的坐标与我们使用的CC世界里的坐标是不一样的。所以我们在创建刚体后,还要将坐标转换,代码实现如下:

bd.position = new b2Vec2(position.x,position.y);
           

2.4 物理胶体和流体的创建

物理胶体的创建与刚体的创建类似。不同的是,我们需要先创建一个粒子系统定义类。粒子系统定义类中定义了粒子系统中单个粒子的半径,摩擦力等属性。定义完成后即可创建粒子系统,代码实现如下:

var psd = new b2ParticleSystemDef();
var particleSystem = world.CreateParticleSystem(psd);
           

粒子系统创建完成后,需要创建粒子系统分组。我们在游戏中,只使用一个粒子系统。但是因为要再创建水粒子,所以我们需要两个粒子组,对两个粒子组进行不同的渲染。粒子系统分组定义类中必须定义分组的形状,分组形状与刚体形状通用,决定了粒子分组在物理世界的初始形状与位置。创建粒子系统分组定义类,代码如下:

var pd = new b2ParticleGroupDef();
pd.shape = shape;
Let group=particleSystem.CreateParticleGroup(pd);
           

粒子系统成功创建后,通过使用游戏引擎自带的渲染组件graphics,即可使粒子系统的粒子获得颜色。然后我们要在视觉效果上,让粒子系统具有胶体或者是流体的效果,粒子类型可以通过b2ParticleFlag来进行定义,定义完成后,即可在物理世界中创造出胶体效果。粒子类型更详细的内容见下面表格所示,代码实现如下:

pd.flags=b2_elasticParticle|b2_staticPressureParticle|b2_repulsiveParticle|b2_elasticParticle ;
           

粒子类型内容表:

粒子类 类型内容
b2_waterParticle 水颗粒。
b2_springParticle 粒子系统通过伸展可以恢复原状。
b2_elasticParticle 粒子系统随着变形可以恢复原状。
b2_powderParticle 粒子之间没有各向同性的压力
b2_repulsiveParticle 粒子之间具有很高的排斥力。
b2_staticPressureParticle 压缩性较差。

物理胶体在物理世界创建完成后,我们需要对其进行实时绘制。LiquidFun提供了一个接口,帮助我们实时获取粒子系统各粒子的当前坐标。下面中的particles是一个浮点数型数组,以两个浮点数为单位,存储了所有粒子的当前坐标值。代码实现如下:

var particles = particleSystems.GetPositionBuffer();
var maxParticles = particles.length;
           

但是想要在关卡已有一个胶体角色的场景中,再创建一个水池的流体效果,就还需要创建一个粒子组。代码实现如下:

var pd1 = new b2ParticleGroupDef();//粒子组定义包含构造粒子组所需的所有数据
pd1.shape = shape1;//一组形状,其中将添加粒子
pd1.flags=b2_waterParticle;//流体效果
           

在绘制判断时,我们还需要要获得不同粒子组的粒子数,这样才能对两个粒子组分别进行渲染绘制,代码实现如下:

for (let i=system.particleGroups[1].GetBufferIndex()*2; i<maxParticles;i += 2);
           

2.5 物理胶体的控制及销毁

在胶体角色创建完成之后,我们要实现对角色的控制,就要通过监听键盘事件来控制胶体角色。定义事件监听函数Listener,并将其加入到事件管理器中。在每次更新时,根据不同的按键,在不同的方向上给胶体角色,在物理世界内给胶体的粒子系统施加力,使其在游戏画面中能产生移动效果,代码实现如下:

cc.eventManager.addListener(listener,self.node);
if(world.particleSystems.accLeft){
    group.ApplyLinearImpulse(new b2Vec2(-1000,0));}
if(world.particleSystems.accRight){
    group.ApplyLinearImpulse(new b2Vec2(1000,0));}
if(world.particleSystems.accUp){
    group.ApplyLinearImpulse(new b2Vec2(0,5000));}
if(world.particleSystems.accDown){
    group.ApplyLinearImpulse(new b2Vec2(0,-5000));}
           

游戏的物理世界是在world.js中创建的,所以我们只有一个物理世界。但是因为我们不只有一个关卡,在每个关卡加载时,我们都会创建一个新的粒子系统,并对其进行渲染绘制。为了避免在同一个场景出现两个胶体的情况,我们在每次场景转换时都要对创建的刚体、胶体和流体进行销毁,代码实现如下:

while (world.joints.length > 0) {
    world.DestroyJoint(world.joints[0]);}
while (world.bodies.length > 0) {
    world.DestroyBody(world.bodies[0]);}
while (world.particleSystems.length > 0) {
    world.DestroyParticleSystem(world.particleSystems[0]);}
           

2.6 后台服务器

本游戏后台服务器使用Node.js搭建,监听端口设置为7456。对于玩家发送的服务器请求,服务器共有以下几个模块处理:

注册模块:

注册模块负责游戏的注册功能。浏览器端发送的数据集包括玩家申请的用户名与密码。服务器会先查询数据库判定用户名是否被占用,如果被占用则会返回一个失败信号给浏览器端。否则,向数据库中插入新玩家的数据,并向浏览器端返回成功信号。

登录模块:

登录模块负责游戏的登录功能。浏览器端发送的数据集包括玩家填写的用户名与密码。服务器会先查询数据库判定用户名是否存在,如果已经存在,再判断用户密码是否符合,如果密码正确则返回该用户的用户名与最大通关数。反之,返回的数据为null。

得分更新模块:

得分更新模块负责游戏的关卡得分更新。当玩家通过关卡时,系统自动发送得分更新请求给服务器。如果该玩家第一次通过该关卡,该关卡的得分数据直接插入数据库中。反之,服务器会对当前得分与数据库中记录的该玩家在该关卡中的最高得分进行比较,新的得分如果更高,则更新数据库中的得分数据。

排行榜模块:

排行榜模块负责游戏的排行榜查询。当玩家查看排行榜时,浏览器端会发送排行榜查询请求。服务器得到关卡号后,查询数据库,从高到低列出前20条数据,存储在数组中返回给浏览器端。数组中的每个元素包含对应玩家的用户名与其在该关卡中的得分。

如果对你有帮助的话,就点个赞吧:)

系列已结束,其余部分见:

(一)https://blog.csdn.net/packdge_black/article/details/103464278

(二)https://blog.csdn.net/packdge_black/article/details/106242955

(四)https://blog.csdn.net/packdge_black/article/details/107466701

继续阅读