cocos2dx游戏开发学习——技能按钮(雏形)

此技能按钮主要有一下几个功能

  1. 技能冷却效果
  2. 技能蓄力

效果图:


技能按钮效果图

代码如下:

SkillButton.h

#ifndef SkillButton_h
#define SkillButton_h

#include <stdio.h>
#include "cocos2d.h"


USING_NS_CC;

class SkillButton:public Node
{
public:
    typedef std::function<void(float pressTime)> alSkillClickCallback;
    typedef std::function<void(float pressTime)> alSkillTouchCallback;
    typedef std::function<void(void)> alSkillCDEndCallback;
    
    
    
public:
    static SkillButton* create(const std::string normalImage,const std::string coolImage = "",float skillTime = 0);
    
    SkillButton();
    virtual ~SkillButton();
    
    /**
      技能是否处于冷却中
     */
    virtual const bool& isSkillCD() const { return _isSkillCD;}
    
    /**
     *  设置正常状态下的Image
     */
    virtual void setupNormalImage(const std::string normalImage);
    /**
     *  设置冷却状态下的Image
     */
    virtual void setupCoolImage(const std::string coolImage);
    
    
    
    /**
     技能的点击回调
     注意:如果不开启按压更新的话  按压时间一直为0
     */
    virtual void addClickCallback(const alSkillClickCallback& callback);
    
    /**
     技能按压开始的回调 按压时间为 0
     */
    virtual void addTouchBeginCallback(const alSkillTouchCallback& callback);
    
    
    /**
     技能按压结束的回调
     注意:如果不开启按压更新的话  按压时间一直为0
     */
    virtual void addTouchEndCallback(const alSkillTouchCallback& callback);
    
    /**
     技能按压更新回调 (每隔一段时间 进行更新)
     注意:如果不开启按压更新的话,此回调函数不会调用
     @param callback 回调函数
     @param interval 时间间隔应该大于等于0.1且是0.1的倍数  如果时间间隔为0 则表示不会启用更新函数
     */
    virtual void addTouchUpdateCallback(const alSkillTouchCallback& callback,float interval);
    
    /**
     添加技能CD结束后的回调
     */
    virtual void addSkillCDEndCallback(const alSkillCDEndCallback& callback);
    

protected:
    virtual bool init() override;
    virtual bool init(const std::string normalImage,const std::string coolImage,float skillTime);
    /**
     添加触摸事件
     */
    virtual void addTouchListener();
    
    /**
     执行技能按钮触摸开始操作
     */
    virtual bool doSkillTouchBegin(const Point& touchPoint);
    
    /**
     执行技能按钮触摸移动操作
     */
    virtual void doSkillTouchMoved(const Point& touchPoint);
    
    /**
     执行技能按钮触摸结束操作
     */
    virtual void doSkillTouchEnd(const Point& touchPoint);
    
    
    /**
      开始技能冷却
     */
    virtual void startSkillCDAction();
    
    /**
     重置技能冷却
     */
    virtual void resetSkillCDAction();
    
    /**
     减少冷却时间
     */
    virtual void reduceSkillCDTimeAction(float time);
    
protected:
    Sprite* _pSkill;
    ProgressTimer* _pProgressCD;
    

    //** 技能是否可用 *//
    CC_PROPERTY_PASS_BY_REF(bool, _skillEnable, SkillEnable);
    //** 技能是否处于冷却时间 *//
    bool _isSkillCD = false;
    //** 技能冷却时间 *//
    CC_SYNTHESIZE_PASS_BY_REF(float, _skillCoolTime, SkillCoolTime);
    //** 技能按压计时更新是否可用 *//
    CC_SYNTHESIZE_PASS_BY_REF(bool, _updateEnable, UpdateEnable);
    //** 按压时间 *//
    CC_SYNTHESIZE_READONLY_PASS_BY_REF(float, _pressTime, PressTime);
    //**  *//
    float _tUpdateInerval = 0;
    
    
    std::string _normalImageName;
    std::string _coolImageName;
    alSkillClickCallback _clickCallback;
    alSkillTouchCallback _touchBeginCallback;
    alSkillTouchCallback _touchEndCallback;
    alSkillClickCallback _touchUpdateCallback;
    alSkillCDEndCallback _skillCDEndCallback;
    
    
private:
    //** 是否技能触摸结束 *//
    bool _isSkillTouchEnd = true;
    //** 更新时间增量数,主要用于 *//
    float _deltaSum = 0;
};

#endif /* SkillButton_h */

SkillButton.cpp

#include "SkillButton.h"


#define SCHEDULE_SKILL_UPDATE_TIME "SCHEDULE_SKILL_UPDATE_TIME"
#define SKILL_UPDATE_INTERVAL 0.1

