第三讲 Touch

前面两篇我们学习的内容,足够我们做一款简单的小游戏。也能够说,我们已经入门了,能够蹒跚的走路了。

本篇将解说cocos2dx中非常重要的touch回调机制。你肯定记得第一章做定时器时间的时候用过CC_CALLBACK_1宏定义,它让我们回调一个仅仅有一个形參的函数来运行定时操作。

回调函数的实现(Lambda表达式)

学习本篇前请细致学习一下C++11的特性,std::function和lambda表达式。C++11还引入了非常多boost库的优秀代码,使我们在使用的时候不必再加boost::,比方将要使用的std::bind;

学习地址例如以下:

C++11function使用

lua闭包,iosblock,C++lambda函数

简单的说一下function的使用。统一的函数定义格式:function<int(int,float)>。就相当于一种数据类型。仅仅只是int是定义一个整形数,而function是定义一个函数。

function<int(int,float)>func = [](int a ,float b){return a+b;};//定义一个函数,名为func,它的第一个形參是int,第二个形參是float,返回值是int类型。
int ret = func(3, 1.2f);

首先让我们来看一下宏CC_CALLBACK_的定义

// new callbacksbased on C++11
#defineCC_CALLBACK_0(__selector__,__target__, ...)std::bind(&__selector__,__target__, ##__VA_ARGS__)
#defineCC_CALLBACK_1(__selector__,__target__, ...)std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
#defineCC_CALLBACK_2(__selector__,__target__, ...)std::bind(&__selector__,__target__, std::placeholders::_1,std::placeholders::_2, ##__VA_ARGS__)
#defineCC_CALLBACK_3(__selector__,__target__, ...)std::bind(&__selector__,__target__, std::placeholders::_1,std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)

事实上它们仅仅是封装了bind的使用方法。能够看到有几个參数,bind后面就跟几个_*。

要使用bind和function须要引入头文件#include
<functional>,bind的參数类型须要引入命名空间using namespace std::placeholders;

#include"stdafx.h"
#include<iostream>
#include<string>
#include<functional>
using namespace std;
using namespacestd::placeholders; struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout<< num_ + i << '\n'; }
int num_;
};
int _tmain(int argc,_TCHAR* argv[])
{
const Foo foo(123);
function<void(int)> f_add_display =bind(&Foo::print_add, foo, _1);
return0;
}

注意:bind要绑定一个类函数的时候,第二个參数必须是类对象。

所以我们在菜单子项绑定回调函数的时候,能够不使用CC_CALLBACK_1:

        auto item =MenuItemLabel::create(Label::createWithBMFont("fonts/futura-48.fnt",itemName->getCString()));
item->setCallback(CC_CALLBACK_1(KT0618::change, this));

等价于

        auto item =MenuItemLabel::create(Label::createWithBMFont("fonts/futura-48.fnt",itemName->getCString()));
item->setCallback(std::bind(&KT0618::change, this,std::placeholders::_1));

也等价于

        auto item =MenuItemLabel::create(Label::createWithBMFont("fonts/futura-48.fnt",itemName->getCString()));
item->setCallback([=](Ref *ref)->void{//lambd表达式
……
});

怎样正确定义和声明回调函数:

当我们create一个精灵的时候,往往记不住这样的类型的精灵的回调函数有几个參数,參数类型是什么。这里非常重要的方法就是阅读api。我们追踪进去看create方法源代码,在create方法中查看须要的回调函数返回值是什么类型,形參是什么类型,复制过来就可以。

在练习过程中,我们要尽量不使用CC_CALLBACK_*,而是自己书写bind函数或者lambda表达式。

***************************************************************************************************************

反向传值

一个简单的应用场景:游戏主界面展示最高分,切换到游戏场景完毕游戏后,要向主场景返回成绩推断是否刷新纪录,假设是的话就更新主界面的最高分。

前面我们学习了正向传值,使用子场景的成员函数能够向子场景传值。所谓反向传值能够理解为子场景传值回主场景。

依据我们上面学习的function,我们应该把主场景的函数指针利用子场景的成员函数传递给子场景存储起来,然后在子场景中能够调用它的成员变量来调用主场景的函数。
这样我们切换场景的时候仅仅能pushScene,子场景使用popScene。否则主场景的对象都不存在了还怎样实现回调呢?!

新手可能会想,再在子场景中实例化一个主场景的类对象这么就能够传递值了,然后使用replace切换场景,而不须要这么麻烦的传递一个函数指针。假设按这样的做法,主场景和子场景要相互引用头文件实例化对方,违反了低耦合的原则。

在子场景头文件里这么定义:

Public:
std::function<void(int)> func;

我们就能够在主场景切换到子场景的时候这样来注冊回调函数:

auto scene =HomeWorkSnowFight::createScene();
HomeWorkSnowFight*layer = (HomeWorkSnowFight*)scene->getChildren().at(0);
layer->func = std::bind(&HomeWorkSnow::callback1,this,std::placeholders::_1 );//绑定回调函数到子场景
Director::getInstance()->pushScene(TransitionCrossFade::create(1,scene));

