Cocos3.4 横版游戏制作-《KillBear》-添加摇杆并控制Hero

转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。
资源为网上寻找的,仅研究学习用,若是侵犯版权请通知本人整改。


上一篇:Cocos3.4 横版游戏制作-《KillBear》-加入Hero
在这一篇中,
上半部分我们将在控制层OperateLayer中加入一个摇杆,并通过摇杆控制Hero
下半部分我们控制Hero防止其跑出地图和跑上墙


开发环境


  • win64 : vs2010
  • Cocos2d-x v3.4Final
  • TexturePackerGUI
  • MapEdit

代码构建A

管理Operate

我们先做一个Joystick

摇杆Joystick

  • .h
class Joystick : public Sprite
{
public:
    Joystick();
    ~Joystick();
    virtual bool init();
    virtual void onTouchesBegan(const std::vector& touches, cocos2d::Event *unused_event);
    virtual void onTouchesMoved(const std::vector& touches, cocos2d::Event *unused_event);
    virtual void onTouchesEnded(const std::vector& touches, cocos2d::Event *unused_event); 
    void setJoystick(Vec2 point);
    CREATE_FUNC(Joystick);
private:
    void showJoystick();
    void hideJoystick();
    void updateJoystick(Touch* touch);
    int m_pJoystickr;
    int m_pJoystickR;
    Sprite *m_pJoystick;
    Sprite *m_pJoystickBg;
    Vec2 start;
};
  • .cpp
bool Joystick::init()
{
    bool ret = false;
    do {
        CC_BREAK_IF( !Sprite::init() );
        m_pJoystickBg = Sprite::create("JoystickBg.png");//背景
        m_pJoystick = Sprite::create("Joystick.png");//摇杆
        this->addChild(m_pJoystickBg, 0);
        this->addChild(m_pJoystick, 1);
        this->hideJoystick();
        //this->showJoystick();
        m_pJoystickR= m_pJoystickBg->getContentSize().width/2;
        m_pJoystickr= m_pJoystick->getContentSize().width/2;

        //新的API注册这么写
        auto listener = EventListenerTouchAllAtOnce::create();
        listener->onTouchesBegan = CC_CALLBACK_2(Joystick::onTouchesBegan, this);
        listener->onTouchesMoved = CC_CALLBACK_2(Joystick::onTouchesMoved, this);
        listener->onTouchesEnded = CC_CALLBACK_2(Joystick::onTouchesEnded, this); 
        _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

        ret = true;
    } while(0);

    return ret;
}

void Joystick::showJoystick()
{
    //显示摇杆
    m_pJoystick->setVisible(true); 
    m_pJoystickBg->setVisible(true);
}

void Joystick::hideJoystick()
{
    //隐藏摇杆
    //m_pJoystick->setPosition(m_pJoystickBg->getPosition());
    m_pJoystick->setVisible(false);
    //m_pJoystickBg->setVisible(false);
    m_pJoystickBg->setVisible(true);
}


void Joystick::onTouchesBegan(const vector& touches, Event *unused_event)
{
    //按下事件处理
    std::vector::const_iterator touchIter = touches.begin();
    Touch* touch = (Touch*)(*touchIter);
    if(m_pJoystick->getBoundingBox().containsPoint(touch->getLocation()))
    {
        this->showJoystick();
        updateJoystick(touch);
        CCLOG("***************");
        CCLOG("update touch:%f %f",touch->getLocation().x,touch->getLocation().y);

        return;
    }
}

void Joystick::onTouchesMoved(const vector& touches, Event *unused_event)
{
    //移动时处理
    std::vector::const_iterator touchIter = touches.begin();
    Touch* touch = (Touch*)(*touchIter);
    if(m_pJoystick->isVisible())
    {
        updateJoystick(touch);
        return;
    }
}

