Cocos2d-JS 使用定时器取消往图片、骨骼动画添加的shader

原文地址(刚学Cocos2d-js,若有什么说的不对的地方请大家教教…):http://blog.csdn.net/et_sandy/article/details/40488861

环境:
win7 64位
Cocos2d-JS v3.0 (final)
Cocos Code IDE v1.0.0.RC2
注意本文是在非web上实现,使用shader本人的浏览器不能正常显示效果

本文基于上一篇文章:http://blog.csdn.net/et_sandy/article/details/40272829 进行拓展。

正文:
使用shader还原方法有很多种,这里先介绍一下本人的理解:图片添加shader并不会影响图片原来的各种属性,只是在屏幕上显示之前,把图片的信息和所用的shader进行各种处理,把处理后的效果显示出来。

用一个公式解释的话,那就是 A (图片) + B(shader) = C(最终显示效果)。
而不是A+B=A。

(实际上opengl变换并不是相加,这里只是打个比方,示意一下)

既然理解了,那么要在已经使用shader的图片/骨骼动画/等等上取消特效就很简单了,直接使用shader,只是这个shader不添加任何特效,单纯的显示图片原来的信息,那就好了。

这里介绍两种方法:
第一种:在app.js最下面添加这样一个方法(具体可以参照上文app.js里面garySprte(sprite)方法):

function sourceSprite(sprite) {
if (sprite) {
var shader = new cc.GLProgram();
shader.initWithString(cc.SHADER_POSITION_COLOR_VERT , cc.SHADER_POSITION_COLOR_FRAG );//没有特效的shader
shader.link();
shader.updateUniforms();//我的理解是经过一系列的矩阵变换渲染到屏幕上
sprite.setShaderProgram(shader);//应用到精灵上
}
}

这里不需要添加新的.vsh和.fsh文件了,因为cc.SHADER_POSITION_COLOR_VERT 和cc.SHADER_POSITION_COLOR_FRAG 已经是了,我们可以到cocos2d-js 官方文档查看API : http://www.cocos2d-x.org/reference/html5-js/V3.0/index.html
接着在中间位置找到CCShaders.js

接着在73到100行可以看到具体的定义

然后在app.js里面的
graySprite(this.sprite);
后面添加
this.scheduleOnce(function(){
sourceSprite(this.sprite);
} , 6);//表示6秒后执行还原方法:sourceSprite(this.sprite)

但是,这样添加的话,效果是有的,但是控制台每帧都报opengl error,尽管报错,但是效果跟我们预期的一样。不过强迫症不能忍,当然也可以去找一下怎么修复这个问题。

不过这里还有另外一种更加简单的方法,那就是在使用(带特效)shader之前,先保存目标的shader,具体操作为:
在app.js里的ctor方法里,调用
graySprite(this.sprite);
之前,添加这么一句获取目标的shader:
var spriteBefore = this.sprite.getShaderProgram();
接着在这句的后面,添加定时器操作:
this.scheduleOnce(function(){
this.sprite.setShaderProgram(spriteBefore);
} , 5);//表示6秒后执行还原:this.sprite.setShaderProgram(spriteBefore);

同理,SkeletonAnimation也是一样,在ctor方法return true前添加:
var spineBoyBefore = spineBoy.getShaderProgram();
this.scheduleOnce(function(){
spineBoy.setShaderProgram(spineBoyBefore)
;} , 6);//6秒后执行还原

注意在创建spineBoy后添加。

然后特别说明一下,添加特效shader时要注意SkeletonAnimation的gl_Position为:
gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position;
不然还原后位置会发生变化 ,感觉就像在位移

最后就是Armature动画
这个要块骨骼皮肤渲染的比较麻烦,因为在遍历的时候使用for in了,而不是for(;;){},在for in中,就算在使用特效shader前保存了原本的shader,之前再使用定时器设定X秒后运行还原,尽管执行的次数跟骨骼数量一样,但只对一个骨骼有效,在遍历方法里:
grayArmature: function (armature) {
var locBoneDic = armature.getBoneDic();
for (var key in locBoneDic) {
var bone = locBoneDic;

        if (bone && bone.getDisplayRenderNode()) { 
            var node = bone.getDisplayRenderNode();  
            graySprite(node);  
        }  
    }  
}  