这样我们在子场景中调用func(99);就相当于调用的主场景的callback1(99)了。

三 touch事件

陀螺仪

    Device::setAccelerometerEnabled(true);
// auto ac =EventListenerAcceleration::create(CC_CALLBACK_2(KT0618::accelerationc, this));
auto ac =EventListenerAcceleration::create([&](Acceleration* acc, Event* e){
sp->setPositionX(acc->x+sp->getPositionX());
});
_eventDispatcher->addEventListenerWithSceneGraphPriority(ac, this);

书写函数的时候细致查看api,看create有几个參数。

比方陀螺仪create函数的形參格式例如以下:

const std::function<void(Acceleration*, Event*)>& callback

这就说明须要传入的參数应该是有两个參数的void类型的函数对象。

陀螺仪的代码仅仅能在真机上測试了。

键盘事件

在xcode下是无法模拟的,仅仅有在VS下才干測试。以下的这些代码都是要牢记于心的,动手实现一下就明确了!

    auto keyboardLs =EventListenerKeyboard::create();
keyboardLs->onKeyPressed =[=](EventKeyboard::KeyCode code, Event*event){
if(code==EventKeyboard::KeyCode::KEY_A)
{
CCLOG("AAA");
}
};
keyboardLs->onKeyReleased =[](EventKeyboard::KeyCode code, Event*event){
CCLOG("BBB");
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(keyboardLs,this);

鼠标事件  单点触控

    auto listen =EventListenerTouchOneByOne::create();
listen->onTouchBegan =CC_CALLBACK_2(KT0618::onTouchBegan, this);
listen->onTouchMoved =CC_CALLBACK_2(KT0618::onTouchMoved, this);
listen->onTouchEnded =CC_CALLBACK_2(KT0618::onTouchEnded, this); listen->setSwallowTouches(true);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listen,this);

动手实现一下拖动一个物体到还有一个物体上,仅仅须要拖动到目的的边缘松开鼠标,物体会自己主动放到中心处。

b

oolKT0618::onTouchBegan(Touch *t, Event*e)
{
Point p = t->getLocation();
Rect rect = spMove->getBoundingBox();
if (rect.containsPoint(p))
{
return true;
}
else
{
return false;
}
return true;
}
voidKT0618::onTouchMoved(Touch *t, Event*e)
{
Point p = t->getLocation();
Rect rect = spMove->getBoundingBox();
if (rect.containsPoint(p))
{
spMove->setPosition(p);
}
} voidKT0618::onTouchEnded(Touch *t, Event*e)
{
Point p = t->getLocation();
Rect rect = spBase->getBoundingBox(); if (rect.containsPoint(p))
{
spMove->setPosition(spBase->getPosition());
}
}

多点触控

ios下AppController.mm加一句[eaglView setMultipleTouchEnabled:YES];在模拟器按住alt能够调试多点。

windows就不用想了,surface除外。

    auto touchMore =EventListenerTouchAllAtOnce::create();
touchMore->onTouchesBegan =CC_CALLBACK_2(KT0618::onTouchesBegan, this);
touchMore->onTouchesMoved =CC_CALLBACK_2(KT0618::onTouchesMoved, this);
touchMore->onTouchesEnded =CC_CALLBACK_2(KT0618::onTouchesEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchMore,this);

onTouchesBegan跟单点触控返回值不同,请详细的依据api来写。

void  KT0618::onTouchesBegan(conststd::vector<Touch*>& touches, Event* events)
{
for (auto v : touches )
{
v->getLocation();
}
}
voidKT0618::onTouchesMoved(const std::vector<Touch*>& touches, Event*unused_event)
{
}
void KT0618::onTouchesEnded(conststd::vector<Touch*>& touches, Event *unused_event)
{
}

加入自己定义消息响应EventListenerCustom

init()里面加入例如以下代码,那么这个层就会响应标志位shutdown的消息。

   auto listenCustom =EventListenerCustom::create("shutdown",CC_CALLBACK_1(KT0618::popupLayerCustom, this));
Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(listenCustom,1);

这样能够在须要发送shutdown消息的地方这样加入,第二个參数是这条消息的自己定义參数。

_eventDispatcher->dispatchCustomEvent("shutdown", (void*)"Go!DongGuan!");

这样就往外分发了一个名字是shutdown的消息。

这个消息能够在不同的层中接收到,利用第二个參数能够做到数据传递。能够是不论什么类型的数据,比回调方便。

其它层init也照上面加入,并加入相应的响应函数。

voidPopupLayer::shutdown(EventCustom * event)
{
char * str =(char *)event->getUserData();
CCLOG("Do not toucheme,bullshit!");
CCLOG(str);
}

加入音效

#include<SimpleAudioEngine.h>

usingnamespace CocosDenshion;

CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic(MUSIC_BG,true);

CocosDenshion:

