查看原文
其他

Cocos Creator基础教程(12)—精灵变身

张晓衡 Creator星球游戏开发社区 2021-08-09

在Cocos Creator中使用率最高的非精灵(Sprite)莫属了, 在游戏中我们经常会遇到将一张图片替换成另一张图片的情况,或者是在不同状态时来回切换图片。实现这个功能对程序员同学来说并不难,但是!回头检视一下编写的代码,能否让美术、策划同学使用上吗?如果不能的话,相信这篇教程可能对你和你的伙伴有更多启发!


1. SpriteIndex组件

我们这里设计一个SpriteIndex组件,使用组件的index属性来控制Sprite组件的spriteFrame属性,从而得到图片变换的能力。

//SpriteIndex.js
cc.Class({    extends: cc.Component,               //编辑器属性,只在编辑状态有效    editor: CC_EDITOR && {        requireComponent: cc.Sprite,     //要求节点必须有cc.Sprite组件    },    properties: {        spriteFrames: [cc.SpriteFrame],  //定义一个SpriteFrames数组        _index: 0,                       //以下划线“_”开始的为私用变量              index: {                         //index属性控制图片切换            type: cc.Integer,            //定义属性为整数类型            //这次没使用notify方式实现属性值的变化监听,改用getter/setter方式            get() {                                          return this._index;            },
           //为负数退出
           set(value) {                                if (value < 0) {                    
                   return;                }                
               //根据spriteFrames组件长度计算this._index                this._index = value % this.spriteFrames.length;                
               //获取当前节点上的Sprite组件对象                let sprite = this.node.getComponent(cc.Sprite);                
               //设置Sprite组件的spriteFrame属性,变换图片                sprite.spriteFrame = this.spriteFrames[this._index];            },        }    },        /**    *next方法,调用index++切换图片,    *可以方便被cc.Button组件的事件调用    */    next() {        
       this.index++; //调用自身index属性,编号+1    } });

代码比之前的教程要复杂了一点点,对没有编程经验的美术、策划同学来说可能残忍了点,但坚持跟着注释了解组件的实现意图,相信你会收获更多。

我们再看下图,SpriteIndex组件的用法:



  1. 在编辑器场景中添加一个Sprite组件

  2. 然后挂载上SpriteIndex

  3. 添加SpriteFrames数组属性元素

  4. 将可能会出现的图片拖动到SpriteFrames数组属性下

  5. 尝试修改index属性,你会看到精灵图片的变化


2. 运行时图片切换

请出我们的按钮组件,通过点击时调用SpriteIndex.next方法进行切换,看下图配置:


此时启动预览,尝试点击这个精灵节点你就能看到图片在不断切换变化了。如果你想玩的再高级一点,可以在一个定时器中调用next方法,它立马就是成了一个序列帧动画了。

3. 直接继承cc.Sprite

我们设计的是通用型组件,最好还是不要访问别的节点、组件的属性和方法,保持干净!这样更具有可扩展性和适应性。

SpriteIndex在这里就是给cc.Sprite做辅助的,它能不脱离cc.Sprite而存在。坚持做到最好,尽可能的让组件更多人能使用,限制越少越好,属性也是越少越好,只要能完成任务就行,看下在的做法,我们改进一下:

//SpriteEx.js

let SpriteEx = cc.Class({    
   extends: cc.Sprite,    //继承自cc.Sprite    properties: {        
       spriteFrames: [cc.SpriteFrame],        
       _index: 0,        
       index: {            
       type: cc.Integer,            set(value) {                
               if (value < 0) {                    
                   return;                }                
               this._index = value % this.spriteFrames.length;                //直接访问spriteFrame属性,因为this就是cc.Sprite                this.spriteFrame = this.spriteFrames[this._index];            },            get() {                
               return this._index;            }        }    },    next() {        this.index++    } });


//下面是控制SpriteEx组件在属性检查器中的属性显示

//不显示spriteFrame属性
cc.Class.Attr.setClassAttr(SpriteEx, 'spriteFrame', 'visible', false);
//不显示Atlas属性
cc.Class.Attr.setClassAttr(SpriteEx, '_atlas', 'visible', false);
//根据函数返回值控制属性显示、隐藏
cc.Class.Attr.setClassAttr(SpriteEx, 'fillType', 'visible', function() {    
   return this._type === cc.Sprite.Type.FILLED; }); cc.Class.Attr.setClassAttr(SpriteEx, 'fillCenter', 'visible', function() {    
   return this._type === cc.Sprite.Type.FILLED; }); cc.Class.Attr.setClassAttr(SpriteEx, 'fillStart', 'visible', function() {    
   return this._type === cc.Sprite.Type.FILLED; }); cc.Class.Attr.setClassAttr(SpriteEx, 'fillEnd', 'visible', function() {    
   return this._type === cc.Sprite.Type.FILLED; }); cc.Class.Attr.setClassAttr(SpriteEx, 'fillRange', 'visible', function() {    
   return this._type === cc.Sprite.Type.FILLED; }); cc.Class.Attr.setClassAttr(SpriteEx, 'srcBlendFactor', 'visible', function() {    
   return this._type === cc.Sprite.Type.FILLED; }); cc.Class.Attr.setClassAttr(SpriteEx, 'dstBlendFactor', 'visible', function() {    
   return this._type === cc.Sprite.Type.FILLED; });

上面的核心代码没几行,千万不要被吓到了,后面的属性控制函数主要是减少不必要的属性产生的干扰,提高组件使用时的体验,看下图:


红色框线是我们添加的属性,上面的是cc.Sprite组件原始属性,我们屏蔽了spriteFrame、Atlas属性的显示,这里已经看不到了。

节点下面再挂接一个Button组件,与之前的SpriteIndex的用法一样,运行起来效果相同。不过这里的节点挂载的组件少了一个,使用起来会简单一些,对策划、美术同学来说更贴心啦。

可能有程序员同学会怀疑,「奎特尔星球」是不是被美术、策划给收买了,尽为他们着想。请相信Shawn,我这是在为程序员节省时间,将界面编辑的工作交给擅长的人,这样你就有时间可以去学习更有价值的东西了,比如说写点文章分享到「奎特尔星球」上

4.小结

本篇介绍了对cc.Sprite组件的扩展,有两种方式,一种是做辅助,一种是继承,他们没有绝对的优缺点,需要根据不同的情景取舍不同的方案。一是实现起来尽可能简单,二是用起来容易,三是能解决实际问题。

其实我们这里仍然是在讲组件化思维,合格的组件化组件将成为非程序员创作游戏内容的利器,它也是提高生产力的秘密。我在想以后是不是做代码审查,美术、策划也能为程序投上一票呢?

继续之前的问卷调查,希望大家能积极参与,感谢您的支持!




欢迎关注「奎特尔星球」微信公众号,更期待你向公众号投稿,来我们一起成长!


热门文章TOP10,邀你遨游「奎特尔星球」

  1. Cococ Creator基础教程—meta的秘密(3)

  2. uikiller再度进化支持Cocos Creator 2.0

  3. 庆祝「奎特尔」500勇士,诞生!

  4. 当creator遇上protobufjs—感谢有你,再战2.0

  5. Cocos Creator基础教程(7)—场景切换

  6. Cocos Creator基础教程(9)—优化代码编辑器

  7. Cocos Creator基础教程(4)—color属性的妙用

  8. Cocos Creator基础教程(1)—从zIndex开始

  9. Cocos Creator基础教程(2)—聊聊scale与size属性

  10. Creator模块介绍—领略模块化的力量

  11. 探索CocosH5正确的开发姿势

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存