void Joystick::onTouchesEnded(const vector& touches, Event *unused_event)
{
    //离开是处理
    //m_pJoystick->runAction(MoveTo::create(0.08f,start));
    //m_pJoystick->setPosition(start);
    //global->hero->onStop();
    this->hideJoystick();

}
void Joystick::setJoystick(Vec2 point)
{
    //将这个摇杆的放在某个坐标上
    start =point;
    m_pJoystickBg->setPosition(start);
    m_pJoystick->setPosition(m_pJoystickBg->getPosition());
}
void Joystick::updateJoystick(Touch* touch)
{
    //更新摇杆状态
    //我用向量来判断
    Vec2 hit = touch->getLocation();
    float distance = start.getDistance(hit);
    Vec2 direction = (hit - start).getNormalized();
    //为了防止摇杆移出摇杆背景
    if(distance < m_pJoystickr/2)
    {
        m_pJoystick->setPosition(start + (direction * distance));
    }else if(distance >m_pJoystickr) {
        m_pJoystick->setPosition(start + (direction * m_pJoystickr));
    }else {
        m_pJoystick->setPosition(start + (direction * m_pJoystickr/2));
    }

    //global->hero->onMove(direction,distance);
}

这个JoyStick的写法我用了向量,不用笛卡尔坐标(xOy),我认为这样写更好理解它.
而且这个摇杆分为2段,

  • 某个范围A内,随意移动
  • 超出最大范围B-接触点移出了摇杆背景时,将其设定在最大边沿B处
  • 在这两个范围A-B之间,会沿着A的边沿放置

最大的好处:我可以通过摇杆移动距离控制角色 (走),(跑)切换
相当的讨厌必须按2下才能让角色执行跑动,自己写简单点行不行啊?

控制层OperateLayer

  • .h
#include "Joystick.h"
  • .cpp

init中

        auto m_pjoystick = Joystick::create();
        m_pjoystick->setJoystick(Vec2(50,50));
        this->addChild(m_pjoystick);

效果A

大功告成:


接下来我提一个问题:Joystick和我们的Hero在不同层,如何让这个摇杆控制我们的Hero呢?

实现的方法有很多.
这里通过创建另一个Global,并将Joystick和Hero”注册”上去,通过Joystick控制Global中的Hero,也就是直接控制Hero实现
Global是全局单类
Cocos3.4 横版游戏制作-《KillBear》-添加摇杆并控制Hero_第1张图片


代码构建B

首先引入一个Single.h

Other

唯一实例Single

这是一个优秀的单例实例(这是从别人的代码中找到的)

  • .h
#ifndef _SINGLETON_H
#define _SINGLETON_H

using namespace std;

template <class T>
class Singleton
{
public:
    //获取类的唯一实例
    static inline T* instance();
    //释放类的唯一实例
    void release();
protected:
    Singleton(void){}
    ~Singleton(void){}
    static T* _instance;
};

template <class T>
inline T* Singleton::instance()
{
        if(NULL == _instance){
            _instance = new T;
        }
    return _instance;
}

template <class T>
void Singleton::release()
{
    if (!_instance)
        return;
    delete _instance;
    _instance = 0;
}

//cpp文件中需要先声明静态变量
#define DECLARE_SINGLETON_MEMBER(_Ty)   \
    template <> _Ty* Singleton<_Ty>::_instance = NULL;

#endif//_SINGLETON_H

全局类Global

我把后面需要用的都放进去了,头文件好说.
- .h

#ifndef _GLOBAL_H_
#define _GLOBAL_H_
#include "cocos2d.h"
USING_NS_CC; 

#include "Singleton.h"
#include "GameLayer.h"
#include "OperateLayer.h"
#include "StateLayer.h"

//需引入以下类,否则在这些类中访问单例对象会报错
class GameLayer;
class OperateLayer;
class StateLayer;
class Hero;
class Enemy;

//全局单例
class Global :public Singleton<Global>
{
public:
    Global(void);
    ~Global(void);

    //GameScene *gameScene;

    GameLayer *gameLayer;           //游戏层
    OperateLayer *operateLayer;     //操作层
    StateLayer * stateLayer;        //状态层
    Hero *hero;                     //英雄
    __Array *enemies;               //敌人
    TMXTiledMap *tileMap;           //地图

    Point tilePosFromLocation(Vec2 MovePoint, TMXTiledMap *map = NULL);//将point转换成地图GID的point
    bool  tileAllowMove(Vec2 MovePoint);
};

#define global Global::instance()

#endif
  • .cpp
#include "Global.h"

DECLARE_SINGLETON_MEMBER(Global);
Global::Global(void)
{
}