:SimpleAudioEngine::sharedEngine()->playEffect(MUSIC_ENEMY1);

音效init的时候预载入,可是要注意切换场景的时候要释放掉预载入的音效。

CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic(MUSIC_BG);
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect(MUSIC_BULLET);

cocos2dx 3.1从零学习(三)——Touch事件(回调,反向传值)的更多相关文章

  1. cocos2dx 3.1从零学习(一)——入门篇(一天学会打飞机)

    没办法,浏览这么高,为啥没人投票呢?朋友们,我这篇文章參加了csdn博文大赛.喜欢的来点个赞吧!点击:http://vote.blog.csdn.net/Article/Details?article ...

  2. cocos2dx 3.1从零学习(四)——内存管理(错误案例分析)

    本篇内容文字比較较多,可是这些都是建立在前面三章写代码特别是传值的时候崩溃的基础上的.可能表达的跟正确的机制有出入,还请指正. 假设有不理解的能够联系我.大家能够讨论一下,共同学习. 首先明白一个事实 ...

  3. cocos2dx 3.1从零学习(五)——动画

    动画是游戏中最重要的表现部分,本篇仅仅是初步学习大部分动画的用法.没有什么原理性的东西,可是样例有非常多,假设有不熟的地方要练一下. 特别是Spawn和Sequence的组合使用,什么时候使用Spaw ...

  4. cocos2dx 3.1从零学习(二)——菜单、场景切换、场景传值

    回想一下上一篇的内容,我们已经学会了创建一个新的场景scene,加入sprite和label到层中.掌握了定时事件schedule. 我们能够顺利的写出打飞机的主场景框架. 上一篇的内容我练习了七个新 ...

  5. cocos2dx 3.1从零学习(六)——CocosStudio(VS2013project导入及环境设置)

    导入libCocosStudio.libExtensions.libGUI 新建的project例如以下图: 加入现有项目 右键解决方式.例如以下操作: watermark/2/text/aHR0cD ...

  6. IOS 学习笔记 2015-04-15 控制器数据反向传值

    // // FirstViewController.h // 控制器数据传递 // // Created by wangtouwang on 15/4/15. // Copyright (c) 201 ...

  7. es6从零学习(三):Class的基本用法

    es6从零学习(三):Class的基本用法 一:定义一个类 //定义类 class Point { constructor(x, y) { this.x = x; this.y = y; } toSt ...

  8. 2、JavaScript 基础二 (从零学习JavaScript)

     11.强制转换 强制转换主要指使用Number.String和Boolean三个构造函数,手动将各种类型的值,转换成数字.字符串或者布尔值. 1>Number强制转换 参数为原始类型值的转换规 ...

  9. 1、JavaScript 基础一 (从零学习JavaScript)

    1:定义:javascript是一种弱类型.动态类型.解释型的脚本语言. 弱类型:类型检查不严格,偏向于容忍隐式类型转换. 强类型:类型检查严格,偏向于不容忍隐式类型转换. 动态类型:运行的时候执行类 ...

随机推荐

  1. c++面试知识点

    static #include<stdio.h> #include<iostream> #include<assert.h> using namespace std ...

  2. Bom和Dom编程以及js中prototype的详解

    一.Bom编程: 1.事件练习: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "h ...

  3. Android应用程序绑定服务(bindService)的过程源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6745181 Android应用程序组件Serv ...

  4. Flashback Drop实例操作

    1.Flashback DropFlashback Drop 是从Oracle 10g 开始出现的,用于恢复用户误删除的对象(包括表,索引等), 这个技术依赖于Tablespace Recycle B ...

  5. [J2EE框架][Debug]

    注意xml头部问题 比如在xx-servlet中注意: <mvc:annotation-driven/> <context:component-scan base-package=& ...

  6. 在IIS Express中调试时无法读取配置文件

    在IIS Express中调试代码时,如果出现“无法读取配置文件”的问题(如图),这种情况是IIS Express的“applicationhost.config”配置文件中的映射关系出了问题[ps: ...

  7. asp.net mvc输出自定义404等错误页面,非302跳转

      朋友问到一个问题,如何输出自定义错误页面,不使用302跳转.当前页面地址不能改变. 还要执行一些代码等,生成一些错误信息,方便用户提交反馈. 500错误,mvc框架已经有现成解决方法: filte ...

  8. self和this的不同

    在Java和C++中,this总是指的是当前实例地址,而在静态方法也就是类方法中,是不可以使用this的.在Objectvie-C中,self是既可以出现在实例方法中,也可以出现在类方法中,并且在不同 ...

  9. 设计模式 之 Organizing the Catalog 组织目录

    Design patterns vary in their granularity and level of abstraction. Because thereare many design pat ...

  10. css3属性:column分栏

    css3选择器中提出了分栏的属性,其浏览器支持情况为:Internet Explorer 10 和 Opera 支持 column 属性,Firefox 支持替代的 -moz-column 属性,Sa ...