cocos2d-x-4.0\tools\cocos2d-console\bin
目录添加到环境变量。以上全都下载最新版本应该也没问题,至此就可以使用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啦。Test
(你的项目)设定为启动项目即可。log
会运行失败,报错信息如下所示,错误指向文件CCConsloe.h
的void CC_DLL log(const char * format, ...) CC_FORMAT_PRINTF(1, 2);
。这个错误实在是太刁钻了,查了很多论坛才发现,只要把错误列表的“生成 + IntelliSense”改成“仅生成”即可。cocos引擎的UI编辑器,由于已经停止维护了(下载官网也找不到了),所以这里可以提供个人的下载链接,提取码为etki
。
CocosStudio发布资源有两种方式:
发布成csb文件,将xml格式的csd文件转换成二进制文件,便于解析。
发布为VS工程,但是在VS2019上运行会存在诸如"function": 不是 "std" 的成员
之类的一堆错误。幸好在github上找到解决方法,只需要在cocos2d\cocos\audio\win32\AudioCache.h
文件中加上#include
即可。
实际上在VS工程中也是通过加载csb文件来进行渲染的。
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
auto rootNode = CSLoader::createNode("MainScene.csb");
addChild(rootNode);
return true;
}
从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;
}
...
}
推荐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
。[捕获值列表](参数列表){函数体}
。捕获值的意义可以参看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
cocos2d-x - Ref 引用计数
Node
cocos2d-x - Node 节点
cocos2d-x - Render 渲染
除了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
:事件监听器自身设定的优先级,监听器通过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);
}
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 _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;
}
cocos2d-x - Action动作
【参考资料】
[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