因为是在X秒后再对node进行还原shader,而此时node保存的只是遍历骨骼时保存的最后一个骨骼,所以说,要用for(;;){},把骨骼添加进node*数组(或者列表里面),X秒后操作的时候每个骨骼都保留了下来,但是,由于本人刚接触JS,而且基础不咋地,就继续探索别的方法了。

那,先保存原来的shader,再写一个遍历方法,再设置为原来的shader不就可以了?

cocos2d-js的定时器延迟函数:
scheduleCallbackForTarget
scheduleOnce
等等,延时器的回调函数使用的注意方法在这里本人以目前的了解总结了一下:http://blog.csdn.net/et_sandy/article/details/40683921

以下为本文使用的方法,那就是在每帧更新方法里面设置一个标志,X秒后把标志设置为true,然后执行遍历方法,接着把标志设置为false

先在一开始添加全局变量:
*

*
bc:图片原本的shader
fa:标志位
armature:骨骼动画

接着在onEnter里面添加:(其实添加的位置最好是在ctor,也就是初始化的时候,不过本人注重实现效果,优化什么的完全没有考虑)
this.fa = false;//初始化标志位为false
this.scheduleUpdate();//开启update,每秒刷新时的操作
this.scheduleOnce(function() {
this.fa = true;
}, 7); //7秒后把标志位设置为true

重写update方法:
update : function() {
if (this.fa) {//上面用延迟方法把this.fa设置为7秒后为true
this.grayArmature(this.armature, false);//下面会介绍修改的grayArmature方法
this.fa = false;
}
},

接着把修改onEnter方法:
onEnter : function() {//主要是更新armature的作用域,改变了X坐标
** this._super();
var size = cc.winSize;
var skins = new Array();
ccs.armatureDataManager.addArmatureFileInfo(res.COwboy_png0,res.CowBoy_plist0,res.CowBoy_exportjson);
this.armature = ccs.Armature.create(“Cowboy”);
this.armature.getAnimation().play(“Walk”);
this.armature.x = size.width * 0.8;
this.armature.y = size.height / 2;

    this.armature.setScale(0.5);
    this.grayArmature(this.armature,true);
    
    this.addChild(this.armature, 4, 0);

    this.fa = false;
    this.scheduleUpdate();
   this.scheduleOnce(function() {
    this.fa = true;
}, 7); 

},**
最后,改动grayArmature
grayArmature:function(armature,flag){
var locBoneDic = armature.getBoneDic();
for (var key in locBoneDic) {
var bone = locBoneDic;
if (bone && bone.getDisplayRenderNode()) {
var node = bone.getDisplayRenderNode();

            if (flag) {
                this.bc = node.getShaderProgram();//保存原本的shader
                graySprite(node);
            } else {
                node.setShaderProgram(this.bc);
            }
        }
    }
},

方法最后有没有逗号,根据自己实际情况而定

最后看看效果,运行程序后第4秒时:
*


*

第5秒:
*


*

第6秒(注意要做gl_Position的处理):
*


*

第7秒:
*


*

