Cocos2d-x 基本操作+事件监听+调试技巧+常见问题

本节将分享

1. Cocos2dx开发中VS(通用)基本操作

2. Cocos2dx 事件监听 

3. 基本的Debug操作(非断点调试)

4. 常见问题
Cocos2d-x 版本:3.17.2

目录

一. 基本操作

1. 快速查看对应的函数声明(Ctrl+点击)

2. 快速查找/替换(Ctrl+F)

3. 查看所有引用(Fn+Shift+F12)

4. 格式化输出

二. 事件监听

1. 点击按钮触发

2. 鼠标点击事件

方法1 全局的鼠标事件

方法2 绑定在UI按钮上的点击事件

3. 键盘事件

三. 常见问题

1. Debug常规操作

编译时出错

运行时出错

控制变量+日志输出

2. 常见bug

新建项目后的缓存问题

空指针异常

场景切换报错

内容不显示,被遮挡

场景切换时黑屏,或场景切换多次

CocosStudio AnmationEdtior动画使用问题


一. 基本操作

Visual Studio通用操作

1. 快速查看对应的函数声明(Ctrl+点击)

如果有不清楚某种函数有多少种重载函数的时候,可尝试按住Ctrl+鼠标左键点击对应的函数名字,从而跳转到对应的函数声明。

1. 当项目过大的时候,可以快速跳转到对应的声明区域。

2. 若不清楚Cocos2dx里面的某种函数,大多也可以根据这个校验你的函数是否正确以及查看对应的重载函数可以传入什么参数。甚至还可以看到其他相似函数的声明。

比如长按CC_CALLBACK_0可以看到其他函数声明,这里的数字就代表了我们回调函数中不定参数的个数。

2. 快速查找/替换(Ctrl+F)

如果想找一段特定的字符串,通过 Ctrl+F可以调出查找框,可以设置成替换查找范围是当前文档还是当前项目。

3. 查看所有引用(Fn+Shift+F12)

功能同上,一种快速定位的方法。可以更加清晰地看到这个变量或者函数在其他地方的引用。这个默认快捷键比较别扭,个人还是喜欢鼠标选择某一个函数直接右键弹出这个选项。

4. 格式化输出

在Cocos2dx中创建了一个Label,如果我们需要动态设置其中的内容,比如显示分数。我们可以使用StringUtils::format进行格式化输出。代码如下:

//创建计分板
// 这里的mark是int类型,markLabel是Label*类型,都挂载到HelloWorld类对象中声明。
mark = 0;
markLabel = Label::createWithTTF(StringUtils::format("Mark : %d", mark), "fonts/arial.ttf",24);
markLabel->setTag(123);
markLabel->setString(StringUtils::format("Mark : %d", mark));
markLabel->setPosition(Vec2(100, 500));
markLabel->setColor(Color3B(255, 0, 0));
this->addChild(markLabel, 1);

二. 事件监听

可以自己摸索,比如创建一个button类对象的指针,通过输入->set,可以看到所有这个按钮可以设置的内容,包括监听器。

1. 点击按钮触发

通过addTouchEventListener可以直接传一个函数进去,这里通过&传函数的引用。(直接把函数声明给免了)

  • 方式一:匿名函数取引用
//重开按钮
auto buttonReplay = Button::create("button2.png", "button3.png");//正常与触发的状态
buttonReplay->setTitleText("Replay");//文本
buttonReplay->setTitleFontSize(50.0f);//字体大小
buttonReplay->setTitleColor(Color3B(0, 0, 255));//颜色
buttonReplay->setZoomScale(2.0);//缩放
buttonReplay->setPosition(Vec2(850, 400));//位置
buttonReplay->addTouchEventListener([&](Ref* sender, Widget::TouchEventType type) {//按下的回调函数
	switch (type)
	{
	case ui::Widget::TouchEventType::BEGAN:
		break;
	case ui::Widget::TouchEventType::ENDED:
		Director::getInstance()->replaceScene(TransitionFlipX::create(1.0f, HelloWorld::createScene()));
		break;
	default:
		break;
	}
	});
