cocos2d-x 提升篇 (14) cocos2d-x游戏程序的构建

本文详细介绍了cocos2d-x游戏程序的构建过程,重点关注proj.android和proj.linux文件夹,以及Resources和Classes目录。讲解了AndroidManifest.xml、LOCAL_SRC_FILES和LOCAL_C_INCLUDES在Android构建中的作用,以及main.cpp在Linux构建中的关键角色。此外,还讨论了AppDelegate类的功能,包括初始化GLContextAttrs和处理应用程序生命周期事件。文章提到了cocos2d-x使用cmake构建工程的重要性,并简单提及了Android和Linux项目的构建方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

打开src/cocos2d-x-3.14.1/tests/cpp-tests的目录可以看到有很多和平台相关的文件夹

比如android, android-studio, linux, ios, mac, win10, win32,

所以说只要游戏写一次,按照编译指令就会生成对应的文件,比如编译android会生成对应的apk,ubuntu运行的话会生成对应的可执行程序,在前面的章节中有介绍。

这里主要看两个文件夹proj.android 和proj.linux

和两个目录Classes目录和Resources目录,根据它们的定义就很容易知道Resources是放资源文件的,Classes是放源码目录的。

cocos2d-x使用的是cmake构建工程,cmake后面需要专门的章节进行介绍,

因为懂得怎么写cmake就知道怎么构建工程,对理解cocos2d-x引擎整体的构建起到举足轻重的作用。


来看proj.android文件夹

进入proj.android文件夹,就是一个最简单的android工程,java部分几乎没有什么实现,很多都是android自动生成的文件,有几个地方需要自己改动:

res/values/strings.xml文件下的

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">CppTests</string>
</resources>
这里可以更改生成apk的名字,也可以更改程序的显示图片,最直接的做法就是替换res/drawable-Idpi中的icon.png和它命名一样的名字。

AndroidManifest.xml规定了程序的入口

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
我们最需要改的是jin目录下的Android.mk文件

其中LOCAL_SRC_FILES是重中之重,规定自己编写的文件路径位置,LOCAL_C_INCLUDES规定头文件位置。

android是通过自身的ndk开发调用生成cpp_tests_shared库的,这里就不讲ndk怎么实现,语言的本身只是更方便。

其实android系统的实现都是用的C++和C,通过java提供的jni功能完成所有的到java的转换。

app层用java可以更方便开发者快速有效地开发功能比较好的程序,而不需要关注底层的实现,需要明确注意client和implementation的身份。

说了一大段的废话,其实最重要的是上面标红色的部分。

最后再说一句它是用ant打包的。


再来看proj.linux构建linux程序

这个更简单只有一个main.cpp文件

#include "../Classes/AppDelegate.h"
#include "cocos2d.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string>

USING_NS_CC;

int main(int argc, char **argv)
{
    // create the application instance
    AppDelegate app;
    return Application::getInstance()->run();
}
在这里创建了一个类,就像冰山里的一角,下面蕴藏着巨大的密码。

return Application::getInstance()->run();

让程序一直进入循环中,接收外部的点击事件,分发事件,所以这个run()会在程序结束返回。

重点关注CMakeLists.txt文件, 它定义了一个变量TEST_SRC表示自己写的文件夹。

关于文件的部分已经讲完了,接着分析Classes文件夹中的第一个类AppDelegate.cpp,万事开头难,后面就轻车熟路了。


AppDelegate.h

可以看到这个类定义了一个默认构造器,定义了一个析构器,

initGLContextAttrs

根据函数名大概可以知道初始化opengl相关的属性的,具体是操作什么还得看源码,后面的VisibleSize什么的都与它相关。

applicationDidFinishLaunching, applicationDidEnterBackground, applicationWillEnterForground根据它们的字面意思和注释就很容易知道干什么。

程序启动之后做什么,程序进入背景怎么做,程序进入前景(用户可以看到的界面)做什么,在AppDelegate.cpp文件中都有说明。

有一个_testController变量,

#ifndef  _APP_DELEGATE_H_
#define  _APP_DELEGATE_H_

#include "cocos2d.h"

class TestController;
/**
@brief    The cocos2d Application.

Private inheritance here hides part of interface from Director.
*/
class  AppDelegate : private cocos2d::Application
{
public:
    AppDelegate();
    virtual ~AppDelegate();

    virtual void initGLContextAttrs();

    /**
    @brief    Implement Director and cocos2d::Scene* init code here.
    @return true    Initialize success, app continue.
    @return false   Initialize failed, app terminate.
    */
    virtual bool applicationDidFinishLaunching();

    /**
    @brief  Called when the application moves to the background
    @param  the pointer of the application
    */
    virtual void applicationDidEnterBackground();

    /**
    @brief  Called when the application reenters the foreground
    @param  the pointer of the application
    */
    virtual void applicationWillEnterForeground();

private:
    TestController* _testController;
};

#endif // _APP_DELEGATE_H_


AppDelegate.cpp文件

构造器设置_testController为空,析构一个SimpleAudioEngine 和一个ArmureDataManager,

AudioEngine是和声音有关的,暂时不要去管它,等学到声音相关的再去看。

ArmureDataManager这个类的注释是这样的

@brief    format and manage armature configuration and armature animation

这个应该是和cocostudio相关的,不过现在官方不提倡使用cocostudio,提倡使用cocos creator,不过这里使用的是qt creator,一个很不错的c++ gui的ide.

GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};

表示颜色的,应该是8位表示红色,8位表示绿色,8位表示蓝色,图像处理中的二进制量化。

等后面需要深入opengl的时候再来了解GLContextAttrs和GLView的作用,现在只知道需要这么做,初始化图形底层接口。

#include "AppDelegate.h"

