cocos2d-x 4.0 安装教程(Windows + VS) + cocosui入门知识储备

(一)前期准备

  • 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工程

  1. 新建项目
    打开命令行执行cocos new PROJECT-NAME -p com.coco2dx.org -l cpp -d PROJECT-PATH。其中PROJECT-NAME是实际的项目名,PROJECT-PATH是具体的项目路径。以Test作为项目名为例,命令执行之后可以生成以下目录:
    cocos2d-x 4.0 安装教程(Windows + VS) + cocosui入门知识储备_第1张图片
  2. 在该目录下新建build文件夹并打开。(其实文件夹命名随意,这里只是以build为例)
  3. build目录下打开命令行执行cmake .. -G"Visual Studio 16 2019" -Tv142 -A win32 ..(vs2017则是cmake .. -G"Visual Studio 15 2017" -Tv141 -A win32 ..)这里以Test作为项目名。
  4. 执行完毕后就会在build目录下生成VS工程,双击sln文件打开工程。
    cocos2d-x 4.0 安装教程(Windows + VS) + cocosui入门知识储备_第2张图片
  5. 直接运行就可以生成可执行文件,在build\bin\Test\Debug目录下。双击运行Test.exe就可以看到hello world啦。
    cocos2d-x 4.0 安装教程(Windows + VS) + cocosui入门知识储备_第3张图片
    cocos2d-x 4.0 安装教程(Windows + VS) + cocosui入门知识储备_第4张图片
    运行时可能会遇到一个无法启动ALL_BUILD的问题,这里将Test(你的项目)设定为启动项目即可。
    cocos2d-x 4.0 安装教程(Windows + VS) + cocosui入门知识储备_第5张图片
    cocos2d-x 4.0 安装教程(Windows + VS) + cocosui入门知识储备_第6张图片
    还有的情况下使用了log会运行失败,报错信息如下所示,错误指向文件CCConsloe.hvoid 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 即可。

    实际上在VS工程中也是通过加载csb文件来进行渲染的。

    bool HelloWorld::init()
    {
    	if ( !Layer::init() )
        {
            return false;
        }
        
        auto rootNode = CSLoader::createNode("MainScene.csb");
    
        addChild(rootNode);
    
        return true;
    }
    

    运行结果为:
    cocos2d-x 4.0 安装教程(Windows + VS) + cocosui入门知识储备_第7张图片


(四)新手文档

Hello World

从HelloWorld的demo也可以看出,资源文件存放在Resources目录下。
cocos2d-x 4.0 安装教程(Windows + VS) + cocosui入门知识储备_第8张图片
运行过程:

  1. 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();
    }
    
  2. 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指针指向当前对象
    }
    
  3. Application的静态方法getInstance直接返回单例,即静态的sm_pSharedApplication指针。
    // CCApplication-win32.cpp
    Application* Application::getInstance()
    {
        CC_ASSERT(sm_pSharedApplication);
        return sm_pSharedApplication;
    }
    
  4. 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;
    }
    
  5. 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;
        }
    }
    
  6. 所以我们可以在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

    cocos2d-x - Ref 引用计数

  • Node

    cocos2d-x - Node 节点

  • 渲染

    cocos2d-x - Render 渲染

  • cocosui

    除了LabelMenu,其余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 _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

你可能感兴趣的:(编程语言,cocos2d)