Cocos2d-js 开发记录:自定义按钮

游戏开发总是有些特殊,一般的预制的UI无法满足要求。其实对于不复杂的功能,与其看文档还不如自己写一个。比如游戏中一个虚拟键盘,其中的按键在按下时会增长,变为原来的两倍高度,在原来高度上方显示按键的字如:

  

整体键盘:

 

一般来说按钮各个状态的各个大小都是一样的,一般可以直接使用cocos中的cc.MenuItemSprite或者cc.MenuItemImage来实现。而上述这个键盘按钮却是大小不同的。那么在两个状态的图片大小不同的时候,cc.MenuItemImage会怎么对齐呢?试了一下采取的是底端对齐,按钮的点击范围有正常状态下的图片大小确定,这两个情况其实和我们要实现的键盘按钮的要求是一致的。但是cc.MenuItemImage不能加入Label,更没有可用根据点击情况变化这个label位置的功能。

 这些cc.MenuItemXxxx系列的类都直接或间接继承自cc.MenuItem,而cc.MenuItem继承自cc.Node(framework/cocos2d-html5/cocos2d/core/menus/CCMenuItem.js),来看下cc.MenuItem的源码:

/**
 * Subclass cc.MenuItem (or any subclass) to create your custom cc.MenuItem objects.
 * @class
 * @extends cc.Node
 * @param {function|String} callback
 * @param  {cc.Node} target
 */
cc.MenuItem = cc.Node.extend(/** @lends cc.MenuItem# */{
    _enabled: false,
    _target: null,
    _callback: null,
    _isSelected: false,
    _className: "MenuItem",

    /**
     * Constructor of cc.MenuItem
     * @param {function|String} callback
     * @param {cc.Node} target
     */
    ctor: function (callback, target) {
        var nodeP = cc.Node.prototype;
        nodeP.ctor.call(this);
        this._target = null;
        this._callback = null;
        this._isSelected = false;
        this._enabled = false;

        nodeP.setAnchorPoint.call(this, 0.5, 0.5);
        this._target = target || null;
        this._callback = callback || null;
        if (this._callback) {
            this._enabled = true;
        }
    },

    /**
     * return whether MenuItem is selected
     * @return {Boolean}
     */
    isSelected: function () {
        return this._isSelected;
    },
    /**
     * only use for jsbinding
     * @param value
     */
    setOpacityModifyRGB: function (value) {
    },
    /**
     * only use for jsbinding
     * @returns {boolean}
     */
    isOpacityModifyRGB: function () {
        return false;
    },

    /**
     * set the target/selector of the menu item
     * @param {function|String} selector
     * @param {cc.Node} rec
     * @deprecated since v3.0
     */
    setTarget: function (selector, rec) {
        this._target = rec;
        this._callback = selector;
    },

    /**
     * return whether MenuItem is Enabled
     * @return {Boolean}
     */
    isEnabled: function () {
        return this._enabled;
    },

    /**
     * set enable value of MenuItem
     * @param {Boolean} enable
     */
    setEnabled: function (enable) {
        this._enabled = enable;
    },

    /**
     * initializes a cc.MenuItem with callback
     * @param {function|String} callback
     * @param {cc.Node} target
     * @return {Boolean}
     */
    initWithCallback: function (callback, target) {
        this.anchorX = 0.5;
        this.anchorY = 0.5;
        this._target = target;
        this._callback = callback;
        this._enabled = true;
        this._isSelected = false;
        return true;
    },

    /**
     * return rect value of cc.MenuItem
     * @return {cc.Rect}
     */
    rect: function () {
        var locPosition = this._position, locContentSize = this._contentSize, locAnchorPoint = this._anchorPoint;
        return cc.rect(locPosition.x - locContentSize.width * locAnchorPoint.x,
            locPosition.y - locContentSize.height * locAnchorPoint.y,
            locContentSize.width, locContentSize.height);
    },

    /**
     * set the cc.MenuItem selected same as setIsSelected(true)
     */
    selected: function () {
        this._isSelected = true;
    },

    /**
     * set the cc.MenuItem unselected same as setIsSelected(false)
     */
    unselected: function () {
        this._isSelected = false;
    },

    /**
     * set the callback to the menu item
     * @param {function|String} callback
     * @param {cc.Node} target
     */
    setCallback: function (callback, target) {
        this._target = target;
        this._callback = callback;
    },

    /**
     * call the selector with target
     */
    activate: function () {
        if (this._enabled) {
            var locTarget = this._target, locCallback = this._callback;
            if (!locCallback)
                return;
            if (locTarget && cc.isString(locCallback)) {
                locTarget[locCallback](this);
            } else if (locTarget && cc.isFunction(locCallback)) {
                locCallback.call(locTarget, this);
            } else
                locCallback(this);
        }
    }
});