#include "cocos2d.h"
#include "controller.h"
#include "editor-support/cocostudio/CocoStudio.h"
#include "extensions/cocos-ext.h"

USING_NS_CC;

AppDelegate::AppDelegate()
: _testController(nullptr)
{
}

AppDelegate::~AppDelegate()
{
    //SimpleAudioEngine::end();
    cocostudio::ArmatureDataManager::destroyInstance();
}

// if you want a different context, modify the value of glContextAttrs
// it will affect all platforms
void AppDelegate::initGLContextAttrs()
{
    // set OpenGL context attributes: red,green,blue,alpha,depth,stencil
    GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};

    GLView::setGLContextAttrs(glContextAttrs);
}

bool AppDelegate::applicationDidFinishLaunching()
{
    // As an example, load config file
    // FIXME:: This should be loaded before the Director is initialized,
    // FIXME:: but at this point, the director is already initialized
    Configuration::getInstance()->loadConfigFile("configs/config-example.plist");

    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
        glview = GLViewImpl::create("Cpp Tests");
        director->setOpenGLView(glview);
    }

    director->setDisplayStats(true);
    director->setAnimationInterval(1.0f / 60);

    auto screenSize = glview->getFrameSize();
    auto designSize = Size(480, 320);

    auto fileUtils = FileUtils::getInstance();
    std::vector<std::string> searchPaths;
    
    if (screenSize.height > 320)
    {
        auto resourceSize = Size(960, 640);
        searchPaths.push_back("hd");
        searchPaths.push_back("ccs-res/hd");
        searchPaths.push_back("ccs-res");
        searchPaths.push_back("Manifests");
        director->setContentScaleFactor(resourceSize.height/designSize.height);

        searchPaths.push_back("hd/ActionTimeline");
    }
    else
    {
        searchPaths.push_back("ccs-res");
        
        searchPaths.push_back("ActionTimeline");
    }
    
    fileUtils->setSearchPaths(searchPaths);

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    // a bug in DirectX 11 level9-x on the device prevents ResolutionPolicy::NO_BORDER from working correctly
    glview->setDesignResolutionSize(designSize.width, designSize.height, ResolutionPolicy::SHOW_ALL);
#else
    glview->setDesignResolutionSize(designSize.width, designSize.height, ResolutionPolicy::NO_BORDER);
#endif

    // Enable Remote Console
    auto console = director->getConsole();
    console->listenOnTCP(5678);

    _testController = TestController::getInstance();

    // To enable built-in VR, use this line.
//    auto vrImpl = new VRGenericRenderer;
//    glview->setVR(vrImpl);

    return true;
}

// This function will be called when the app is inactive. Note, when receiving a phone call it is invoked.
void AppDelegate::applicationDidEnterBackground()
{
    if (_testController)
    {
        _testController->onEnterBackground();
    }
    
    Director::getInstance()->stopAnimation();
}

// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground()
{
    if (_testController)
    {
        _testController->onEnterForeground();
    }
    
    Director::getInstance()->startAnimation();
}

applicationDidEnterBackground, applicationWillEnterForground没什么讲的,

AppDelegate类和TestController类应该是属于聚合关系,网上查了一下类的关系,组合和聚合比较难以分辨,基本上两者差不错。或者可以打这样一个比方,组合比聚合更紧密,人有一个脑袋,人和脑袋就是组合关系,人和世界是属于聚合关系,没有人,世界依然会存在,不知道这样比喻是否贴切。

网上找的一个图,表示它们两者的关系。

applicationDidFinishLaunching做的事情比较多,

.plist是读文件,这个文件的后缀不常见,看到list也就可以联想到是一系列文件,p大概是push的意思,大概是把一系列的文件放大一个文件中,节约内存。

后面会专门介绍png图像,以及其它的文件的制作,这里不展开叙述。

游戏中的字体部分也十分的关键。

Director是一个单例模式,

单例表示整个程序中只有这么一个,就像Director描述的那样整个电影就只有一个导演。

里面有非常多程序的属性以及方法之类的类,截取部分

//texture cache belongs to this director
    TextureCache *_textureCache;

    float _animationInterval;
    float _oldAnimationInterval;

    /* landscape mode ? */
    bool _landscape;
    
    bool _displayStats;
    float _accumDt;
    float _frameRate;
    
    LabelAtlas *_FPSLabel;
    LabelAtlas *_drawnBatchesLabel;
    LabelAtlas *_drawnVerticesLabel;
    
可以看到还能设置是否横竖屏,

Director类没有构造器,只能通过getInstance()这个方法获取

Director* Director::getInstance()
{
    if (!s_SharedDirector)
    {
        s_SharedDirector = new (std::nothrow) Director;
        CCASSERT(s_SharedDirector, "FATAL: Not enough memory");
        s_SharedDirector->init();
    }

    return s_SharedDirector;
}
同时可以看到s_ShareDirector是一个静态成员变量,整个程序只能存在一个,很常用的单例模式构造模型。

static Director *s_SharedDirector = nullptr;

同时可以看到程序根据屏幕尺寸的大小做了一些适配,读取不同的文件夹。

屏幕适配是个比较复杂的问题,如果使用cocos new新建的工程,和这个有点不同,

屏幕适配还是个比较烦人的问题。

最后这句话

    _testController = TestController::getInstance();
走进另外一个类,TestController,也是一个单例模式。

到这里AppDelegate.cpp这个类基本上讲完了,留下一个大概的印象就可以了,关键是后面怎么调用api,

其实也可以看一个空工程的例子。

有时直接

Director->getInstance()->replaceScene(NewScene::createScene());
调到一个新的场景中,很少有什么东西在这个类中实现。

接下来看为了构建整个工程,其它和功能无关的辅助类,比如这个TestController,顾名思义,就是用来控制所有需要测试的case的。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值