SkillButton::SkillButton()
{
    _pSkill = nullptr;
    _pProgressCD = nullptr;
    
    
    _skillEnable = true;
    _skillCoolTime = 0;
    _isSkillCD = false;
    _updateEnable = false;
    _pressTime = 0;
    _tUpdateInerval = 0;
    
    _normalImageName = "";
    _coolImageName ="";
    
    _clickCallback = nullptr;
    _touchBeginCallback = nullptr;
    _touchEndCallback = nullptr;
    _touchUpdateCallback = nullptr;
    _skillCDEndCallback = nullptr;
    
    
    _deltaSum = 0;
}


SkillButton::~SkillButton()
{
    
}

SkillButton* SkillButton::create(const std::string normalImage,const std::string coolImage,float skillTime)
{
    SkillButton* skillBtn = new (std::nothrow) SkillButton();
    if (skillBtn && skillBtn->init(normalImage, coolImage, skillTime)) {
        return skillBtn;
    }else{
        CC_SAFE_DELETE(skillBtn);
        return nullptr;
    }
}


bool SkillButton::init()
{
    if (Node::init()) {
        return true;
    }
    return false;
}

bool SkillButton::init(const std::string normalImage, const std::string coolImage, float skillTime)
{
    if (init()) {
        _skillCoolTime = skillTime;
        setupNormalImage(normalImage);
        setupCoolImage(coolImage);
        
        addTouchListener();
        return true;
    }
    return false;
}


void SkillButton::addTouchListener()
{
    auto listener = EventListenerTouchOneByOne::create();
    listener->setSwallowTouches(false);
    listener->onTouchBegan = [this](Touch* touch, Event* evnt)->bool
    {
        return this->doSkillTouchBegin(touch->getLocation());
    };
    
    listener->onTouchMoved = [this](Touch* touch, Event* evnt)
    {

        this->doSkillTouchMoved(touch->getLocation());
    };
    
    auto touchEndedCallback = [this](Touch* touch, Event* evnt)
    {
        this->doSkillTouchEnd(touch->getLocation());
    };
    
    listener->onTouchCancelled = touchEndedCallback;
    
    listener->onTouchEnded = touchEndedCallback;
    
    Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);
    
}


bool SkillButton::doSkillTouchBegin(const Point& touchPoint)
{
    // 判断点击区域
    Size contentSize = _pSkill->getContentSize();
    Rect btnRect = Rect(this->getPositionX() - contentSize.width/2, this->getPositionY()- contentSize.height/2, contentSize.width, contentSize.height);
    if (btnRect.containsPoint(touchPoint)) {
        if (_skillEnable && !_isSkillCD) {
            // 开启计时器
            if (_updateEnable) {
                unschedule(SCHEDULE_SKILL_UPDATE_TIME);
                _pressTime = 0;
                _deltaSum = 0;
                schedule([this](float delta){
                    _pressTime += delta;
                    _pressTime = (float)((int)(_pressTime * 1000 + 0.5f))/1000;//保留小数点后三位
                    if (_tUpdateInerval>= delta && _touchUpdateCallback)
                    {
                        _deltaSum+= delta;
                        _deltaSum = (float)((int)(_deltaSum * 1000 + 0.5f))/1000;// 保留小数点后三位
                        if (_deltaSum >= _tUpdateInerval) {
                            _touchUpdateCallback(_pressTime);
                            _deltaSum = 0;
                        }
                    }
                    
                }, SKILL_UPDATE_INTERVAL, SCHEDULE_SKILL_UPDATE_TIME);
            }
            // 调用开始回调
            if (_touchBeginCallback) {
                _touchBeginCallback(0);
            }
            _isSkillTouchEnd = false;
            return true;
        }
    }
    _isSkillTouchEnd = true;
    return false;
}


void SkillButton::doSkillTouchMoved(const cocos2d::Point &touchPoint)
{
    if (!_isSkillTouchEnd) {
        Size touchSize = Size(_pSkill->getContentSize().width*1.2, _pSkill->getContentSize().height*1.2);
        Rect touchRect = Rect(this->getPositionX() - touchSize.width/2, this->getPositionY()- touchSize.height/2, touchSize.width, touchSize.height);
        
        if (!touchRect.containsPoint(touchPoint)) {
            this->doSkillTouchEnd(touchPoint);
        }
    }
}


void SkillButton::doSkillTouchEnd(const Point& touchPoint)
{
    if (!_isSkillTouchEnd)
    {
        // 停止计时器
        unschedule(SCHEDULE_SKILL_UPDATE_TIME);
        
        float pressTime = _pressTime;
        _pressTime = 0;
        _deltaSum = 0;
        
        if (_touchEndCallback) {
            _touchEndCallback(pressTime);
        }
        if (_clickCallback) {
            _clickCallback(pressTime);
        }
        
        // 开始读冷却
        this->startSkillCDAction();
        _isSkillTouchEnd = true;
    }
}


