游戏开发总是有些特殊,一般的预制的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;
级联不透明度,可以使得对按钮设置透明度时整体透明度也一起变化