Cocos2dx Android工程的启动过程

转自:http://blog.csdn.net/maximuszhou/article/details/39448971

本文通过分析cocos2d-x(分析版本为cocos2d-x-2.2.1)自身提供的示例程序HelloLua(在目录$(sourcedir)\samples\Lua\HelloLua\下)来分析cocos2d-x的在android平台下的具体启动过程。
    我们知道android平台的游戏是从一个Activity开始的。HelloLua的启动Activity是在文件HelloLua.java中定义的,相关代码如下:
[java]  view plain  copy
  1. public class HelloLua extends Cocos2dxActivity{  
  2.     protected void onCreate(Bundle savedInstanceState){  
  3.         super.onCreate(savedInstanceState);  
  4.     }  
  5.       
  6.     public Cocos2dxGLSurfaceView onCreateGLSurfaceView() {  
  7.         return new LuaGLSurfaceView(this);  
  8.     }  
  9.       
  10.     static {  
  11.         System.loadLibrary("hellolua");  
  12.    }  
  13. }  

从上面的代码,启动Activity是继承Cocos2dxActivity的,在游戏启动时,Activity首先会执行静态代码块,加载libhellolua.so库,这个动态链接库是在用NDK编译的时候生成的;然后就是执行onCreate方法,这里调用了父类Cocos2dxActivity的onCreate方法。 Cocos2dxActivity在

[java]  view plain  copy
  1. $(sourcedir)\cocos2dx\platform\android\java\src\org\cocos2dx\lib\Cocos2dxActivity.java  

中定义,其OnCreate方法代码如下:

[java]  view plain  copy
  1. protected void onCreate(final Bundle savedInstanceState) {  
  2.     super.onCreate(savedInstanceState);  
  3.     sContext = this;  
  4.     this.mHandler = new Cocos2dxHandler(this);  
  5.   
  6.     this.init();  
  7.   
  8.     Cocos2dxHelper.init(thisthis);  
  9. }  

这里主要是执行初始化过程,Cocos2dxHandler主要处理显示Dialog的消息,Cocos2dxHelper是个辅助类,我们主要看init()方法,代码如下:

[java]  view plain  copy
  1. public void init() {  
  2.           
  3.         // FrameLayout  
  4.         ViewGroup.LayoutParams framelayout_params =  
  5.             new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,  
  6.                                        ViewGroup.LayoutParams.FILL_PARENT);  
  7.         FrameLayout framelayout = new FrameLayout(this);  
  8.         framelayout.setLayoutParams(framelayout_params);  
  9.   
  10.         // Cocos2dxEditText layout  
  11.         ViewGroup.LayoutParams edittext_layout_params =  
  12.             new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,  
  13.                                        ViewGroup.LayoutParams.WRAP_CONTENT);  
  14.         Cocos2dxEditText edittext = new Cocos2dxEditText(this);  
  15.         edittext.setLayoutParams(edittext_layout_params);  
  16.   
  17.         // ...add to FrameLayout  
  18.         framelayout.addView(edittext);  
  19.   
  20.         // Cocos2dxGLSurfaceView  
  21.         this.mGLSurfaceView = this.onCreateView();  
  22.   
  23.         // ...add to FrameLayout  
  24.         framelayout.addView(this.mGLSurfaceView);  
  25.   
  26.         // Switch to supported OpenGL (ARGB888) mode on emulator  
  27.         if (isAndroidEmulator())  
  28.            this.mGLSurfaceView.setEGLConfigChooser(8 , 888160);  
  29.   
  30.         this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());  
  31.         this.mGLSurfaceView.setCocos2dxEditText(edittext);  
  32.   
  33.         // Set framelayout as the content view  
  34.         setContentView(framelayout);  
  35. }  
init()方法就是为Activity绑定View Hierarchy,这里的FrameLayout是根View,FrameLayout又包含一个Cocos2dxEditText和一个Cocos2dxGLSurfaceView,这个Cocos2dxGLSurfaceView的实例在方法onCreateView中创建的,代码非常简单,如下:
[java]  view plain  copy
  1. public Cocos2dxGLSurfaceView onCreateView() {  
  2.     return new Cocos2dxGLSurfaceView(this);  
  3. }  
而类Cocos2dxGLSurfaceView本身是在
[java]  view plain  copy
  1. $(sourcedir)\cocos2dx\platform\android\java\src\org\cocos2dx\lib\Cocos2dxGLSurfaceView.java  