void SkillButton::startSkillCDAction()
{
    if (_skillCoolTime > 0) {
        _isSkillCD = true;
        _pProgressCD->setPercentage(100);
        _pProgressCD->stopAllActions();
        _pProgressCD->runAction(Sequence::create(ProgressTo::create(_skillCoolTime, 0),CallFunc::create([this]{
            // 技能冷却结束
            _isSkillCD = false;
            // 技能冷却完成后的回调
            if (_skillCDEndCallback) {
                _skillCDEndCallback();
            }
        }), NULL));
    }else{
        _pProgressCD->stopAllActions();
        _pProgressCD->setPercentage(0);
        _isSkillCD = false;
        // 技能冷却完成后的回调
        if (_skillCDEndCallback) {
            _skillCDEndCallback();
        }
    }
    
    
}

void SkillButton::resetSkillCDAction()
{
    if (_isSkillCD) {
        _pProgressCD->stopAllActions();
        _pProgressCD->setPercentage(0);
        _isSkillCD = false;
        // 技能冷却完成后的回调
        if (_skillCDEndCallback) {
            _skillCDEndCallback();
        }
    }
}

void SkillButton::reduceSkillCDTimeAction(float time)
{
    if (_isSkillCD) {
        _pProgressCD->stopAllActions();
        float percent = _pProgressCD->getPercentage();
        float remainingTime = MIN(0, percent*_skillCoolTime - time);
        if (remainingTime > 0) {
            float nowPercent = 100.f / _skillCoolTime * remainingTime;
            _pProgressCD->setPercentage(nowPercent);
            _pProgressCD->runAction(Sequence::create(ProgressTo::create(remainingTime, 0),CallFunc::create([this]{
                // 技能冷却结束
                _isSkillCD = false;
                // 技能冷却完成后的回调
                if (_skillCDEndCallback) {
                    _skillCDEndCallback();
                }
            }), NULL));
        }else{
            resetSkillCDAction();
        }
    }
}




//============================
// !!!: public
//============================

void SkillButton::setSkillEnable(const bool &enable)
{
    if (_skillEnable != enable) {
        _skillEnable = enable;
        if (!_skillEnable) {
            _pProgressCD->stopAllActions();
            _pProgressCD->setPercentage(100);
            _isSkillCD = true;
        }
    }
}


const bool& SkillButton::getSkillEnable() const
{
    return _skillEnable;
}


void SkillButton::setupNormalImage(const std::string normalImage)
{
    _normalImageName = normalImage;
    CCASSERT(!normalImage.empty(), "SkillButton : normalImage is empty");
    auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(normalImage);
    if (_pSkill) {
        (frame ? _pSkill->setSpriteFrame(frame) : _pSkill->setSpriteFrame(normalImage));
    }else{
        _pSkill = frame ? Sprite::createWithSpriteFrame(frame) : Sprite::create(normalImage);
        this->addChild(_pSkill,0);
    }
    setContentSize(_pSkill->getContentSize());
    
    if (_coolImageName.empty()) {
        setupCoolImage("");
    }
    
}

void SkillButton::setupCoolImage(const std::string coolImage)
{
    _coolImageName = coolImage;
    Sprite* coolSprite = nullptr;
    if (coolImage.empty()) {
        auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(_normalImageName);
        coolSprite = frame ? Sprite::createWithSpriteFrame(frame) : Sprite::create(_normalImageName);
        coolSprite->setColor(Color3B(64, 64, 64));
    }else{
        auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(coolImage);
        coolSprite = frame ? Sprite::createWithSpriteFrame(frame) : Sprite::create(coolImage);
    }
    if (_pProgressCD) {
        _pProgressCD->setSprite(coolSprite);
    }else{
        _pProgressCD = ProgressTimer::create(coolSprite);
        _pProgressCD->setType(ProgressTimer::Type::RADIAL);
        _pProgressCD->setReverseProgress(true);
        _pProgressCD->setPercentage(0);
        this->addChild(_pProgressCD,1);
    }
}

void SkillButton::addClickCallback(const SkillButton::alSkillClickCallback &callback)
{
    _clickCallback = callback;
}

void SkillButton::addTouchBeginCallback(const SkillButton::alSkillTouchCallback &callback)
{
    _touchBeginCallback = callback;
}

void SkillButton::addTouchEndCallback(const SkillButton::alSkillTouchCallback &callback)
{
    _touchEndCallback = callback;
}

void SkillButton::addTouchUpdateCallback(const SkillButton::alSkillTouchCallback &callback, float interval)
{
    if (interval >= SKILL_UPDATE_INTERVAL) {
        _touchUpdateCallback = callback;
        _tUpdateInerval = interval;
    }
}

void SkillButton::addSkillCDEndCallback(const alSkillCDEndCallback& callback)
{
    _skillCDEndCallback = callback;
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容