cocos2d-x 4.0 安装教程(Windows + VS) + cocosui入门知识储备
(一)前期准备Windows 10操作系统VS 2019 或 VS 2017Python 2.7.18:记得勾选将python添加到环境变量的复选框,不然自己手动添加也行。CMake 3.19.0:记得勾选将cmake添加到环境变量的复选框,不然自己手动添加也行。cocos2d-x 4.0:手动将cocos2d-x-4.0\tools\cocos2d-console\bin目录添加到环境变量。以上
(一)前期准备
- Windows 10操作系统
- VS 2019 或 VS 2017
- Python 2.7.18:记得勾选将python添加到环境变量的复选框,不然自己手动添加也行。
- CMake 3.19.0:记得勾选将cmake添加到环境变量的复选框,不然自己手动添加也行。
- cocos2d-x 4.0:手动将
cocos2d-x-4.0\tools\cocos2d-console\bin
目录添加到环境变量。
以上全都下载最新版本应该也没问题,至此就可以使用VS来编译cocos2d-x工程了。
(二)VS编译cocos2d-x工程
- 新建项目
打开命令行执行cocos new PROJECT-NAME -p com.coco2dx.org -l cpp -d PROJECT-PATH
。其中PROJECT-NAME
是实际的项目名,PROJECT-PATH
是具体的项目路径。以Test
作为项目名为例,命令执行之后可以生成以下目录:
- 在该目录下新建
build
文件夹并打开。(其实文件夹命名随意,这里只是以build
为例) - 在
build
目录下打开命令行执行cmake .. -G"Visual Studio 16 2019" -Tv142 -A win32 ..
(vs2017则是cmake .. -G"Visual Studio 15 2017" -Tv141 -A win32 ..
)这里以Test
作为项目名。 - 执行完毕后就会在
build
目录下生成VS工程,双击sln文件打开工程。
- 直接运行就可以生成可执行文件,在
build\bin\Test\Debug
目录下。双击运行Test.exe
就可以看到hello world啦。
运行时可能会遇到一个无法启动ALL_BUILD的问题,这里将Test
(你的项目)设定为启动项目即可。
还有的情况下使用了log
会运行失败,报错信息如下所示,错误指向文件CCConsloe.h
的void CC_DLL log(const char * format, ...) CC_FORMAT_PRINTF(1, 2);
。这个错误实在是太刁钻了,查了很多论坛才发现,只要把错误列表的“生成 + IntelliSense”改成“仅生成”即可。
(三)CocosStudio
cocos引擎的UI编辑器,由于已经停止维护了(下载官网也找不到了),所以这里可以提供个人的下载链接,提取码为etki
。
CocosStudio发布资源有两种方式:
-
发布成csb文件,将xml格式的csd文件转换成二进制文件,便于解析。
-
发布为VS工程,但是在VS2019上运行会存在诸如
"function": 不是 "std" 的成员
之类的一堆错误。幸好在github上找到解决方法,只需要在cocos2d\cocos\audio\win32\AudioCache.h
文件中加上#include <functional>
即可。实际上在VS工程中也是通过加载csb文件来进行渲染的。
bool HelloWorld::init() { if ( !Layer::init() ) { return false; } auto rootNode = CSLoader::createNode("MainScene.csb"); addChild(rootNode); return true; }
运行结果为:
(四)新手文档
Hello World
从HelloWorld的demo也可以看出,资源文件存放在Resources
目录下。
运行过程:
main.cpp
中的入口函数做了两件事情:初始化Application
对象和运行其单例的run
方法。// main.cpp int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // create the application instance AppDelegate app; // 继承自Application return Application::getInstance()->run(); }
AppDelegate
的构造方法看似什么事都没做,其实是隐式调用了其父类Application
的构造方法。// AppDelegate.cpp AppDelegate::AppDelegate() { } // CCApplication-win32.cpp Application::Application() : _instance(nullptr) , _accelTable(nullptr) { _instance = GetModuleHandle(nullptr); _animationInterval.QuadPart = 0; CC_ASSERT(! sm_pSharedApplication); sm_pSharedApplication = this; // 将静态的sm_pSharedApplication指针指向当前对象 }
Application
的静态方法getInstance
直接返回单例,即静态的sm_pSharedApplication
指针。// CCApplication-win32.cpp Application* Application::getInstance() { CC_ASSERT(sm_pSharedApplication); return sm_pSharedApplication; }
Application
的实例方法run
中调用了applicationDidFinishLaunching
方法,该方法在其子类AppDelegate
中已经被重写,在其中调用了HelloWorld
场景的静态createScene
方法,以及设置场景窗口、屏幕适配方案、FPS等一系列方法。// CCApplication-win32.cpp int Application::run() { ... if (!applicationDidFinishLaunching()) { return 1; } ... } // AppDelegate.cpp bool AppDelegate::applicationDidFinishLaunching() { ... // create a scene. it's an autorelease object auto scene = HelloWorld::createScene(); // run director->runWithScene(scene); return true; }
HelloWorld::createScene
实际上是调用其父类Scene
的静态create
方法,在其中新创建一个Scene
对象,调用初始化方法init
并返回。// HelloWorldScene.cpp Scene* HelloWorld::createScene() { return HelloWorld::create(); } // CCScene.cpp Scene* Scene::create() { Scene *ret = new (std::nothrow) Scene(); if (ret && ret->init()) { ret->autorelease(); return ret; } else { CC_SAFE_DELETE(ret); return nullptr; } }
- 所以我们可以在
HelloWorld
场景的init
方法里面做自定义的初始化工作了。e.g.// HelloWorldScene.cpp bool HelloWorld::init(){ if ( !Scene::init() ) { return false; } ... }
常用API
推荐Cocos2d-x 用户手册,里面有对一些基本概念和进阶内容的介绍。
-
打印
log("");
log
函数的参数类型为const char*
,对字符串string
实例要先调用c_str
方法转换成const char*
类型。不过更建议使用格式化日志。e.g.auto listener = EventListenerKeyboard::create(); listener->onKeyPressed = [](EventKeyboard::KeyCode keyCode, Event* event) { log("Key with keycode %d pressed", keyCode); return true; };
-
回调
回调函数有两种写法:
CALL_BACK_N(__selector__, __target__)
:N可选择1, 2, 3, 4,表示__selector__
方法的参数个数。__selector__
参数是指类的成员函数,即类名::方法名
的形式,__target__
是该类的对象,可以是指针或对象,一般使用this
。- Lambda表达式:
[捕获值列表](参数列表){函数体}
。捕获值的意义可以参看C++11中的Lambda表达式构成之——捕获值列表
e.g.
void HelloWorld::init(){ // 关闭按键的两种回调方式: auto closeItem = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", CC_CALLBACK_1(HelloWorld::menuCloseCallback, this)); auto closeItem2 = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", [&](Ref* pSender) { Director::getInstance()->end(); }); } void HelloWorld::menuCloseCallback(Ref* pSender) { Director::getInstance()->end(); }
-
Ref
-
Node
-
渲染
-
cocosui
除了
Label
和Menu
,其余UI组件都需要使用到头文件"ui/CocosGUI.h"
以及命名空间ui
.Label
:标签auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24); // 参数:文字,字体,字号
Menu
:菜单auto menuItem = MenuItemImage::create("normal.png", "selected.png", 回调函数); // 用法一:直接使用MenuItem创建菜单 auto menu = Menu::create(closeItem, NULL); // 用法二:使用vector组织多个MenuItem来创建菜单 Vector<MenuItem*> menuItems; menuItems.pushBack(closeItem); auto menu = Menu::createWithArray(menuItems);
Button
:按键auto button = ui::Button::create("ButtonNormal.png", "ButtonSelected.png", "ButtonDisable.png"); // 参数:未选中图片,选中图片,不可用图片 button->addTouchEventListener([&](Ref* sender, ui::Widget::TouchEventType type) { switch (type) { } });
TouchEventType
枚举常量一共有四种类型:BEGAN, MOVED, ENDED, CANCELED
。enum class TouchEventType { BEGAN, MOVED, ENDED, CANCELED };
CheckBox
:复选框auto checkbox = ui::CheckBox::create("normal.png", "normal_press.png", "active.png", "normal_disable.png", "active_disable.png"); checkbox->addTouchEventListener([&](Ref* sender, ui::Widget::TouchEventType type){ switch (type) { } });
LoadingBar
:进度条auto loadingBar = ui::LoadingBar::create("LoadingBarFile.png"); loadingBar->setDirection(LoadingBar::Direction::RIGHT); // 参数:方向 loadingBar->setPercent(25); // 参数:进度
Slider
:滑动条auto slider = ui::Slider::create(); slider->loadBarTexture("SliderBar.png"); // 参数:滑动框背景图 slider->loadSlidBallTextures("SliderNodeNormal.png", "SliderNodePress.png", "SliderNodeDisable.png"); // 参数:未选中图片,选中图片,不可用图片 slider->loadProgressBarTexture("SliderProgressBar.png"); // 参数:进度条图片 slider->addTouchEventListener([&](Ref* sender, ui::Widget::TouchEventType type){ switch (type) { } });
TextFiled
:文本框auto textField = ui::TextField::create("","Arial",30); textField->addTouchEventListener([&](Ref* sender, Widget::TouchEventType type){ });
-
事件分发机制
- 事件监听器:负责接收事件,并执行预定义的事件处理函数。事件监听器可以直接对各种回调函数进行直接赋值,只有一个回调的监听器往往可以其
create
方法会以回调作为参数(e.g. 加速度事件监听器等)。EventListenerTouch
:响应触摸事件。触摸事件的回调包括(只有onTouchBegan
方法是具有bool
返回值的,可以设置触摸吞噬):
关于单点触摸(std::function<bool(Touch *touch, Event *event)> onTouchBegan; std::function<void(Touch *touch, Event *event)> onTouchMoved; std::function<void(Touch *touch, Event *event)> onTouchEnded; std::function<void(Touch *touch, Event *event)> onTouchCancelled;
EventListenerTouchOneByOne
)和多点触摸(EventListenerTouchAllAtOnce
)的区别可以查看:Cocos2d-X之触摸事件EventListenerMouse
:响应鼠标事件。鼠标事件的回调包括:std::function<void(EventMouse* event)> onMouseDown; std::function<void(EventMouse* event)> onMouseUp; std::function<void(EventMouse* event)> onMouseMove; std::function<void(EventMouse* event)> onMouseScroll;
EventListenerKeyboard
:响应键盘事件。键盘事件的回调包括:std::function<void(EventKeyboard::KeyCode, Event*)> onKeyPressed; std::function<void(EventKeyboard::KeyCode, Event*)> onKeyReleased;
EventListener
基类的回调其实是只有一个参数的,而EventListenerKeyboard
其实是对基类的回调进行了扩充(其实就是对event
进行强转并获取其_keyCode
属性),增加了额外的参数。其他监听器扩充参数的思路也可由此类推。bool EventListenerKeyboard::init() { auto listener = [this](Event* event){ auto keyboardEvent = static_cast<EventKeyboard*>(event); if (keyboardEvent->_isPressed) { if (onKeyPressed != nullptr) onKeyPressed(keyboardEvent->_keyCode, event); } else { if (onKeyReleased != nullptr) onKeyReleased(keyboardEvent->_keyCode, event); } }; if (EventListener::init(Type::KEYBOARD, LISTENER_ID, listener)) { return true; } return false; }
EventListenerAcceleration
:响应加速度事件。加速度事件监听器只有一个回调。static EventListenerAcceleration* create(const std::function<void(Acceleration*, Event*)>& callback); std::function<void(Acceleration*, Event*)> onAccelerationEvent;
EventListenerCustom
:响应自定义事件。自定义事件监听器也只有一个回调。static EventListenerCustom* create(const std::string& eventName, const std::function<void(EventCustom*)>& callback); std::function<void(EventCustom*)> _onCustomEvent;
- 每个监听器都有对应的回调函数,可以直接使用前面所说的
CC_CALLBACK
或者Lambda表达式来赋值。 - 事件分派是具有一定优先级的,优先级越高,就越先接收到事件。事件分派优先级可以分为FixedPriority和SceneGraphPriority,两者只能使用一种作为标准。
FixedPriority
:事件监听器自身设定的优先级,监听器通过addEventListenerWithFixedPriority
方法注册。void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority) { CCASSERT(listener, "Invalid parameters."); CCASSERT(!listener->isRegistered(), "The listener has been registered."); CCASSERT(fixedPriority != 0, "0 priority is forbidden for fixed priority since it's used for scene graph based priority."); if (!listener->checkAvailable()) return; listener->setAssociatedNode(nullptr); listener->setFixedPriority(fixedPriority); listener->setRegistered(true); listener->setPaused(false); addEventListener(listener); }
- SceneGraphPriority:使用节点的层级作为事件分派的优先级,监听器通过
addEventListenerWithSceneGraphPriority
方法注册。void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node) { CCASSERT(listener && node, "Invalid parameters."); CCASSERT(!listener->isRegistered(), "The listener has been registered."); if (!listener->checkAvailable()) return; listener->setAssociatedNode(node); listener->setFixedPriority(0); listener->setRegistered(true); addEventListener(listener); }
- 如果要在当前节点消费(吞没)事件,不再往更低优先级的节点传递,可以在回调的最后直接
return true
。这一点常用于Touch
事件。注意:ui::Button
的触摸事件以及监听器其实是自定义实现的(EventFocus, EventFocusListener
),默认是在捕获时直接消费,不会再继续分派。 - 事件监听器只能注册一次(从
CCASSERT(!listener->isRegistered(), "The listener has been registered.");
可以看出)。如果需要多次注册同一个监听器对象,只能使用其clone
副本。e.g._eventDispatcher->addEventListenerWithSceneGraphPriority(listener, sprite1); _eventDispatcher->addEventListenerWithSceneGraphPriority(listener->clone(), sprite2);
- 事件分发器:负责注册监听器以及发起通知。前面四种事件一般由系统分派,自定义事件可以手动分派。以下为增删监听器和分派事件的常用方法:
事件分发最终是会按顺序调用到监听器的回调函数class CC_DLL EventDispatcher : public Ref { public: void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node); void addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority); void removeEventListener(EventListener* listener); void removeEventListenersForType(EventListener::Type listenerType); void removeEventListenersForTarget(Node* target, bool recursive = false); void removeCustomEventListeners(const std::string& customEventName); void removeAllEventListeners(); void dispatchEvent(Event* event); void dispatchCustomEvent(const std::string &eventName, void *optionalUserData = nullptr); protected: void dispatchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent); void dispatchTouchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent); }
std::function<void(Event*)> _onEvent
。 - 事件对象:记录事件的相关信息。事件基类为
Event
,事件和事件监听器是一一对应的关系。EventTouch
:触摸事件。EventMouse
:鼠标事件。EventKeyboard
:键盘事件。EventAcceleration
:加速度事件。EventCustom
:自定义事件。自定义事件使用一个void指针指向需要传递的数据,在自定义事件初始化之后可以调用setUserData
方法设置数据,而在EventListenerCustom
监听器的回调中可以调用getUserData
方法获取数据。自定义事件提供一个事件名_eventName
便于事件分派器定向分派事件和删除监听器。class CC_DLL EventCustom : public Event { public: EventCustom(const std::string& eventName); void setUserData(void* data) { _userData = data; } void* getUserData() const { return _userData; } const std::string& getEventName() const { return _eventName; } protected: void* _userData; ///< User data std::string _eventName; }
- 事件监听器:负责接收事件,并执行预定义的事件处理函数。事件监听器可以直接对各种回调函数进行直接赋值,只有一个回调的监听器往往可以其
-
动作
【参考资料】
[1] Cocos2d-x Windows 环境搭建 - zhxmdefj - 博客园
[2] cocos2dx4.0 - 简书
[3] Cocos2dx-v4.0学习-使用CMake编译Cocos2d-4.0 (For Visual Studio)_hunter_wyh-CSDN博客
[4] 无法启动 build/x64/debug/ALL_BUILD_weixin_42279610的博客-CSDN博客
[5] Cocos2d-x 用户手册
[6] 理解CC_CALLBACK_0, CC_CALLBACK_1, CC_CALLBACK_2, CC_CALLBACK_3
[7] C++11中的Lambda表达式构成之——捕获值列表
[8] “ccconsole.h, ccutf8.h error, expected a {” - cocos2d-x / C++ - Cocos Forums
更多推荐
所有评论(0)