中定义,它继承自opengl中类GLSurfaceView。GLSurfaceView的核心就是Renderer,初始化时会调用Renderer的onSurfaceCreated方法,每一帧的绘制是通过调用Renderer的onDrawFrame方法。Cocos2dxGLSurfaceView的Renderer是一个Cocos2dxRenderer对象,类Cocos2dxRenderer在

[java]  view plain  copy
  1. $(sourcedir)\cocos2dx\platform\android\java\src\org\cocos2dx\lib\Cocos2dxRenderer.java  

中定义,其方法onSurfaceCreated代码如下:

[java]  view plain  copy
  1. public void onSurfaceCreated(final GL10 pGL10, final EGLConfig pEGLConfig) {  
  2.     Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight);  
  3.     this.mLastTickInNanoSeconds = System.nanoTime();  
  4. }  

这里主要调用了一个方法nativeInit,nativeInit被声明为:

[java]  view plain  copy
  1. private static native void nativeInit(final int pWidth, final int pHeight);  

即它是一个native的函数,即该函数用C++代码中实现,关于native函数简单说,就是在Java在调用C++代码实现的函数,即需要采用JNI技术(可以看成是Java与C++交互的一个协议~)。方法nativeInit对应的C++实现是在(sourcedir)\samples\Lua\HelloLua\proj.Android\jni\hellolua\main.cpp中:

[cpp]  view plain  copy
  1. void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)  
  2. {  
  3.     if (!CCDirector::sharedDirector()->getOpenGLView())  
  4.     {  
  5.         CCEGLView *view = CCEGLView::sharedOpenGLView();  
  6.         view->setFrameSize(w, h);  
  7.   
  8.         AppDelegate *pAppDelegate = new AppDelegate();  
  9.         CCApplication::sharedApplication()->run();  
  10.     }  
  11.     else  
  12.     {  
  13.     .....  
  14.     }  
  15. }  
这里的函数命名Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit是完全按照JNI实现方法的命名规则。CCDirector就是是cocos2d-x中的导演类,该类在$(sourcedir)\cocos2dx\CCDirector.cpp中实现。方法sharedDirector是类CCDirector的一个静态方法,该方法用来创建游戏中唯一的CCDirector对象,代码如下:
[cpp]  view plain  copy
  1. CCDirector* CCDirector::sharedDirector(void)  
  2. {  
  3.     if (!s_SharedDirector)  
  4.     {  
  5.         s_SharedDirector = new CCDisplayLinkDirector();  
  6.         s_SharedDirector->init();  
  7.     }  
  8.   
  9.     return s_SharedDirector;  
  10. }  

这里的CCCCDisplayLinkDirector是CCDirector的子类,方法init()做一些初始化工作:

[cpp]  view plain  copy
  1. bool CCDirector::init(void)  
  2. {  
  3.     setDefaultValues();  
  4.   
  5.     //场景相关  
  6.     ...  
  7.     ...  
  8.   
  9.     //管理场景的数组栈  
  10.     m_pobScenesStack = new CCArray();  
  11.     m_pobScenesStack->init();  
  12.   
  13.     // 一些FPS等相关的成员变量初始化  
  14.     ...  
  15.     ...  
  16.   
  17.     //初始化调度器对象  
  18.     m_pScheduler = new CCScheduler();  
  19.   
  20.     //动作管理器对象  
  21.     m_pActionManager = new CCActionManager();  
  22.     m_pScheduler->scheduleUpdateForTarget(m_pActionManager, kCCPrioritySystem, false);  
  23.   
  24.     // touchDispatcher,KeypadDispatcher,Accelerometer等对象初始化  
  25.     ...  
  26.     ...  
  27.   
  28.     // CCPoolManager中实现了cocos2d-x CCObject对象的内存管理机制  
  29.     CCPoolManager::sharedPoolManager()->push();  
  30.   
  31.     return true;  
  32. }  

创建好导演对象后,Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit中调用了该对象的getOpenGLView方法:

[cpp]  view plain  copy
  1. inline CCEGLView* getOpenGLView(void) { return m_pobOpenGLView; }  

该方法返回用于游戏绘制的CCEGLView,在init()中,m_pobOpenGLView复制为NULL。在android平台下,这个CCEGLView其实没有什么作用,因为游戏都是绘制在Cocos2dxGLSurfaceView上的,因此方法Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit接着执行if分支:

[cpp]  view plain  copy
  1. CCEGLView *view = CCEGLView::sharedOpenGLView();  
  2. view->setFrameSize(w, h);  

这里同样创建了游戏中类CCEGLView的唯一实例,类在$(sourcedir)\cocos2dx\platform\android\CCEGLView.cpp中实现。接着执行

[cpp]  view plain  copy
  1. AppDelegate *pAppDelegate = new AppDelegate();  
  2. CCApplication::sharedApplication()->run();  