buttonReplay->setTag(234);//设置tag值,便于在其他函数 通过getChildByTag获取
this->addChild(buttonReplay, 5);
buttonReplay->setVisible(false);
  •  方式二:声明函数,依旧使用addTouchEventListener下方的语句。若从CocosStudio导出,记得勾上“交互”。
//注意CocosStudio需要引入的头文件
#include "ui/CocosGUI.h"
#include "cocostudio/CocoStudio.h"
USING_NS_CC;
using namespace ui;
using namespace cocostudio;
//...
button->addTouchEventListener(this, toucheventselector(MyFirstScene::onClick));

回调函数中的声明如下:

void MyFirstScene::onClick(Ref* sender, Widget::TouchEventType type) {
	switch (type)
	{
		case ui::Widget::TouchEventType::BEGAN:
			break;
		case ui::Widget::TouchEventType::ENDED:
			Director::getInstance()->replaceScene(TransitionFlipX::create(1.0f, HelloWorld::createScene()));
			break;
		default:
			break;
	}
}

2. 鼠标点击事件

方法1 全局的鼠标事件

如果没有使用上述的取引用,就需要先在对应的h文件里面声明这个回调函数。

 比如我这次的鼠标点击逻辑是点击后,判断是否位于按钮附近,若是,则切换场景。下面是在cpp中的实现代码:

void MyFirstScene::onMouseDown(Event* event)
{
    //获取事件
	EventMouse* e = (EventMouse*)event;
	//获取元素
    auto ui = this->getChildByTag(666);
	auto button = ui->getChildByTag(4);
	//获取鼠标事件触发的坐标->限定按钮范围
	if (e->getCursorX() >= button->getPositionX()-100 && e->getCursorX() <= button->getPositionX()+100
		&& e->getCursorY() >= button->getPositionY() - 60 && e->getCursorY() <= button->getPositionY() + 60)
	{
		Director::getInstance()->replaceScene(TransitionCrossFade::create(1.0f, HelloWorld::createScene()));
	}
}

最后,在场景的init函数中,我们还需要绑定该鼠标回调的监听器。

//鼠标监听器
auto _mouseListener = EventListenerMouse::create();

_mouseListener->onMouseDown = CC_CALLBACK_1(MyFirstScene::onMouseDown, this);

_eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener, this);

方法2 绑定在UI按钮上的点击事件

流程:我们首先需要获取到一个Button*对象,然后调用其自带的方法addClickEventListener。

获取到一个Button*对象并增加事件监听,注意普通的Node类型没有这个方法,所以获取节点元素的时候要确保它的类型正确。

// 获取scene里面的ui层,得到的变量ui是Node类型
auto ui = this->getChildByTag(666);

// 获取ui层里面的按钮,需要加上<>限定一下类型
Button* button = ui->getChildByTag<Button*>(4);

button->addClickEventListener(CC_CALLBACK_1(MyFirstScene::onMouseDown2, this));

回调函数的参数是Ref*类型,传入的是一个UI指针,除了计数好像没啥用。

void MyFirstScene::onMouseDown2(Ref* sender)
{
	Director::getInstance()->replaceScene(TransitionCrossFade::create(1.0f, HelloWorld::createScene()));
}

在lambda表达式中,可以使用捕获列表来传入自定义参数。例如,上面的示例代码中,使用[=]捕获列表,表示将所有外部变量都按值传入lambda表达式中。

 使用lambda简化的版本如下(无参数):

button->addClickEventListener([=](Ref* sender) {
	Director::getInstance()->replaceScene(TransitionCrossFade::create(1.0f, HelloWorld::createScene()));
});

如果需要自定义参数,就在外面套多一层函数:

void MyScene::onButtonClicked(Ref* sender, int param1, float param2) {
    // 点击按钮后执行的操作,可以使用param1和param2参数
}

// 在 init() 方法中绑定回调函数到按钮上,并传入自定义参数
auto button = Button::create("button.png");
int myParam1 = 123;
float myParam2 = 3.14f;
button->addClickEventListener([=](Ref* sender){
    onButtonClicked(sender, myParam1, myParam2);
});

3. 键盘事件

首先依旧在h文件中声明函数

void onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event);

cpp文件中实现:

void IceHokeyScene::onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event)
{

	//判断 keyCode 枚举来区分所按下的按键
	switch (keyCode) {
		//角色1的四个方向移动
		case EventKeyboard::KeyCode::KEY_W: {
			player->playVerticalDirection(NORTH);
			break;
		}
		case EventKeyboard::KeyCode::KEY_S: {
			player->playVerticalDirection(SOUTH);
			break;
		}
        //...
    }
}

如果不知道对应的键盘有哪些,可以通过Ctrl+鼠标左键点击KeyCode,就可以跳转到声明的地方。

最后在场景的init函数中绑定回调函数即可。

//创建一个监听器
EventListenerKeyboard * listenerKeyboard = cocos2d::EventListenerKeyboard::create();
//绑定事件,注册回调函数
listenerKeyboard->onKeyPressed = CC_CALLBACK_2(IceHokeyScene::onKeyPressed,this);
//注册这个监听器
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listenerKeyboard, this);

与onKeyPressed对应的,还有一个onKeyReleased,对应按下松开。两者配合可以实现键盘按住的效果。

三. 常见问题

1. Debug常规操作

编译时出错

一般这种情况看下方的错误列表中出现的错误。

 这里的经典bug,无法解析外部符号,原因很多。多半需要检查一下头文件的引入有无重复引入,删除缓存重新跑。

运行时出错

此处的错误可以点击上方的堆栈帧来定位最近的出错代码。先找MyGame下的代码,如果没有,还还可以根据Cocos2dx函数名来推测错误类型。这里是事件监听的错误。 

原因多半是回调函数的参数错误。需要去检查自己的回调函数参数。

控制变量+日志输出

如果在原本能够正常运行,但是后续修改导致无法运行,可以尝试注释掉部分修改。通过CCLOG打印输出。

这个方法也可以用来在程序运行的时候动态查看游戏数据。

CCLOG("%f %f", player->getPositionX(), player->getPositionY());
if(player 位于 球前面){
    //踢球
    //为什么进不去这个分支?
    //LOG 一下player和球的位置
}

输出窗口一般位于左下角。 

2. 常见bug

新建项目后的缓存问题

若在移动资源前跑了一次Helloworld,在删除helloworld并右键项目名移入现有项后,仍旧出现原本Helloworld的初始程序,则需要把项目文件夹里面的Debug或者Release文件夹删除,重新编译。

空指针异常

一般没有正确获取到某一个元素,会在程序刚跑起来的时候报错。

如果是从CocosStudio导出的元素空指针异常,除了上述看堆栈帧操作外,还需要检查自己的CocosStudio的层级关系,如果在mainUI下面的两层,是无法用getChildByTag一次性拿到的。需要一层一层往下拿。

auto mainUI = GUIReader::getInstance()->widgetFromJsonFile("xxx/yyy.ExportJson");
auto image1 = (ImageView*)mainUI->getChildByTag(10);
auto button1 = (Button*)image1->getChildByTag(40);

场景切换报错

如果在场景切换的时候运行时出错,通过堆栈帧定位到如下语句。

细节:Cocos2dx的导演是单例模式,要用Director::getInstance()。

内容不显示,被遮挡

如果添加了一个按钮,而该按钮无法在页面中展示出来,需要注意addChild这个函数有第二个参数可以设置。类似图层的概念,数值越大,优先级越高。

this->addChild(button,10);

场景切换时黑屏,或场景切换多次

做实验三(人物与牛头人battle)易出现该问题:角色生命值为0后,仅切换场景,但是却未初始化角色的生命值,导致isDead变量始终为真,重复切换场景。

CocosStudio AnmationEdtior动画使用问题

1. 若播放一遍即卡住,则需要考虑在结束的帧上增加帧事件(不需要全部layer的帧都加)。

2. 若播放一个动画不改变其状态想维持结束帧,但是重复播放(比如死亡)。需要去AnimationEditor中取消勾选“是否循环”的选项。

3. 若做出上述调整,仍出现和之前相同的问题,参考前置缓存问题的处理方式。清理对应的Debug或者Realse文件夹,甚至可以修改文件引入的路径。(有时候就是这么神奇)

内容好多,以后遇到新的bug再补在这里。

  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

如果皮卡会coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值