基于CocosCreator的切水果小游戏(二)

    在之前完成开始界面和刀光的制作后,接下来完成水果的分开、掉落部分。另外,在完成一个炸弹的火星特效,完成后大概像这个样子:

    切开水果效果:

    炸弹的火星效果:

    

    

    对于水果分开的话,在已有的素材中已经有切割完成的了,因此,只需要将切割后的两部分水果,制成预制体Prefab就行了,同时需要去调整好它们的锚点,以方便旋转的效果更好一些。

            

可以先让完整sandia的锚点处于正中位置,然后拖动切开后的两部分sandia1和sandia2,让它们拼起来并且与sandia重合,同时让这三个prefab的锚点重合,并且position也相等,这样后面写代码的时候就方便一点。

    新建脚本fruit.js,用它来作为水果类:

    主要是声明一些需要用到的属性,同时在最后保存了一个游戏主体start的引用。(类似于官方的“摘星星”例子)

//fruit.js

cc.Class({
    extends: cc.Component,

    properties: {
        fruit_type: null,    //水果种类
        position_x: null,    //水果生成位置
        position_y: null,    //
        //fruit_per_roate: 0,    //水果自转角度
        fruit_all: {        // 完整水果的prefab
            default: null,
            type: cc.Prefab
        },
        fruit_apart_1: {    // 水果切开后的部分1
            default: null, 
            type: cc.Prefab
        },
        fruit_apart_2: {    // 切开后的部分2
            default: null,
            
            type: cc.Prefab
        },
        fruit_all_curent_angle: 0,    // 完整水果实时角度
        fruit_all_per_roate_angle: 0,    //完整水果自转角度 
        fruit_all_x_speed: 0,    //完整水果x和y速度
        fruit_all_y_speed: 0,
        fruit_all_y_acc: 0.15,    //完整水果y轴掉落加速度

        fruit_apart_x_speed: 0,    //部分水果...
        fruit_apart_y_speed: 0,
        fruit_apart_y_acc: 0.17,
        fruit_apart_roate_angle: 150,
 
        isBroken: false,    //是否完整

        isActivate: true,
        isAvailable: true,

        game_manager: null,    //这里保存了游戏主体脚本start的一个引用

    },
    
    //...

});

   

    水果切开的逻辑,实际上就是移除原先的完整水果,在同一位置生成切开后的两部分水果,并且在旋转的同时完成一个下落的过程。

 

//fruit.js

//当水果被切开时
apart(){
    this.fruit_apart_1.x = this.fruit_all.x;
            this.fruit_apart_1.y = this.fruit_all.y;
            this.fruit_apart_2.x = this.fruit_all.x;
            this.fruit_apart_2.y = this.fruit_all.y;
            
            this.fruit_apart_1.angle = this.fruit_all.angle;
            this.fruit_apart_2.angle = this.fruit_all.angle;

            let direction = Math.sign((this.fruit_all_curent_angle - 90) * (270 - this.fruit_all_curent_angle%360)+1e-10);
            this.fruit_apart_x_speed = (1.5 * Math.random() + 3.5) * direction;
            this.fruit_apart_roate_angle = (150 * Math.random() + 50) * direction;
            
            this.fruit_all.parent.removeChild(this.fruit_all);
            this.fruit_all = null;
            // this.game_manager.node.removeChild(this.fruit_all);

           
            this.fruit_apart_1.runAction(cc.rotateBy(1,this.fruit_apart_roate_angle));
            this.fruit_apart_2.runAction(cc.rotateBy(1,-this.fruit_apart_roate_angle));

},

   

    这里面主要是一些简单的数学计算,首先要让切开水果的两部分的位置和角度和原先完整水果一样,接着,根据具体角度,设置这两部分水果在x轴上的运动方向(当然也可以不管角度,就直接将它们按固定的方向飞)。并且使用runAction来完成它们各自的旋转。

   最后,在update里面,对它们的x和y方向的位置做更新就可以了。x轴做匀速运动,y轴做向下加速运动。

 

//fruit.js