Global::~Global(void)
{
    CC_SAFE_DELETE(gameLayer);
    CC_SAFE_DELETE(operateLayer);
    CC_SAFE_DELETE(stateLayer);
    CC_SAFE_DELETE(hero);
    CC_SAFE_DELETE(enemies);
    //CC_SAFE_DELETE(tileMap);
    gameLayer = NULL;           
    operateLayer= NULL; 
    stateLayer= NULL;       
    hero= NULL;             
    enemies= NULL;      
    tileMap= NULL;      
}
Point Global::tilePosFromLocation(Point MovePoint, TMXTiledMap *map) 
{
    Point point = MovePoint - map->getPosition();
    Point pointGID = Vec2::ZERO;
    pointGID.x = (int) (point.x / map->getTileSize().width); 
    pointGID.y = (int) ((map->getMapSize().height * map->getTileSize().height - point.y) / map->getTileSize().height); 
    return pointGID; 
}

bool Global::tileAllowMove(Point MovePoint)
{
    TMXLayer *floor = global->tileMap->getLayer("Floor");
    Point tileGid = tilePosFromLocation(MovePoint,global->tileMap);
    auto allowpoint =floor->getTileGIDAt(tileGid);
    if(0 == allowpoint)
    {
        return false;
    }
    return true;
}

在需要用到的地方注册他比如GameLayer中:

  • .h
#include "Global.h"

根据Global.h中所定义的,在对应cpp中需要添加诸如
- .cpp

GameLayer::GameLayer()
{
    global->gameLayer=this;
}
OperateLayer::OperateLayer()
{
    global->operateLayer=this;
}
StateLayer::StateLayer()
{
    global->stateLayer = this;
}

………需要添加的地方………太多了 还是看代码吧.
不过后来如果有新的头文件我就扔上一个.
目前需要添加的头文件
MapLayer
GameLayer
StateLayer
OpreateLayer
Hero
JoyStick


角色Role

Hero

更新我们的Hero,添加下面代码
- .h

    void onMove(Vec2 direction, float distance);
    void onStop();
    void onAttack(int number);
    void updateSelf();
  • .cpp
void Hero::onMove(Vec2 direction, float distance)//移动调用
{
    this->setFlippedX(direction.x < 0 ? true : false);
    this->runWalkAction();
    Vec2 velocity = direction * (distance < 33 ? 1 : 3);
    this->setVelocity(velocity);
}
void Hero::onStop()//站立
{
    this->runIdleAction();
    this->setVelocity(Vec2::ZERO);
}
void Hero::onAttack(int number)//执行攻击
{
    this->runNomalAttackA();
}
void Hero::updateSelf()//刷新自己
{
    if(this->getCurrActionState() == ACTION_STATE_WALK)
    {
        Vec2 currentP= this->getPosition();             //当前坐标
        Vec2 expectP = currentP + this->getVelocity();  //期望坐标
        Vec2 actualP = expectP;                         //实际坐标

        this->setPosition(actualP);
        this->setLocalZOrder( Director::getInstance()->getVisibleSize().height - this->getPositionY());
    }
}

之后我们在Joystick中调用Hero的onMove,就可以让其移动了
其他代码我们后期再用

控制Operate

Joystick

去掉里面的onTouchesEnded,updateJoystick中关于global->hero的//
实现Joystick控制Global中的hero

效果B


额,主角的状态切换了,但是还是不能动?什么情况


找到原因了…..GameLayer中没有更新主角坐标啊

Game

GameLayer

  • .h
    void update(float dt);
    void updateHero(float dt);
  • .cpp
    init中启动默认定时器update
        this->scheduleUpdate();

然后是实现方法

void GameLayer::update(float dt)
{
    this->updateHero(dt);
}

而updateHero

void GameLayer::updateHero(float dt)
{
    m_pHero->updateSelf();//自更新状态
}

效果C

结语

至此,我们终于实现了标题的效果:添加摇杆并控制Hero
不过还是有一堆BUG,比如主角能跑到天上,主角移出了地图
地图不会动等.
将在下一篇中消除这些BUG

你可能感兴趣的:(移动游戏开发,游戏编程,cocos2d-x)