cc.MenuItem对Node的selected,unselected, activate方法进行了重新,提供了回调函数设置设置,修改按钮锚点等功能,自定义的按钮从cc.MenuItem继承即可。根据需要复写一下方法:

  • selected,被按下时调用(相当于pressed)
  • unselected,按下后松开时调用(相当于released)
  • activate,按下松开完成后调用(相当于click)

将要显示的内容元素如cc.Sprite通过this.addChild加入即可显示,在上述方法中通过控制这些元素的visible和位置属性可以实现自定义按钮的各种效果,还可以runAction。

 下面给出自己的一个按钮示例:

/* implementation element(key button) used by keyboard */
var KeyMenuItem = cc.MenuItem.extend({
    _label: null,
    _normal_sprite: null,
    _press_sprite: null,
    FONT_EXTENDED_BOTTOM_PADDING_FACTOR: 0.75,
    FONT_BOTTOM_PADDING_FACTOR: 0,
    FONT_SIZE_FACTOR: 0.4,

    ctor: function(normal_img, press_img, text, callback, target) {
        cc.MenuItem.prototype.ctor.call(this);
        this.initWithCallback(callback, target);

        var normal_sprite = new cc.Sprite(normal_img);
        var press_sprite = new cc.Sprite(press_img);

        this._normal_sprite = normal_sprite;
        this._press_sprite = press_sprite;

        this.width = normal_sprite.width;
        this.height= normal_sprite.height;var label = new cc.LabelTTF(text, "Arial", Math.ceil(normal_sprite.width * this.FONT_SIZE_FACTOR));
        label.setColor(cc.color(0, 0, 0, 255));
        this._label = label;
        this.setNormal();

        this.addChild(label, 2);
        this.addChild(press_sprite, 0);
        this.addChild(normal_sprite, 1);

        this.cascadeColor = true;
        this.cascadeOpacity = true;
    },

    selected: function() {
        cc.MenuItem.prototype.selected.call(this);
        if (this._enabled) {
            this.setPress();
            cc.log("custom button selected");
        }
        cc.audioEngine.playMusic(res.button_press_mp3, false);
    },

    unselected: function() {
        cc.MenuItem.prototype.unselected.call(this);
        if (this._enabled) {
            this.setNormal();
            cc.log("custom button unselected");
        }
    },

    setNormal: function() {
        this.setLabelNormal();
        this.setSpriteNormal();
    },

    setPress: function() {
        this.setLabelPressed();
        this.setSpritePressed();
    },

    setLabelNormal: function () {
        var label = this._label;
        var nsprite = this._normal_sprite;
        label.setPosition(nsprite.width / 2.0, this.height * (0.5 + this.FONT_BOTTOM_PADDING_FACTOR));
    },

    setLabelPressed: function() {
        var label = this._label;
        var psprite = this._press_sprite;
        var factor = this.FONT_EXTENDED_BOTTOM_PADDING_FACTOR;
        label.setPosition(psprite.width / 2.0, psprite.height * factor);
    },

    setSpriteNormal: function () {
        var nsprite = this._normal_sprite;
        var psprite = this._press_sprite;

        psprite.visible = false;
        nsprite.visible = true;

        nsprite.setPosition(this.width / 2.0, this.height / 2.0);
        psprite.setPosition(psprite.width / 2.0, psprite.height / 2.0);
    },

    setSpritePressed: function() {
        var nsprite = this._normal_sprite;
        var psprite = this._press_sprite;

        psprite.visible = true;
        nsprite.visible = false;

        psprite.setPosition(psprite.width / 2.0, psprite.height / 2.0);
        nsprite.setPosition(this.width / 2.0, this.height / 2.0);
    },

    setEnabled: function(enabled) {
        var nsprite = this._normal_sprite;
        var psprite = this._press_sprite;
        var label = this._label;
        if (this._enabled != enabled) {
            if (enabled == false) {
                this.setOpacity(0);
            } else {
                this.setOpacity(255);
            }
        }
        cc.MenuItem.prototype.setEnabled.call(this, enabled);
    },

    getString: function () {
        return this._label.getString();
    }
});

 其中:

this.cascadeOpacity = true;

级联不透明度,可以使得对按钮设置透明度时整体透明度也一起变化

 

posted @ 2014-10-12 11:33  卖程序的小歪  阅读(1217)  评论(0编辑  收藏  举报