cocosCreator 骨骼动画

骨骼动画定义
传统的动画,一般是对一个物体对象进行位移、旋转、缩放、变形,然后把关键帧的信息记录下来,在播放的时候按照关键帧时间对物体对象进行位移、旋转、缩放、变形,并在关键帧与关键帧之间做插值运算。
骨骼动画的特点是,需要做动画的物体对象本身不记录位移、旋转、缩放、变形信息,而是通过了第三方的“骨骼”物体记录动画信息,然后物体对象本身只记录受到骨骼物体影响的权重。在播放的时候,通过骨骼物体的关键帧和物体对象记录的权重,让动画重现
骨骼动画的好处
1、骨骼动画是影响到顶点级别的动画,而且可以多根骨骼根据权重影响同一个顶点,不论是2D或者3D使用,都可以让动画做得更丰富。
2、做到了对象和动画分离,我们只需要记录了物体对于骨骼的蒙皮权重,就可以单独的去制作骨骼的动画,在保证蒙皮信息和骨骼信息一致的情况下,还可以多个物体之间共享动画。
3、相对于2D的逐帧动画大大的节省资源容量。
4、3D角色动画离不开骨骼动画。
骨骼动画文件
spine骨骼动画导出时一般会包含三个文件,png,atlas,json
png是材质,atlas是创建整个初始骨骼物体对png的解析,即每一个蒙皮碎片在整个纹理图集上的位置,下边重点介绍一下 json文件内容:
keys:skeleton,描述骨骼的尺寸,fps,
keys:bones,描述所有骨骼的初始信息(位置,旋转,父节点,)
keys:slots,插槽,描述的是bone对应的的蒙皮名字
keys:skins,蒙皮,描述骨骼对应的蒙皮的一些状态(旋转,大小,位置,名字)
keys:animations,动画,描述的是动画的信息,每一段动画,都有若干帧,都对应这所有骨骼的变化,而这些变化就是(translate,rotate,shear),外界可通过这些骨骼的变化再依据权值来算出蒙皮的状态,最后呈现出来一帧帧动画

creator中使用骨骼

**Skeleton:**骨骼的渲染脚本
animation:外界可以通过它来修改要播放的动画名字
update函数:
_updateCache函数:
updateToFrame(idx);_frameCache成员变量:(AnimationCache):
spine.AnimationState.apply(),
时间轴.prototype.apply()
修改骨骼的状态
修改蒙皮的状态

**AnimationCache:**缓存一个动画里的所有帧

缓存模式有三种:SHARED_CACHE(共享缓存),PRIVATE_CACHE(私有缓存),REALTIME(实时计算)
**SkeletonCache:**骨骼的缓存
通过spine.SkeletonData,可以创建spine.Skeleton,spine.SkeletonClipping(), spine.AnimationState,其实缓存的就是这三个数据

**spine.SkeletonJson和spine.SkeletonBinary:**主要是来解析骨骼动画数据的,骨骼动画文件在导出时一般有两种格式,二进制(skel)或者json,最后会生成第二级骨骼动画数(spine.SkeletonData),注意,此处是生成spine.SkeletonData所有成员的数据,具体可以查看这两个类的readSkeletonData的函数实现
**第一级骨骼动画数据SkeletonData:**这个是继承自cc.Asset,他是我们加载骨骼动画源文件所产生的一个解析文件,主要是解析atlas和skel(二进制,也可以导出为json)这两个源文件
**第二级骨骼动画数据是spine.SkeletonData:**解析第一级骨骼动画数据,生成新的data,主要包含骨骼(bones),插槽(slots),蒙皮(skins),事件(events),约束,animations(动画数据),fps这些数据
**spine.Skeleton:**自身也是一个骨骼,基础数据(位置,缩放,时间),管理所有的骨骼(Bone),约束条件(ikConstraints,transformConstraints,pathConstraints),插槽(slots),蒙皮(skin)等以及他们之间的关系
**spine.PathConstraint:**路径约束
**spine.PathConstraintData:**路径约束的data
**spine.IkConstraint:**反向动力学约束
**spine.IkConstraintData:**反向动力学约束的data
**spine.ConstraintData:**约束的基类data
**spine.Bone:**单个骨骼
**spine.BoneData:**单个骨骼的数据

**spine.Animation:**游戏中的动画,持有动画名,动画的时间轴,动画的时间

**spine.AnimationState:**动画的状态,每一个骨骼动画都有一个动画状态
它的apply函数直接
**spine.AnimationStateData:**动画的状态数据

**spine.TrackEntry:**持有当前要播放的动画数据animation,当前动画是否loop,当前动画的总时间

时间轴的类型有15种,修改骨骼bone的状态
**spine.CurveTimeline:**时间轴基类
**spine.RotateTimeline:**旋转
**spine.TranslateTimeline:**平移
**spine.ScaleTimeline:**缩放
**spine.ShearTimeline:**曲线
**spine.ColorTimeline:**颜色
spine.TwoColorTimeline:
spine.AttachmentTimeline:
spine.DeformTimeline:
**spine.EventTimeline:**事件
spine.DrawOrderTimeline:
spine.IkConstraintTimeline:
spine.TransformConstraintTimeline:
spine.PathConstraintMixTimeline:
spine.PathConstraintSpacingTimeline:
spine.PathConstraintPositionTimeline:

一个骨骼动画通常会包含两个文件(纹理信息png和动画数据skel)
当把一个骨骼动画导入内存的时候,

一个动画文件包含若干组动画,每一组动画都有一个名字来标记,这一组动画则由一个帧数组frames[N]组成,默认动画播放的第一帧为frames[0],当游戏设置完动画名字以后,就开始更新动画,按照一帧一帧的去frames中取动画数据来播放

creator中sprite

一个sprite渲染组件,会包含两个特殊的成员变量,一个spriteFrame,一个是material
spriteFrame:指的是精灵帧,它主要管理的是uv坐标,顶点位置,矩形区域

//ccspriteFrame.js
//通过纹理的名字来加载纹理信息
 _loadTexture: function () {
        if (this._textureFilename) {
            let texture = textureUtil.loadImage(this._textureFilename);
            this._refreshTexture(texture);
        }
    }
//texture-util.js
//加载纹理信息
loadImage (url, cb, target) {
        cc.assertID(url, 3103);

        var tex = cc.loader.getRes(url);
        if (tex) {
            if (tex.loaded) {
                cb && cb.call(target, null, tex);
                return tex;
            }
            else
            {
                tex.once("load", function(){
                   cb && cb.call(target, null, tex);
                }, target);
                return tex;
            }
        }
        else {
            //../assets/CCTexture2D
            tex = new Texture2D();
            tex.url = url;
            cc.loader.load({url: url, texture: tex}, function (err, texture) {
                if (err) {
                    return cb && cb.call(target, err || new Error('Unknown error'));
                }
                texture.handleLoadedTexture();
                cb && cb.call(target, null, texture);
            });
            return tex;
        }
    }
//loader.js
//cc.loader.load调用的时候会进到下面这个函数中
function loadImage (item) {
    var loadByDeserializedAsset = (item._owner instanceof cc.Asset);
    if (loadByDeserializedAsset) {
        // already has cc.Asset
        return null;
    }

    var image = item.content;
    if (cc.sys.platform !== cc.sys.FB_PLAYABLE_ADS && !(image instanceof Image)) {
        return new Error('Image Loader: Input item doesn\'t contain Image content');
    } 

    // load cc.Texture2D
    var tex = item.texture || new Texture2D();
    tex._uuid = item.uuid;
    tex.url = item.url;
    tex._setRawAsset(item.rawUrl, false);
    tex._nativeAsset = image;
    return tex;
}
//

material:指的是材质,它会和shader关联,将shader中的一些属性暴露出来,比如我们可以通过材质给shader中的属性赋值,比如texture

spriteFrame内部持有有一个CC.Texture2D,这个成员变量主要是解析纹理信息的

CC.Texture2D内部持有一个renderer.Texture2D,这个成员变量主要是和GPU打交道的

//renderer.Texture2D.js

constructor(device, options) {
    super(device);

    let gl = this._device._gl;
    //目标缓冲
    this._target = gl.TEXTURE_2D;
    //首先会在内部创建glID这个是纹理在GPU中的标识
    this._glID = gl.createTexture();

    // always alloc texture in GPU when we create it.
    //创建时总是在gpu中分配纹理
    options.images = options.images || [null];
    //此处主要是更新数据到GPU纹理缓存
    this.update(options);
  }

在shader中经常会使用texImage2D(texture,v.uv0),这里的纹理texture从哪里加载来的呢

//当外界加载一张纹理成功以后,返回的数据,我们可以把它赋给sprite.spriteframe,这个时候就会调用下面的函数
//ccsprite.js
_applySpriteFrame (oldFrame) {
        let oldTexture = oldFrame && oldFrame.getTexture();
        if (oldTexture && !oldTexture.loaded) {
            oldFrame.off('load', this._applySpriteSize, this);
        }

        if (this._spriteFrame && !this._spriteFrame.isValid) {
            cc.log('sprite frame is not valid', this.node._name)
            this._spriteFrame = null;
        }

        let spriteFrame = this._spriteFrame;
        if (spriteFrame) {
            this._updateMaterial();
            let newTexture = spriteFrame.getTexture();
            if (newTexture && newTexture.loaded) {
                this._applySpriteSize();
            }
            else {
                this.disableRender();
                spriteFrame.once('load', this._applySpriteSize, this);
            }
        }
        else {
            this.disableRender();
        }

        if (CC_EDITOR) {
            // Set atlas
            this._applyAtlas(spriteFrame);
        }
    }
//ccsprite.js
_updateMaterial () {
        let texture = this._spriteFrame && this._spriteFrame.getTexture();
        // make sure material is belong to self.
        let material = this.getMaterial(0);
        if (material) {
            if (material.getDefine('USE_TEXTURE') !== undefined) {
                material.define('USE_TEXTURE', true);
            }
            //设置纹理信息,将来可以在shader中使用
            material.setProperty('texture', texture);
        }
        BlendFunc.prototype._updateMaterial.call(this);
    },
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值