创建了一个类AppDelegate对象,类AppDelegate在$(sourcedir)\samples\Lua\HelloLua\Classes\AppDelegate.cpp中实现,类AppDelegate在各个平台之间共用的:

[cpp]  view plain  copy
  1. class  AppDelegate : private cocos2d::CCApplication  
  2. {  
  3. public:  
  4.     AppDelegate();  
  5.     virtual ~AppDelegate();  
  6.     virtual bool applicationDidFinishLaunching();  
  7.   
  8.     virtual void applicationDidEnterBackground();  
  9.   
  10.     virtual void applicationWillEnterForeground();  
  11. };  

该类继承自CCApplication,注意类CCApplication是在$(sourcedir)\cocos2dx\platform\android\CCApplication.cpp中实现的,它的实现是与平台相关的。创建AppDelegate实例的工作实质就是实例化CCApplication,它的构造函数代码如下:

[cpp]  view plain  copy
  1. CCApplication::CCApplication()  
  2. {  
  3.     CCAssert(! sm_pSharedApplication, "");  
  4.     sm_pSharedApplication = this;  
  5. }  

因此在实例化AppDelegate时,主要工作是用AppDelegate对象初始化全局变量sm_pSharedApplication。接着后面调用CCApplication::sharedApplication()->run(),相关代码如下:

[cpp]  view plain  copy
  1. CCApplication* CCApplication::sharedApplication()  
  2. {  
  3.     CCAssert(sm_pSharedApplication, "");  
  4.     return sm_pSharedApplication;  
  5. }  
  6. int CCApplication::run()  
  7. {  
  8.     // Initialize instance and cocos2d.  
  9.     if (! applicationDidFinishLaunching())  
  10.     {  
  11.         return 0;  
  12.     }  
  13.       
  14.     return -1;  
  15. }  

run中调用的虚函数applicationDidFinishLaunching实质上调用的是子类AppDelegate中的applicationDidFinishLaunching,代码如下:

[cpp]  view plain  copy
  1. bool AppDelegate::applicationDidFinishLaunching()  
  2. {  
  3.     //初始化director  
  4.     CCDirector *pDirector = CCDirector::sharedDirector();  
  5.     pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());  
  6.       
  7.     CCEGLView::sharedOpenGLView()->setDesignResolutionSize(480, 320, kResolutionNoBorder);  
  8.   
  9.     //显示FPS  
  10.     pDirector->setDisplayStats(true);  
  11.   
  12.     // 设置FPS  
  13.     pDirector->setAnimationInterval(1.0 / 60);  
  14.   
  15.     //创建Lua脚本引擎  
  16.     CCLuaEngine* pEngine = CCLuaEngine::defaultEngine();  
  17.     CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);  
  18.   
  19.     //加载执行lua脚本  
  20.     std::string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua");  
  21.     pEngine->executeScriptFile(path.c_str());  
  22.   
  23.     return true;  
  24. }  
至此游戏启动完成,但是我们并没看到类似while循环的东东(btw,分析windows和Linux平台上cocos2d-x的启动流程可以清楚看到类似while循环东东~),这是因为在android平台下,主循环是由渲染线程发起的,通过不断调用render来驱动的,具体说就是调用Java类Cocos2dxRenderer中的onDrawFrame方法:
[java]  view plain  copy
  1. public void onDrawFrame(final GL10 gl) {  
  2.     Cocos2dxRenderer.nativeRender();  
  3. }  

其唯一的工作是调用了nativeRender方法,该方法是native的,其对应的实现是:

[cpp]  view plain  copy
  1. JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender(JNIEnv* env) {  
  2.     cocos2d::CCDirector::sharedDirector()->mainLoop();  
  3. }  

即执行导演对象中的方法mainLoop:

[cpp]  view plain  copy
  1. void CCDisplayLinkDirector::mainLoop(void)  
  2. {  
  3.     // 此变量决定程序是否结束  
  4.     if (m_bPurgeDirecotorInNextLoop)  
  5.     {  
  6.         m_bPurgeDirecotorInNextLoop = false;  
  7.          
  8.         purgeDirector();  
  9.     }  
  10.     else if (! m_bInvalid)  
  11.      {  
  12.         //屏幕绘制,并做一些相应的逻辑处理  
  13.          drawScene();  
  14.        
  15.          // 释放对象管理器中CCObject 对象  
  16.          CCPoolManager::sharedPoolManager()->pop();          
  17.      }  
  18. }  

这个方法就是所有平台最后真正执行的循环体。

参考资料

