这个系列今天进入第三课,在第一课中我们学习了添加地图,第二课学习了添加英雄人物。在这一篇中,我们将分两部分进行。  上半部分我们将在控制层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

1#include "Joystick.h"

.cpp

init中

1

2

3auto m_pjoystick = Joystick::create();

m_pjoystick->setJoystick(Vec2(50,50));

this->addChild(m_pjoystick);

效果A

大功告成:


20150209133433092.gif

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

实现的方法有很多。

这里通过创建另一个Global,并将Joystick和Hero”注册”上去,通过Joystick控制Global中的Hero,也就是直接控制Hero实现。

Global是全局单类。


20150209134417404.jpg

代码构建B

首先引入一个Single.h

Other

唯一实例Single

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

.h

#ifndef _SINGLETON_H

#define _SINGLETON_H

using namespace std;

template

class Singleton

{

public:

//获取类的唯一实例

static inline T* instance();

//释放类的唯一实例

void release();

protected:

Singleton(void){}

~Singleton(void){}

static T* _instance;

};

template

inline T* Singleton::instance()

{

if(NULL == _instance){

_instance = new T;

}

return _instance;

}

template

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

{

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

1#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


20150209145209045.gif

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

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

Game

GameLayer

.h

1

2void update(float dt);

void updateHero(float dt);

.cpp

init中启动默认定时器update

1this->scheduleUpdate();

然后是实现方法:

1

2

3

4void GameLayer::update(float dt)

{

this->updateHero(dt);

}

而updateHero

void GameLayer::updateHero(float dt)

{

m_pHero->updateSelf();//自更新状态

}

效果C


20150209150356990.gif

结语

至此,我们终于实现了标题的效果:添加摇杆并控制Hero

不过还是有一堆BUG,比如主角能跑到天上,主角移出了地图,地图不会动等。

将在下一篇中消除这些BUG。