按钮(Button)组件是游戏中最常用的组件之一,可以显示文本标签,图标或者两种同时显示。CocosCreator为广大开发者提供了一个Button组件应有的基本功能,暂且将CocosCreator提供的Button组件称为系统Button组件,往往我们会想使用系统已有的Button组件来做二次封装,当然自己封装组件有很多种方式,在此就不过多的介绍其他二次封装组件的思路。

本文二次封装Button组件,为Button组件提供新的天赋:

  1. 防止按钮短时间内重复点击

  2. 防止多个按钮同时按下触发不同的功能。

  3. 通过界面简单配置,达到按钮触发播放不同的音效。

  4. 通过简单的配置,为Button增加不同的按钮动画,来引导用户点击按钮。

第一步:在bb.d.ts(自定义命名空间脚本)中为BUTTON_ACTION枚举增加代码提示。

export enum BUTTON_ACTION {
    NONE,
    HEARTBEAT,
    VERTICAL_ROCK,
}

第二步:在bb.ts(初始化游戏框架脚本)中为BUTTON_ACTION枚举赋值。

bb.BUTTON_ACTION = cc.Enum({
    NONE: 1,
    HEARTBEAT: 2,
    VERTICAL_ROCK: 3,
})

第三步:为Button组件增加一个数据组件UserButtonData组件,用于控制新的Button组件的新的功能。

/**
 * @class UserButtonData
 * @deprecated 此类只能和UserButton一起使用才有效,此组件为UserButton的数据组件
 * @author lxx
 */
const {ccclass, property} = cc._decorator;
@ccclass
export default class UserButtonData extends cc.Component {
    @property({tooltip: "是否需要开启多点触摸功能"})
    dmultiTouch: boolean = false;
    @property({tooltip: "是否需要开启屏蔽连续点击行为"})
    openContinuous: boolean = true;
    @property({type: cc.Float, tooltip: "开启屏蔽连续点击行为的间隔时间"})
    continuousTime: number = 1.5;
    @property({type: bb.BUTTON_ACTION, tooltip: "BUTTON动画"})
    buttonAction: bb.BUTTON_ACTION = bb.BUTTON_ACTION.NONE;
    @property({tooltip: "按钮音效"})
    audioUrl = "";
}
}

第四步:增加一个ViewAction脚本,负责管理Button的Action动画和其他Action动画。

export default class ViewAction {
    static _instance: ViewAction = null;
    static getInstance() {
        if (!ViewAction._instance) {
            ViewAction._instance = new ViewAction();
        }
        return ViewAction._instance;
    }
    buttonActionMap: Object = {};
    constructor() {
        this.buttonActionMap[bb.BUTTON_ACTION.HEARTBEAT] = this.playButtonHeartbeat;
        this.buttonActionMap[bb.BUTTON_ACTION.VERTICAL_ROCK] = this.playButtonVerticalRock;
    }
    runButtonAction(buttonAction: bb.BUTTON_ACTION.VIDEO, target: cc.Node, callfunc: Function = null) {
        if (!this.buttonActionMap[buttonAction]) {
            bb.log(`view action not esit!!!`);
            return;
        }
        this.buttonActionMap[buttonAction](target, callfunc);
    }
    playButtonHeartbeat(target: cc.Node, callfunc: Function = null, time: number = 0.2, delayTime: number = 0) {
        // let act1 = cc.delayTime(2);
        // let act2 = cc.scaleTo(0.8, 1.2);
        // let act3 = cc.scaleTo(0.5, 1);
        // let act4 = cc.scaleTo(0.7, 1.1);
        // let act5 = cc.scaleTo(0.5, 1);
        // let act6 = cc.sequence(act1, act2, act3, act4, act5).repeatForever();
        // target.runAction(act6);
        let act1 = cc.delayTime(2);
        let act2 = cc.scaleTo(0.8, 0.8, 1.2);
        let act3 = cc.scaleTo(0.5, 1, 1);
        let act4 = cc.scaleTo(0.7, 0.8, 1.2);
        let act5 = cc.scaleTo(0.5, 1, 1);
        let act6 = cc.sequence(act1, act2, act3, act4, act5).repeatForever();
        target.runAction(act6);
    }
    playButtonVerticalRock(target: cc.Node, callfunc: Function = null, time: number = 0.2, delayTime: number = 0) {
        let act1 = cc.moveBy(0.1, 0, 10);
        let act2 = cc.moveBy(0.1, 0, -10);
        let act3 = cc.moveBy(0.1, 0, -10);
        let act4 = cc.moveBy(0.1, 0, 10);
        let act5 = cc.sequence(act1, act2, act3, act4).repeat(3);
        let act6 = cc.delayTime(1);
        let act7 = [act5, act6];
        let act8 = cc.sequence(act7).repeatForever();
        target.runAction(act8);
    }
}

第五步:重点中的重点,利用官方提供的Button样式button.js 扩展Button组件,暂且将该组件命名为UserButton。

import UserButtonData from "./UserButtonData";
import ViewAction from "./ViewAction";
const {ccclass, property, requireComponent, executeInEditMode, menu, help, inspector} = cc._decorator;
@ccclass
@menu('i18n:MAIN_MENU.component.ui/UserButton')
@executeInEditMode()
@requireComponent(UserButtonData)
@help('i18n:COMPONENT.help_url.widget')
@inspector('packages://inspector/inspectors/comps/button.js')
export default class UserButton extends cc.Button {
    buttonData: UserButtonData;
    openContinuous = true;
    continuousTime: number = 1.5;
    continuous: boolean = false;
    _continuousTimer: number = -1;
    dmultiTouch: boolean = false;
    action: talefun.BUTTON_ACTION = talefun.BUTTON_ACTION.NONE;
    audioUrl: string = null;
    onEnable() {
        this.buttonData = this.node.getComponent(UserButtonData);
        if (this.buttonData) {
            this.openContinuous = this.buttonData.openContinuous;
            this.continuousTime = this.buttonData.continuousTime;
            this.dmultiTouch = this.buttonData.dmultiTouch;
            this.action = this.buttonData.buttonAction;
            this.audioUrl = this.buttonData.audioUrl;
        }
        this.continuous = false;
        super.onEnable();
        if (!CC_EDITOR) {
            ViewAction.getInstance().runButtonAction(this.action, this.node);
        }
    }
    onDisable() {
        if (this._continuousTimer >= 0) {
            clearTimeout(this._continuousTimer);
            this._continuousTimer = -1;
        }
        super.onEnable();
    }
    _onTouchEnded(event) {
        if (!this.interactable || !this.enabledInHierarchy) return;
        if (this["_pressed"] &&  !this.continuous) {
            this.continuous = this.openContinuous ? true : false;
            cc.Component.EventHandler.emitEvents(this.clickEvents, event);
            this.node.emit('click', this);
            bb.AudioManager.playBtnEffect(this.audioUrl ? cc.url.raw(this.audioUrl): null)
            if (this.openContinuous) {
               this._continuousTimer = setTimeout(function(){
                    this.continuous = false;
                }.bind(this), this.continuousTime * 1000);
            }
        }
        this["_pressed"] = false;
        this["_updateState"]();
        event.stopPropagation();
    }
}

关注【游戏讲坛】微信公众号,获取最新动态!