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

时间:2022-08-14 00:09:30

环境:

win7 64位

Cocos2d-JS v3.0 (final)

Cocos Code IDE v1.0.0.RC2

注意本文是在非web上实现,使用shader本人的浏览器不能正常显示效果


本文基于上一篇文章:Cocos2d-JS下往Sprite SkeletonAnimation Armature(骨骼动画)添加shader  进行拓展。


正文:

使用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

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


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

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


然后在app.js里面的

  1. 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[key];  

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

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


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


cocos2d-js的定时器延迟函数:

scheduleCallbackForTarget

scheduleOnce

等等,我在这篇文章总结了一下使用延时函数要注意的地方http://blog.csdn.net/et_sandy/article/details/40683921



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


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

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

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[key];  
    		if (bone && bone.getDisplayRenderNode()) { 
    			var node = bone.getDisplayRenderNode();
    			
    			if (flag) {
    				this.bc = node.getShaderProgram();//保存原本的shader
    				graySprite(node);
				} else {
					node.setShaderProgram(this.bc);
				}
    		}
    	}
    },

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

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

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


第5秒:

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


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

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


第7秒:

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


最后整个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[key]; 
    		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[key]; 
    		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);
    }
});