Os jogos na plataforma Android começam com uma atividade. (Em outras palavras, parece que todos os aplicativos Android começam na Activity).
Existe um diretório no código-fonte do mecanismo, que é o código java do android, que é um código de modelo. Quase todos os jogos usam isso e não muda muito. Você pode assistir se não acredita em mim
O diretório YourCocos2dxDir/cocos2dx/platform/android/java é onde você copiará o código java como modelo ao criar um projeto android.
Vamos dar uma olhada no código do HelloCpp
[java] ver cópia simples
- pacote org.cocos2dx.hellocpp;
- import org.cocos2dx.lib.Cocos2dxActivity;
- importar android.os.Bundle;
- classe pública HelloCpp estende Cocos2dxActivity{
- void protegido onCreate(Pacote salvadoInstanceState){
- super .onCreate(savedInstanceState);
- }
- estático {
- System.loadLibrary("hellocpp");
- }
- }
Simples, certo. Apenas algumas linhas de código, aqui estão dois problemas
1. Cocos2dxActivity é a Activity principal.
2. A parte C++ do jogo, incluindo a parte do motor, é compilada em uma biblioteca de links dinâmicos hellocpp. Aqui está a biblioteca de links dinâmicos hellocpp carregada.
Essa biblioteca de vínculo dinâmico é gerada ao compilar com o NDK, que é libs/armeabi/libhellocpp.so. (muito longe)
Vamos dar uma olhada na Activity Cocos2dxActivity.
[java] ver cópia simples
- classe abstrata pública Cocos2dxActivity estende Activity implementa Cocos2dxHelperListener {
- // ================================================ =============
- // Constantes
- // ================================================ =============
- string final estática privada TAG = Cocos2dxActivity. classe .getSimpleName();
- // ================================================ =============
- // Campos
- // ================================================ =============
- private Cocos2dxGLSurfaceView mGLSurfaceView;//Preste atenção a este SurfaceView
- privado Cocos2dxHandler mHandler;
- // ================================================ =============
- // Construtores
- // ================================================ =============
- @Sobrepor
- void protegido onCreate ( Pacote final saveInstanceState) {
- super .onCreate(savedInstanceState);
- este .mHandler = new Cocos2dxHandler( este );
- este .init();
- Cocos2dxHelper.init( este , este );
- }
- // ================================================ =============
- // Getter & Setter
- // ================================================ =============
- // ================================================ =============
- // Métodos para/de SuperClass/Interfaces
- // ================================================ =============
- @Sobrepor
- void protegido onResume() {
- super .onResume();
- Cocos2dxHelper.onResume();
- este .mGLSurfaceView.onResume();
- }
- @Sobrepor
- void protegido onPause() {
- super .onPause();
- Cocos2dxHelper.onPause();
- este .mGLSurfaceView.onPause();
- }
- @Sobrepor
- public void showDialog( final String pTitle, final String pMessage) {
- Mensagem msg = new Mensagem();
- msg.what = Cocos2dxHandler.HANDLER_SHOW_DIALOG;
- msg.obj = new Cocos2dxHandler.DialogMessage(pTitle, pMessage);
- este .mHandler.sendMessage(msg);
- }
- @Sobrepor
- public void showEditTextDialog( final String pTitle, final String pContent, final int pInputMode, final int pInputFlag, final int pReturnType, final int pMaxLength) {
- Mensagem msg = new Mensagem();
- msg.what = Cocos2dxHandler.HANDLER_SHOW_EDITBOX_DIALOG;
- msg.obj = new Cocos2dxHandler.EditBoxMessage(pTitle, pContent, pInputMode, pInputFlag, pReturnType, pMaxLength);
- este .mHandler.sendMessage(msg);
- }
- @Sobrepor
- public void runOnGLThread( final Runnable pRunnable) {
- este .mGLSurfaceView.queueEvent(pRunnable);
- }
- // ================================================ =============
- // Métodos
- // ================================================ =============
- public void init() {
- // FrameLayout
- ViewGroup.LayoutParams framelayout_params =
- novo ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.FILL_PARENT);
- FrameLayout framelayout = new FrameLayout( this ); // layout do quadro, que pode ser coberto camada por camada
- framelayout.setLayoutParams(framelayout_params);
- // Cocos2dxEditText layout
- ViewGroup.LayoutParams edittext_layout_params =
- novo ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- Cocos2dxEditText edittext = new Cocos2dxEditText( this );
- edittext.setLayoutParams(edittext_layout_params);
- // ...adicionar ao FrameLayout
- framelayout.addView(edittext);
- // Cocos2dxGLSurfaceView
- este .mGLSurfaceView = este .onCreateView();
- // ...adicionar ao FrameLayout
- framelayout.addView( this .mGLSurfaceView);// 添加GLSurfaceView
- this .mGLSurfaceView.setCocos2dxRenderer( new Cocos2dxRenderer());//Esta linha, um renderizador
- este .mGLSurfaceView.setCocos2dxEditText(edittext);
- // Definir framelayout como visualização de conteúdo
- setContentView(framelayout);
- }
- public Cocos2dxGLSurfaceView onCreateView() {
- return new Cocos2dxGLSurfaceView( this );
- }
- // ================================================ =============
- // Classes internas e anônimas
- // ================================================ =============
- }
Um monte de código, hein, hein. Na verdade, o núcleo é o mGLSurfaceView e seu novo renderizador Cocos2dxRenderer(). No Android, a renderização OpenGL é composta por um GLSurfaceView e seu renderizador Render. GLSurfaceView exibe a interface e Render renderiza e atualiza. Este Render é, na verdade, um thread de renderização que é executado continuamente e é mantido pela camada de estrutura. Não há muito a dizer aqui.
Olhe para este Cocos2dxRenderer
[java] ver cópia simples
- pacote org.cocos2dx.lib;
- import javax.microedition.khronos.egl.EGLConfig;
- import javax.microedition.khronos.opengles.GL10;
- importar android.opengl.GLSurfaceView;
- public class Cocos2dxRenderer implementa GLSurfaceView.Renderer {
- // ================================================ =============
- // Constantes
- // ================================================ =============
- privado final estático longo NANOSECONDSPERSECOND = 1000000000L;
- privado final estático longo NANOSECONDSPERMICROSECOND = 1000000;
- private static long sAnimationInterval = ( long ) (1.0 / 60 * Cocos2dxRenderer.NANOSECONDSPERSECOND);
- // ================================================ =============
- // Campos
- // ================================================ =============
- privado longo mLlastTickInNanoSeconds;
- private int mScreenWidth;
- private int mScreenHeight;
- // ================================================ =============
- // Construtores
- // ================================================ =============
- // ================================================ =============
- // Getter & Setter
- // ================================================ =============
- public static void setAnimationInterval( final double pAnimationInterval) {
- Cocos2dxRenderer.sAnimationInterval = ( long ) (pAnimationInterval * Cocos2dxRenderer.NANOSECONDSPERSECOND);
- }
- public void setScreenWidthAndHeight( final int pSurfaceWidth, final int pSurfaceHeight) {
- este .mScreenWidth = pSurfaceWidth;
- este .mScreenHeight = pSurfaceHeight;
- }
- // ================================================ =============
- // Métodos para/de SuperClass/Interfaces
- // ================================================ =============
- @Override //①Preste atenção aqui
- public void onSurfaceCreated( final GL10 pGL10, final EGLConfig pEGLConfig) {
- Cocos2dxRenderer.nativeInit( this .mScreenWidth, this .mScreenHeight);//② Inicializar a janela
- este .mLastTickInNanoSeconds = System.nanoTime();
- }
- @Sobrepor
- public void onSurfaceChanged( final GL10 pGL10, final int pWidth, final int pHeight) {
- }
- @Override //③Preste atenção aqui
- public void onDrawFrame( final GL10 gl) {
- /*
- * O algoritmo de controle de FPS não é preciso e irá diminuir o FPS
- * em alguns dispositivos. Então, comente o código de controle do FPS.
- */
- /*
- final long nowInNanoSeconds = System.nanoTime();
- intervalo longo final = nowInNanoSeconds - this.mLastTickInNanoSeconds;
- */
- // deve renderizar um quadro quando onDrawFrame() é chamado ou há um
- // "fantasma"
- Cocos2dxRenderer.nativeRender();//④ Preste atenção especial a isso
- /*
- // controle de fps
- if (interval < Cocos2dxRenderer.sAnimationInterval) {
- tentar {
- // porque renderizamos antes, então devemos dormir duas vezes intervalo de tempo
- Thread.sleep((Cocos2dxRenderer.sAnimationInterval - intervalo) / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
- } catch (exceção final e) {
- }
- }
- this.mLastTickInNanoSeconds = nowInNanoSeconds;
- */
- }
- // ================================================ =============
- // Métodos
- // ================================================ =============
- private static nativo void nativeTouchesBegin( final int pID, final float pX, final float pY);
- private static nativo void nativeTouchesEnd( final int pID, final float pX, final float pY);
- private static nativo void nativeTouchesMove( final int [] pIDs, final float [] pXs, final float [] pYs);
- private static nativo void nativeTouchesCancel( final int [] pIDs, final float [] pXs, final float [] pYs);
- privado estático nativo booleano nativeKeyDown( final int pKeyCode);
- privado estático nativo void nativeRender();
- private static nativo void nativeInit( final int pWidth, final int pHeight);
- privado estático nativo void nativeOnPause();
- privado estático nativo void nativeOnResume();
- public void handleActionDown( final int pID, final float pX, final float pY) {
- Cocos2dxRenderer.nativeTouchesBegin(pID, pX, pY);
- }
- public void handleActionUp( final int pID, final float pX, final float pY) {
- Cocos2dxRenderer.nativeTouchesEnd(pID, pX, pY);
- }
- public void handleActionCancel( final int [] pIDs, final float [] pXs, final float [] pYs) {
- Cocos2dxRenderer.nativeTouchesCancel(pIDs, pXs, pYs);
- }
- public void handleActionMove( final int [] pIDs, final float [] pXs, final float [] pYs) {
- Cocos2dxRenderer.nativeTouchesMove(pIDs, pXs, pYs);
- }
- public void handleKeyDown( final int pKeyCode) {
- Cocos2dxRenderer.nativeKeyDown(pKeyCode);
- }
- public void handleOnPause() {
- Cocos2dxRenderer.nativeOnPause();
- }
- public void handleOnResume() {
- Cocos2dxRenderer.nativeOnResume();
- }
- private static nativo void nativeInsertText( final String pText);
- privado estático nativo void nativeDeleteBackward();
- privado estático nativo String nativeGetContentText();
- public void handleInsertText( final String pText) {
- Cocos2dxRenderer.nativeInsertText(pText);
- }
- public void handleDeleteBackward() {
- Cocos2dxRenderer.nativeDeleteBackward();
- }
- public String getContentText() {
- return Cocos2dxRenderer.nativeGetContentText();
- }
- // ================================================ =============
- // Classes internas e anônimas
- // ================================================ =============
- }
Existem muitos códigos, que parecem ser muito complicados. Na verdade, o contexto é muito claro, nós só olhamos para o contexto e não consideramos os detalhes. Vamos seguir a videira...
Primeiramente, você deve saber que o renderizador de GLSurfaceView deve implementar a interface GLSurfaceView.Renderer. São os três métodos Override acima.
onSurfaceCreated é chamado quando a janela é criada e onSurfaceChanged é chamado quando a janela é criada e o tamanho muda. O método onDrawFrame é o mesmo que o método Ondraw da View comum. Depois que a janela é criada e inicializada, o thread de renderização mantém chamando este método. Estes são determinados pela estrutura, é assim que parece. Vamos analisar alguns lugares marcados no código:
Veja a marca ①, a janela está estabelecida e precisa ser inicializada neste momento. Neste momento, ele chama uma função nativa, marcada com ②. Vendo este formulário de função, você pode pensar em alguma coisa, e falarei sobre isso mais tarde.
Observe a marca ③, como mencionado anteriormente, esta função será chamada continuamente pelo thread de renderização (como o loop infinito do loop principal). Então há uma função muito incrível nele ④.
Essa é outra função nativa, hein, hein.
O código nativo é incrível, é C++. Se você conhece, você sabe, mas se você não sabe, pesquise no Google.
Como é java chamando C++, é jni chamando.
Vamos dar uma olhada em main.cpp em jni/hellocpp/
[cpp] ver cópia simples
- #include "AppDelegate.h"
- #include "plataforma/android/jni/JniHelper.h"
- #include <jni.h>
- #include <android/log.h>
- #include "HelloWorldScene.h"
- #define LOG_TAG "principal"
- #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
- usando namespace cocos2d;
- externo "C"
- {
- jint JNI_OnLoad(JavaVM *vm, void *reservado)
- {
- JniHelper::setJavaVM(vm);
- retornar JNI_VERSION_1_4;
- }
- void java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv* env, jobject this, jint w, jint h);
- {
- if (!CCDirector::sharedDirector()->getOpenGLView())
- {
- CCEGLView *view = CCEGLView::sharedOpenGLView();
- view->setFrameSize(w, h);
- CCLog("com %d,altura %d",w,h);
- AppDelegate *pAppDelegate = new AppDelegate();
- CCApplication::sharedApplication()->run(); // Veja aqui⑤
- }
- outro
- {
- ccDrawInit();
- ccGLInvalidateStateCache();
- CCShaderCache::sharedShaderCache()->reloadDefaultShaders();
- CCTextureCache::reloadAllTextures();
- CCNotificationCenter::sharedNotificationCenter()->postNotification(EVNET_COME_TO_FOREGROUND, NULL);
- CCDirector::sharedDirector()->setGLDefaultValues();
- }
- }
- }
De acordo com as regras de nomenclatura do Jni, o método nativeInit marcado com ② é a longa string vermelha acima (hehe). Depois que a janela é estabelecida, chamar o método nativeInit é chamar a implementação desse C++. É aqui que a inicialização da janela é feita.
Olhe para a marca ⑤, você acha que essa função de execução entrou no loop principal, hein?
assistir esta corrida
[cpp] ver cópia simples
- <span style="font-size:18px;"> int CCApplication::run()
- {
- // Inicializa a instância e cocos2d.
- if (! applicationDidFinishLaunching())
- {
- retorna 0;
- }
- retornar -1;
- }</span>
Nós vimos um cavalo! Em essência, applicationDidFinishLaunching foi ajustado e nada mais foi feito. Portanto, o loop principal não é inserido aqui.
Agora olhe para ③ e ④ novamente. Essa lógica parece ser o loop principal.
A implementação desta função nativeRender() está em Yourcocos2dDir/cocos2dx/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxRenderer.cpp
[cpp] ver cópia simples
- #include "text_input_node/CCIMEDispatcher.h"
- #include "CCDirector.h"
- #include "../CCApplication.h"
- #include "plataforma/CCFileUtils.h"
- #include "CCEventType.h"
- #include "support/CCNotificationCenter.h"
- #include "JniHelper.h"
- #include <jni.h>
- usando namespace cocos2d;
- externo "C" {
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_<span style="color:#ff0000;">Cocos2dxRenderer_nativeRender</span>(JNIEnv* env) {
- <span style="color:#ff0000;">cocos2d::CCDirector::sharedDirector()->mainLoop();//não vejo nada, o que é isso</span>
- }
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnPause() {
- CCApplication::sharedApplication()->applicationDidEnterBackground();
- CCNotificationCenter::sharedNotificationCenter()->postNotification(EVENT_COME_TO_BACKGROUND, NULL);
- }
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnResume() {
- if (CCDirector::sharedDirector()->getOpenGLView()) {
- CCApplication::sharedApplication()->applicationWillEnterForeground();
- }
- }
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInsertText(JNIEnv* env, jobject thiz, jstring text) {
- const char * pszText = env->GetStringUTFChars(texto, NULL);
- cocos2d::CCIMEDispatcher::sharedDispatcher()->dispatchInsertText(pszText, strlen(pszText));
- env->ReleaseStringUTFChars(texto, pszText);
- }
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeDeleteBackward(JNIEnv* env, jobject thiz) {
- cocos2d::CCIMEDispatcher::sharedDispatcher()->dispatchDeleteBackward();
- }
- JNIEXPORT jstring JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeGetContentText() {
- JNIEnv * env = 0;
- if (JniHelper::getJavaVM()->GetEnv(( void **)&env, JNI_VERSION_1_4) != JNI_OK || ! env) {
- retorna 0;
- }
- const char * pszText = cocos2d::CCIMEDispatcher::sharedDispatcher()->getContentText();
- return env->NewStringUTF(pszText);
- }
- }
Veja a anotação acima, encontrei o diretor e o diretor iniciou o loop principal novamente. É verdade que a multidão o procurou milhares de vezes, mas o homem estava em um local pouco iluminado.
Pode-se verificar aqui que o loop principal no Android não é o mesmo que no win, não é um simples tempo e acabou. Ele é iniciado pelo thread de renderização do java e conduzido pela chamada contínua de render.