update(dt){

    this.fruit_all.x += this.fruit_all_x_speed;
    this.fruit_all_y_speed -= this.fruit_all_y_acc;
    this.fruit_all.y += this.fruit_all_y_speed;

}

 

    这样便完成水果的分开、下落部分了。接下来只需要添加判定水果是否被切到的逻辑就行了,简单的做法可以是对完整水果prefab添加一个碰撞组件,并且在start.js中的移动监听函数TouchMove中,判断当前坐标是否碰到水果就OK了。同样的,水果的生成也可以通过对象池来实现(可利用上面fruit.js中的isActivate和isAvailable)。

    最后是炸弹火星的特效制作。之前没发现炸弹的素材是没有这种火光特效的,因此为了好看点需要自己做一下。它看起来像是那种粒子特效,或者也可以用图片调整大小来做。不过没接触过感觉有点麻烦,我这里就直接用CocosCreator的Graphics组件来代码实现吧。

   事实上,可以观察到每一片“火焰”都是长这样的。

 

  看起来像是叶子的那种性状,实际上,它可以通过两段贝塞尔曲线连接起来来完成。有关贝塞尔曲线这里就不多做介绍了,像是js、css也都有实现的方法。在cocos中,可以通过以下方式来画贝塞尔曲线。

    首先在层级管理器canvas下新建一个节点paint,并给它添加组件Graphics,然后拖动到资源管理器中做成prefab,然后给start.js中新加一个属性,并拖动paint到脚本组件上完成引用。然后可以通过以下函数来绘制两段贝塞尔曲线。

//start.js

properties:{
    //...

    paint:{
            default: null,
            type: cc.Prefab
        },

    //...

},

drawLine(start_point,end_point,control_point_1,control_point_2){
    var g = this.abc.getComponent(cc.Graphics);
    g.moveTo(start_point[0],start_point[1]);
           g.quadraticCurveTo(control_point_1[0],control_point_1[1],end_point[0],end_point[1]);
            g.quadraticCurveTo(control_point_2[0],control_point_2[1],start_point[0],start_point[1]);
    g.stroke();
    g.fillColor = new cc.Color().fromHEX("f0ef9c");
    g.fill();

}

    接下来就是如何确定好起点startpoint、终点endpoint和两个控制点controlpoint。

    这里同样把火星写成一个类BoomFlame,可以简单的写在start.js最下面(注意别写在cc.Class里面)就行了。

    

//start.js

cc.Class({
    //...

});

//炸弹火星类
function BoomFlame(id,life,center,angle,length,create_time,paint){
    this.id = id;
    this.create_time = create_time;    //创建时间
    this.life = life;    //持续时间
    this.center = center;    //中心点位置
    this.angle = angle;    //运动的方向
    this.length = length;    //运动的距离

    this.radius = 15;    //火焰的“半长轴”
    this.elapse = 0;    //已经过的时间

    this.paint = paint;    //画笔(前面的带有graphics组件的prefab)

    this.active = true;  //用于对象池维护

    BoomFlame.prototype.set_properties = function(id,life,center,angle,length,create_time,paint){
        this.id = id;
        this.create_time = create_time;
        this.life = life;
        this.center = center;
        this.angle = angle;
        this.length = length;

        this.radius = 15;
        this.elapse = 0;

        this.paint = paint;

        this.active = true;
    }

    //确定起始点和控制点
    BoomFlame.prototype.update_boom_flame = function(draw){
        if(this.life - this.elapse <= 0){
            this.remove_boom_flame();
            return ;
        }
        this.paint.getComponent(cc.Graphics).clear();
        let ratio_elapse_life = this.elapse / this.life;    //比例
        let angle = this.angle;    
        let center = this.center;
        let len = this.length;
        let r = this.radius

        center = [ Math.trunc(center[0] + len * ratio_elapse_life * Math.cos(angle)) , Math.trunc( center[1] + len * ratio_elapse_life * Math.sin(angle) ) ];
        let start_point = [ Math.trunc( center[0] - r * (1-ratio_elapse_life) * Math.cos(angle) ) , Math.trunc( center[1] - r * (1-ratio_elapse_life) * Math.sin(angle) ) ];
        let end_point = [ Math.trunc( center[0] + r * (1-ratio_elapse_life) * Math.cos(angle) ) , Math.trunc( center[1] + r * (1-ratio_elapse_life) * Math.sin(angle) ) ];
        let control_point_1 = [ Math.trunc( center[0] + r * (1-ratio_elapse_life) * Math.cos(angle + 0.5*Math.PI) * 0.3 ) , Math.trunc( center[1] + r * (1-ratio_elapse_life) * Math.sin(angle + 0.5*Math.PI) * 0.3 ) ];
        let control_point_2 = [ Math.trunc( center[0] + r * (1-ratio_elapse_life) * Math.cos(angle - 0.5*Math.PI) * 0.3 ) , Math.trunc( center[1] + r * (1-ratio_elapse_life) * Math.sin(angle - 0.5*Math.PI) * 0.3 ) ];

        // cc.log('draw');
        // cc.log(this.paint);
        draw(this.paint.getComponent(cc.Graphics),start_point,end_point,control_point_1,control_point_2);



    }

    BoomFlame.prototype.remove_boom_flame = function(){
        this.paint.getComponent(cc.Graphics).clear();
        this.paint.destroy();
        // cc.log('remove');
        this.active = true;
        this.id = null;
        this.create_time = null;
        this.life = null;
        this.center = null;
        this.angle = null;
        this.length = null;

        this.radius = null;
        this.elapse = null;

        this.paint = null;

        
    }

}

    

  其中的update_boom_flame则是主要代码,用来计算出贝塞尔曲线的起始点和控制点,从而完成火焰在某一方向上的运动效果。其中的ratio_elapse_life主要是为了让其有那种 随着时间逐渐变小消失的效果。对于起始点和控制点的计算可以从下面的图看出来:

  