http://blog.csdn.net/cjj7905150/article/details/22587021
http://www.dapps.net/dev/gamedev/cocos2d-x-dev-the-game-loop-4.html
http://blog.leafsoar.com/archives/2013/05-05.html






1、安卓工程下的设置启动activity为src下面的AppActivity,启动调用的onCreate并没有做过多的事情,只是调用了父类Cocos2dxActivity的onCreate。AppActivity代码如下:

import org.cocos2dx.lib.Cocos2dxActivity;

public class AppActivity extends Cocos2dxActivity {
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ......
    }
}

2、Cocos2dxActivity在cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxActivity.java里,查看onCreate,代码如下:

@Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initFMOD(); //加载声音库
        try {
            Class.forName("android.os.AsyncTask");
        } catch (Throwable ignor) {
            ignor.printStackTrace();
        }
        sContext = this;
        PSNetwork.init(sContext); //初始化安卓网络连接服务
        if (Build.VERSION.SDK_INT >= 23) {
            requestUserPermissions(); //系统的部分授权
        }
        mMacAddress = MacAddressUtil.getMacAddress(sContext);//获取wifi的MAC地址
        CocosPlayClient.init(this, false); //暂时无用
        boolean isLoadOK = onLoadNativeLibraries(); //把工程中libs下面的so文件load进来,定义在AndroidManifest, meta-data标签下,android.app.lib_name. 最终在包的data/data/com.XXX.XXX/lib下面
        if (false == isLoadOK) {
            return;
        }

        this.mHandler = new Cocos2dxHandler(this);//处理安卓的弹窗等

        Cocos2dxHelper.init(this);

        this.mGLContextAttrs = getGLContextAttrs();//获取OpenGLEs的相关属性
        this.init(); //说明如下文

        if (mVideoHelper == null) {
            mVideoHelper = new Cocos2dxVideoHelper(this, mFrameLayout);
        }

        if (mWebViewHelper == null) {
            mWebViewHelper = new Cocos2dxWebViewHelper(mFrameLayout);
        }

        if(mEditBoxHelper == null){
            mEditBoxHelper = new Cocos2dxEditBoxHelper(mFrameLayout);
        }
        if (null == mScreenListener) {
            mScreenListener = new ScreenListener(this);
            mScreenListener.begin(this);
        }
    }

3、Cocos2dxActivity的init函数如下:

    public void init() {
        // FrameLayout
        ViewGroup.LayoutParams framelayout_params = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        mFrameLayout = new ResizeLayout(this); //继承自FrameLayout,看成是一块画布(canvas),其他控件添加在上面
        mFrameLayout.setLayoutParams(framelayout_params);

        // Cocos2dxEditText layout
        ViewGroup.LayoutParams edittext_layout_params = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        Cocos2dxEditBox edittext = new Cocos2dxEditBox(this);
        edittext.setLayoutParams(edittext_layout_params);//输入框

        // ...add to FrameLayout
        mFrameLayout.addView(edittext);

        // Cocos2dxGLSurfaceView
        this.mGLSurfaceView = this.onCreateView();//创建游戏的渲染,接受输入事件的OpenGL类

        // ...add to FrameLayout
        mFrameLayout.addView(this.mGLSurfaceView);//添加到画布上

        // Switch to supported OpenGL (ARGB888) mode on emulator
        if (isAndroidEmulator())
            this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);

        this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());//注册自主实现的渲染器,内容如下
        this.mGLSurfaceView.setCocos2dxEditText(edittext);//输入框

        this.onCreateFrameLayout();
        // Set framelayout as the content view
        setContentView(mFrameLayout);//设置这个Activity的显示界面
    }

4、Cocos2dxRenderer,cocos2dx的渲染器,继承自android.opengl.GLSurfaceView.Renderer,当3中的GLSurfaceView被创建的时候会调用render的onSurfaceCreated()方法; 当GLSurfaceView大小或者横竖屏发生变化的时候调用render的onSurfaceChanged()方法; 当系统每一次重新画GLSurfaceView的时候,调用onDrawFrame()方法。所以Cocos2dxRender对这三个方法进行了重写。

    @Override
    public void onSurfaceCreated(final GL10 GL10, final EGLConfig EGLConfig) {
        Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight);//此处调用了一个定义为native的函数,设置glview等,通过jni来访问c++实现的方法,接口实现在cocos/platform/android/javaactivity-android.cpp里面,下面的5会继续讲
        this.mLastTickInNanoSeconds = System.nanoTime();
        mNativeInitCompleted = true;
        try{
            Cocos2dxActivity activity = (Cocos2dxActivity) Cocos2dxActivity.getContext();
            activity.onSurfaceCreated(this, GL10, EGLConfig);
        }catch( Throwable e ){
            e.printStackTrace();
        }
    }
