当两手拇指分别点按左右屏幕来控制人物左右移动的时候,当点按频繁了容易出现人物不听使唤的情况,这是由于两只手同时按的时候,会出现一只一方触摸失效的情况。今天就和大家分享一下我解决这个问题的办法。
今天重新建了一个工程,从0开始,一步步制作人物移动的效果。
1、首先,我们需要定义一个人物的类,人物是我们移动的对象,这个类将人物的所有功能都包括进去。目前我们需要的功能仅仅是人能够移动。
由于主要是讲触摸功能的优化,所以Man类的创建就不多说了,贴出代码,各位自己看吧。
//man.h
class Man : public cocos2d::Node {
public:
static Man *create(const std::string &textureFile);
virtual bool init(const std::string &textureFile);
void moveRight(float delta);
private:
cocos2d::Sprite *sprite;
};
//man.cpp
Man *Man::create(const std::string &textureFile) {
Man *p = new Man;
if (p && p->init(textureFile)) {
p->autorelease();
}
else {
delete p;
p = nullptr;
}
return p;
}
bool Man::init(const std::string &textureFile) {
if (!Node::init())
return false;
//根据纹理名称创建精灵
sprite = Sprite::create(textureFile);
addChild(sprite);
return true;
}
void Man::moveRight(float delta) {
setPositionX(getPositionX() + delta);
}
2、创建人物,并设定触摸监听
人物建好了,我们需要设定触摸监听,目的是我们点击屏幕后,人物可以根据我们点击的方向进行移动。
首先,在HelloWorld的init函数中加入代码创建人物:
auto man = Man::create("man.png");
addChild(man);
man->setPosition(visibleSize / 2);
然后,同样在init函数中创立触摸监听,这里我们只需要用到单点触摸:
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::touchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::touchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::touchEnded, this);
listener->setSwallowTouches(true);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, man);
3、基本的框架搭得差不多了,下面我们就要构建细节了。
重点就在于监听事件的回调函数如何编写,我们需要在触摸开始时,人物开始移动,触摸结束时,人物停止移动。
我用的方法是,另外设立一个data member用来记录人物的状态,触摸回调函数来决定这个data member的值,而用一个schedule来检测这个data member根据它来对人物进行移动,或者立定。
首先在Man class definition中新增一个成员,叫manState:
enum {
STILL = 0,
RUN_LEFT = 1,
RUN_RIGHT = 2
};
int manState = STILL;
接下来,上面的方向分两步走,首先编写schedule:
我的方法是,在Man类中新增一个run函数,来根据manState决定man的运动,然后在调用它的类的update函数中,只需要运行run就可以了,比较简便:
void Man::run(float x) {
switch (manState) {
case STILL:
break;
case RUN_LEFT:
moveRight(-x); break;
case RUN_RIGHT:
moveRight(x); break;
}
}
这样子,run函数就会根据人物的manState状态值来决定人物的走向。
下面到了最关键的时刻,就是编写触摸事件:
首先,用最直观的方法编写触摸事件,然后再逐步修改完善。
bool HelloWorld::touchBegan(cocos2d::Touch *touch, cocos2d::Event *event) {
//获得目标
auto man = reinterpret_cast<Man *>(event->getCurrentTarget());
//如果触摸点在屏幕右边,则往右移动,否则往左
if (touch->getLocation().x >= Director::getInstance()->getVisibleSize().width / 2) {
man->manState = Man::RUN_RIGHT;
}
else {
man->manState = Man::RUN_LEFT;
}
return true;
}
void HelloWorld::touchMoved(cocos2d::Touch *touch, cocos2d::Event *event) {
}
void HelloWorld::touchEnded(cocos2d::Touch *touch, cocos2d::Event *event) {
//获得目标
auto man = reinterpret_cast<Man *>(event->getCurrentTarget());
//触摸停止,人物停止移动
man->manState = Man::STILL;
}
4、最后,在HelloWorld中enable schedule,在update函数中调用run函数。
需要一个额外的处理,就是将之前创建的man的声明放到类定义中,这样update函数就可以access到man了。
class HelloWorld : public cocos2d::Layer
{
//...
Man *man = nullptr;
virtual void update(float delta);
//...
};
bool HelloWorld::init() {
//...
scheduleUpdate();
//...
}
void HelloWorld::update(float delta) {
man->run(5);//5表示,每次调用移动5个像素,一秒钟算60次调用update的话就是300个像素每秒。
}
然后编译,成功后就是下面的样子啦,精灵比较粗糙 ^ ^!
总结一下大概的思路:
1 创建人物
2 创建监听
3 用一个变量作为中转站来表示人物方向
4 监听事件根据点击屏幕的位置来改变中转站的值
5 人物根据中转站的值决定自身移动的方向(run函数)
6 接通电源,通过schedule来激活动作~
到这里基本的实现的完成了,但这只是开始,本帖主要讲的在后面,关于如何优化触摸体验。目前的游戏运行时,如果先按住左,然后左不放手,按住右,人物会往右走,但是,如果放手右手,左手不放手,人物不会再往左走,只会傻在那里!
明天有空的话来讲重点,如何优化算法,让快节奏的游戏触摸变得行云流水!看似简单的优化,其实确实不难 = = 但是有一些小技巧需要注意。
等全部讲完我会给出源码下载。
~~晚安