只要确定好中心点center后,就可以根据角度计算出控制点和起始点了,最后再通过上面的ration_elapse_life控制整体的大小缩放。

   通过cocos的计时器schedule就可以实现这种无限发射的效果了。

  

    相关代码如下:

//start.js

properties:{
    
    //...
    paint:{
            default: null,
            type: cc.Prefab
        },
        
        gid: 0,

        pooled_boom_flame: [],
        num_pooled_boom_flame: 20,

    global_timer: 0,

},

drawLine(g,start_point,end_point,control_point_1,control_point_2){
       
        g.moveTo(start_point[0],start_point[1]);
        
        
        g.quadraticCurveTo(control_point_1[0],control_point_1[1],end_point[0],end_point[1]);
        g.quadraticCurveTo(control_point_2[0],control_point_2[1],start_point[0],start_point[1]);
        g.stroke();
        g.fillColor = new cc.Color().fromHEX("f0ef9c");
        g.fill();

    },



onload(){

//...

// pooled_boom_flame是存放BoomFlame的对象池
for(let i=0;i<this.num_pooled_boom_flame;i++){
            this.pooled_boom_flame.push(new BoomFlame());
        }

        this.boom_flame_schedule = function(){

            if(Math.random() < 0.9)
            {
                let bf = this.getPooledBoomFlame(this.gid++,0.2 + 0.5 * Math.random(),[this.boom.x + this.node_w/2 - this.boom.width/2+3,this.boom.y + this.node_h/2 + this.boom.height/2-3],Math.PI * 2 * Math.random(),60,this.global_timer,cc.instantiate(this.paint));
                
                
                this.node.addChild(bf.paint);
            }
                

            for (var i in this.pooled_boom_flame){

                if(this.pooled_boom_flame[i].active == false){
                    
                    this.pooled_boom_flame[i].elapse = this.global_timer - this.pooled_boom_flame[i].create_time;
                this.pooled_boom_flame[i].update_boom_flame(this.drawLine.bind(this));
                
                }
            }
        }

        
        this.schedule(this.boom_flame_schedule, 0.04);

//...

},

getPooledBoomFlame(id,life,center,angle,length,create_time,paint){
        for(let i=0;i<this.num_pooled_boom_flame;i++){
            if(this.pooled_boom_flame[i].active){
                this.pooled_boom_flame[i].set_properties(id,life,center,angle,length,create_time,paint);
                this.pooled_boom_flame[i].active = false;
                return this.pooled_boom_flame[i];
            }
        }

        let new_BF = new BoomFlame();
        this.pooled_boom_flame.push(new_BF);
        this.num_pooled_boom_flame++;
        new_BF.active = false;
        new_BF.set_properties(id,life,center,angle,length,create_time,paint);
        return new_BF;
    },

update(dt){
    //...
    this.global_timer += dt ;
},


 

    基本完成了一大部分了,最后只剩下游戏中水果出现控制,游戏得分控制,以及一些转场的逻辑等等。游戏中水果是从下面往上抛然后下落的,实现起来也和前面的方法差不多,至于炸弹由于有火星的原因,可以在生成的同时给它绑定一个计时器就行。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值