@Override
    public void onDrawFrame(final GL10 gl) { //系统自动每秒钟调用60次这个函数
        /*
         * No need to use algorithm in default(60 FPS) situation, since
         * onDrawFrame() was called by system 60 times per second by default.
         */
        if( true == mIsPaused ){
            return;
        }
        if( mDelayResumeCount <= DELAY_RESUME_COUNT ){
            mDelayResumeCount = mDelayResumeCount + 1;
            if( mDelayResumeCount == DELAY_RESUME_COUNT ){
                Cocos2dxRenderer.nativeOnResume();
                try{
                    Cocos2dxActivity activity = (Cocos2dxActivity) Cocos2dxActivity.getContext();
                    activity.nativeResume();
                }catch( Throwable e ){
                    e.printStackTrace();
                }
            }
            return;
        }
        if (sAnimationInterval <= 1.0 / 60 * Cocos2dxRenderer.NANOSECONDSPERSECOND) {
            Cocos2dxRenderer.nativeRender(); //大于等于每秒60帧则不经过算法处理,直接执行nativeRender,将在6中有说明
        } else {
            final long now = System.nanoTime();
            final long interval = now - this.mLastTickInNanoSeconds;

            if (interval < Cocos2dxRenderer.sAnimationInterval) { //按照设置的帧数,如果没有到时间,则sleep到相应的时间
                try {
                    Thread.sleep((Cocos2dxRenderer.sAnimationInterval - interval)
                            / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
                } catch (final Exception e) {
                }
            }
            /*
             * Render time MUST be counted in, or the FPS will slower than
             * appointed.
             */
            this.mLastTickInNanoSeconds = System.nanoTime();
            Cocos2dxRenderer.nativeRender();
        }
        try{
            Cocos2dxActivity activity = (Cocos2dxActivity) Cocos2dxActivity.getContext();
            activity.onDrawFrame(this, gl);
        }catch( Throwable e ){
            e.printStackTrace();
        }
    }
    @Override
    public void onSurfaceChanged(final GL10 GL10, final int width,
            final int height) {
        Cocos2dxRenderer.nativeOnSurfaceChanged(width, height);
        try{
            Cocos2dxActivity activity = (Cocos2dxActivity) Cocos2dxActivity.getContext();
            activity.onSurfaceChanged(this, GL10, width, height);
        }catch( Throwable e ){
            e.printStackTrace();
        }
    }

5、 4里面onSurfaceCreated的nativeInit的实现放在cocos/platform/android/javaactivity-android.cpp,方法如下:

void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)
{
    auto director = cocos2d::Director::getInstance();
    auto glview = director->getOpenGLView();
    if (!glview)
    {
        glview = cocos2d::GLViewImpl::create("Android app");
        glview->setFrameSize(w, h);
        director->setOpenGLView(glview); //设置glview

        //cocos_android_app_init(env, thiz);

        cocos2d::Application::getInstance()->run(); //程序开始运行,android的Application实现放在CCApplication-android.cpp,run的代码如下
    }
    else
    {
        cocos2d::GL::invalidateStateCache();
        cocos2d::GLProgramCache::getInstance()->reloadDefaultGLPrograms();
        cocos2d::DrawPrimitives::init();
        cocos2d::VolatileTextureMgr::reloadAllTextures();

        cocos2d::EventCustom recreatedEvent(EVENT_RENDERER_RECREATED);
        director->getEventDispatcher()->dispatchEvent(&recreatedEvent);
        director->setGLDefaultValues();
    }
}
int Application::run()
{
    // Initialize instance and cocos2d.
    if (! applicationDidFinishLaunching()) //applicationDidFinishLanunching在自己的Classes/AppDelegate进行重写,游戏已经启动
    {
        return 0;
    }
    
    return -1;
}
bool AppDelegate::applicationDidDinishLaunching()
{
    ......
    director->setOpenGLView(glview);
    director->setAnimationInterval(1/30.f); //设置帧数,会调用Application-android的setAnimationInterval,再通过JniHelper调用Cocos2dxRenderer中的setAnimationInterval
    director->runWithScene(scene); //
    return true
}

6、关于4中onDrawFrame涉及到的函数nativeRender,它也是一个native类型的函数,实现放在cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxRenderer.cpp

    JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender(JNIEnv* env) {
        cocos2d::Director::getInstance()->mainLoop(); //进入游戏的主循环,Director的mainLoop,事件的分发,渲染,内存池的管理
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值