最后整个app.js:有些测试函数本人就懒得去掉了,大家可以自己拿去改改试一下效果,中间的图片使用的延迟方法跟上面介绍的有出入,不过大家可以到官方文档查查API,效果跟上面介绍是一样的。
var HelloWorldLayer = cc.Layer.extend({
sprite:null,
bc: null,
fa:null,
armature:null,
ctor:function () {
// ////////////////////////////
// 1. super init first
this._super();

    // ///////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the
    // program
    // you may modify it.
    // ask the window size
    var size = cc.winSize;

    // add a "close" icon to exit the progress. it's an autorelease object
    var closeItem = new cc.MenuItemImage(
        res.CloseNormal_png,
        res.CloseSelected_png,
        function () {
            cc.log("Menu is clicked!");
           
        }, this);
    closeItem.attr({
        x: size.width - 20,
        y: 20,
        anchorX: 0.5,
        anchorY: 0.5
    });

    var menu = new cc.Menu(closeItem);
    menu.x = 0;
    menu.y = 0;
    this.addChild(menu, 1);

    // ///////////////////////////
    // 3. add your codes below...
    // add a label shows "Hello World"
    // create and initialize a label
    //var helloLabel = new cc.LabelTTF("Hello World"+SimpleNativeClass.getAnotherMoreComplexField(), "Arial", 38);
    var helloLabel = new cc.LabelTTF("Hello World", "Arial", 38);
    // position the label on the center of the screen
    helloLabel.x = size.width / 2;
    helloLabel.y = 0;
    // add the label as a child to this layer
    this.addChild(helloLabel, 5);

    // add "HelloWorld" splash screen"
    this.sprite = new cc.Sprite(res.HelloWorld_png);
    this.sprite.attr({
        x: size.width / 2,
        y: size.height / 2,
        scale: 0.5,
        rotation: 180
    });
  
    var spriteBefore = this.sprite.getShaderProgram();
    this.addChild(this.sprite, 0);
    graySprite(this.sprite); 
    cc.director.getScheduler().scheduleCallbackForTarget(this, function(){
        this.sprite.setShaderProgram(spriteBefore);
        } , 5, false, 0, !this._isRunning );
    this.sprite.runAction(
        cc.sequence(
            cc.rotateTo(2, 0),
            cc.scaleTo(2, 1, 1)
        )
    );
    helloLabel.runAction(
        cc.spawn(
            cc.moveBy(2.5, cc.p(0, size.height - 40)),
            cc.tintTo(2.5,255,125,0)
        )
    );
    
    var spineBoy = new sp.SkeletonAnimation(res.SpineBoy_json, res.SpineBoy_atalas);
    spineBoy.setPosition(cc.p(size.width * 0.1, size.height / 2 - 150));
    spineBoy.setAnimation(0, 'walk', true);
    spineBoy.setMix('walk', 'jump', 0.2);
    spineBoy.setMix('jump', 'walk', 0.4);
    var spineBoyBefore = spineBoy.getShaderProgram();
    graySprite(spineBoy);
    this.addChild(spineBoy, 2);
    this.scheduleOnce(function(){
        spineBoy.setShaderProgram(spineBoyBefore)
        ;} , 6); 
    return true;
},

onEnter : function() {
    this._super();
    var size = cc.winSize;
    var skins = new Array();
    ccs.armatureDataManager.addArmatureFileInfo(res.COwboy_png0,res.CowBoy_plist0,res.CowBoy_exportjson);
    this.armature = ccs.Armature.create("Cowboy");
    this.armature.getAnimation().play("Walk");
    this.armature.x = size.width * 0.8;
    this.armature.y = size.height / 2;
    
    this.armature.setScale(0.5);
    this.grayArmature(this.armature,true);
    
    this.addChild(this.armature, 4, 0);

    this.fa = false;
    this.scheduleUpdate();
   this.scheduleOnce(function() {
    this.fa = true;
}, 7); 
    
},
update : function() {
     if (this.fa) {
         cc.log("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
        this.grayArmature(this.armature, false);
        this.fa = false;
    } 
},
grayArmature:function(armature,flag){ 
    var locBoneDic = armature.getBoneDic(); 
    cc.log(locBoneDic);
    for (var key in locBoneDic) { 
        var bone = locBoneDic; 
        if (bone && bone.getDisplayRenderNode()) { 
            var node = bone.getDisplayRenderNode();
            
            if (flag) {
                this.bc = node.getShaderProgram();
                graySprite(node);
            } else {
                node.setShaderProgram(this.bc);
            }
        }
    }
},
grayArmature2:function(armature,bc){ 
    var locBoneDic = armature.getBoneDic(); 
    cc.log("131231313");
    for (var key in locBoneDic) { 
        var bone = locBoneDic; 
        if (bone && bone.getDisplayRenderNode()) { 
            var node = bone.getDisplayRenderNode();
                node.setShaderProgram(bc);
        } 
    } 
},

});

function graySprite(sprite)
{
if(sprite)
{
var shader = new cc.GLProgram();
shader.init(res.Grat_vsh, res.Gray_fsh);
shader.link();
shader.updateUniforms();
sprite.setShaderProgram(shader);
}
}

function backToSprite(sprite)
{
if(sprite)
{
var shader = new cc.GLProgram();
shader.init(cc.SHADER_POSITION_COLOR_VERT , cc.SHADER_POSITION_COLOR_FRAG );
shader.link();
shader.updateUniforms();
sprite.setShaderProgram(shader);
}
}

var HelloWorldScene = cc.Scene.extend({
onEnter:function () {
this._super();
var layer = new HelloWorldLayer();
this.addChild(layer);
}
});

稍微更新了一下,,,

话说。。。how to。。。删掉自己的评论

:2: 好文章

:14: :14::14::14: