Criando um jogo com React Native e Axmol Engine: desempenho C++ com interface moderna

Published on: 2025-10-11
Post image
pt reactnative axmol gamedev cplusplus android ios gameengine opensource javascript frontend backend uiux development 2dgame tutorial indiedev design performance coding

Este artigo apresenta um guia completo e autossuficiente para criar um jogo Android integrando React Native com o Axmol Engine. O objetivo é combinar a interface moderna em JavaScript com a performance nativa em C++ do motor de jogo, cobrindo todos os arquivos e configurações necessários.

O conteúdo descreve passo a passo a estrutura do projeto, arquivos Java/Kotlin e C++, scripts de build, configuração do NDK e do CMake, além do código-fonte do jogo simples com sprites e detecção de colisão. As instruções assumem ambiente de desenvolvimento onde ferramentas básicas já estão instaladas.

Visão geral da arquitetura e termos principais

O projeto combina duas camadas: a camada de UI em React Native, escrita em JavaScript, e a camada de renderização em C++ fornecida pelo Axmol Engine. O NDK (Native Development Kit) permite compilar código C++ para o Android. O CMake é o gerador de build que cria os artefatos nativos para integração com o Gradle. A ponte entre JavaScript e C++ passa por módulos nativos Java + JNI (Java Native Interface).

Pré-requisitos de software

A lista abaixo mostra o software mínimo e versões recomendadas para seguir o guia sem falhas.

  • Node.js 20.x ou superior e npm 10.x+
  • React Native CLI para criação do projeto
  • Android Studio 2024.3.2+ com NDK r25+ instalado
  • CMake 3.28.1+ instalado
  • Python 3.9+ para scripts de setup do Axmol
  • Git e um editor de código (por exemplo, VS Code)

Criação do projeto React Native

O trecho a seguir mostra os comandos para inicializar o projeto React Native. Estes comandos geram a estrutura básica de um app Android com suporte à ponte nativa.

# Instalar o CLI (se necessário)
npm install -g @react-native-community/cli

# Criar o projeto (exemplo com versão fixa)
npx @react-native-community/cli@latest init MyAxmolGame --version 0.75.4

# Acessar a pasta do projeto
cd MyAxmolGame

O arquivo package.json mínimo do projeto deve conter dependências essenciais para React Native e a definição do aplicativo. Abaixo está um exemplo reduzido, incluindo a dependência do módulo nativo que será exposto.

{
  "name": "MyAxmolGame",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android"
  },
  "dependencies": {
    "react": "18.2.0",
    "react-native": "0.75.4"
  }
}

Estrutura da interface React Native (App.js)

O arquivo App.js expõe um botão de controle e uma view nativa que receberá a renderização do Axmol. O trecho abaixo cria a interface principal e demonstra a chamada ao módulo nativo para iniciar o jogo.

import React, { useEffect, useState } from 'react';
import { View, Text, Button, StyleSheet, NativeModules, requireNativeComponent } from 'react-native';

const { AxmolModule } = NativeModules;
const AxmolView = requireNativeComponent('AxmolView');

export default function App() {
  const [score, setScore] = useState(0);

  const startGame = () => {
    AxmolModule.startGame();
    setScore(prev => prev + 1); // demonstração local de atualização
  };

  useEffect(() => {
    return () => {
      AxmolModule.stopGame && AxmolModule.stopGame();
    };
  }, []);

  return (


  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  title: { fontSize: 24, marginBottom: 20 },
  gameView: { width: 360, height: 640, marginBottom: 20 }
});

Clonagem e configuração do Axmol Engine

O repositório do Axmol Engine precisa ser clonado e inicializado para gerar as bibliotecas nativas usadas pelo aplicativo Android. Os comandos a seguir exemplificam o fluxo de preparação do motor.

# Clonar o Axmol (exemplo de branch/tag compatível)
git clone https://github.com/axmolengine/axmol.git
cd axmol

# Executar script de setup do Axmol (pode criar variáveis de ambiente)
python setup.py

# Instalar NDK r25 (script de exemplo presente no repositório)
./tools/external/install-ndk-r25.sh

O passo acima gera bibliotecas e ferramentas internas do Axmol. É importante manter o diretório do Axmol acessível ao projeto React Native para referências no CMake.

Estrutura de arquivos nativos Android a criar

A seguir aparecem os arquivos Java e C++ principais que compõem a integração nativa. Cada arquivo será fornecido com conteúdo completo para inclusão no diretório indicado do projeto Android.

Lista de arquivos e seus locais (explicação antes da listagem): a lista aponta os arquivos que devem existir em android/app/src/main e subpastas relacionadas.

  • android/app/src/main/java/com/myaxmolgame/AxmolModule.java
  • android/app/src/main/java/com/myaxmolgame/AxmolPackage.java
  • android/app/src/main/java/com/myaxmolgame/AxmolView.java
  • android/app/src/main/java/com/myaxmolgame/AxmolViewManager.java
  • android/app/src/main/java/com/myaxmolgame/AxmolRenderer.java
  • android/app/src/main/java/com/myaxmolgame/MainActivity.java (modificado)
  • android/app/src/main/cpp/CMakeLists.txt
  • android/app/src/main/cpp/native-lib.cpp
  • android/app/src/main/jniLibs/libaxmol.so (pré-compilado ou gerado pelo Axmol)
  • android/app/src/main/assets/player.png e obstacle.png

Código do módulo nativo Java: AxmolModule.java

O módulo nativo Java expõe métodos que serão chamados a partir do JavaScript. O código abaixo mostra a declaração do módulo e métodos de início/parada do motor via JNI.

// android/app/src/main/java/com/myaxmolgame/AxmolModule.java
package com.myaxmolgame;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

public class AxmolModule extends ReactContextBaseJavaModule {
    static {
        System.loadLibrary("native-lib"); // carrega a biblioteca nativa construída
    }

    public AxmolModule(ReactApplicationContext context) {
        super(context);
    }

    @Override
    public String getName() {
        return "AxmolModule";
    }

    @ReactMethod
    public void startGame() {
        // Chamada JNI para iniciar a cena Axmol
        nativeStartGame();
    }

    @ReactMethod
    public void stopGame() {
        // Chamada JNI para parar o motor Axmol
        nativeStopGame();
    }

    // Declarações JNI
    private native void nativeStartGame();
    private native void nativeStopGame();
}

Registro do pacote e view manager: AxmolPackage.java

O pacote registra módulos nativos e view managers para expor a view OpenGL ao React Native. O código abaixo adiciona AxmolModule e AxmolViewManager à lista de pacotes.

// android/app/src/main/java/com/myaxmolgame/AxmolPackage.java
package com.myaxmolgame;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class AxmolPackage implements ReactPackage {
    @Override
    public List createNativeModules(ReactApplicationContext reactContext) {
        List modules = new ArrayList<>();
        modules.add(new AxmolModule(reactContext));
        return modules;
    }

    @Override
    public List createViewManagers(ReactApplicationContext reactContext) {
        List managers = new ArrayList<>();
        managers.add(new AxmolViewManager());
        return managers;
    }
}

View nativa para renderização OpenGL: AxmolView.java e AxmolRenderer.java

A view personalizada cria um contexto OpenGL ES e delega a renderização ao motor nativo. O AxmolRenderer mantém pontos de entrada onde a camada nativa pode inicializar e desenhar cada frame.

// android/app/src/main/java/com/myaxmolgame/AxmolView.java
package com.myaxmolgame;

import android.content.Context;
import android.opengl.GLSurfaceView;

public class AxmolView extends GLSurfaceView {
    public AxmolView(Context context) {
        super(context);
        setEGLContextClientVersion(2); // OpenGL ES 2.0
        setRenderer(new AxmolRenderer());
        setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
    }
}
// android/app/src/main/java/com/myaxmolgame/AxmolRenderer.java
package com.myaxmolgame;

import android.opengl.GLSurfaceView;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.egl.EGLConfig;

public class AxmolRenderer implements GLSurfaceView.Renderer {
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // Chama inicialização nativa do Axmol
        nativeOnSurfaceCreated();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        nativeOnSurfaceChanged(width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        nativeOnDrawFrame();
    }

    private native void nativeOnSurfaceCreated();
    private native void nativeOnSurfaceChanged(int width, int height);
    private native void nativeOnDrawFrame();
}

View manager para expor AxmolView ao React Native

O view manager registra o nome da view que será referenciada em JavaScript por meio de requireNativeComponent. O código abaixo cria a instância da view nativa.

// android/app/src/main/java/com/myaxmolgame/AxmolViewManager.java
package com.myaxmolgame;

import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;

public class AxmolViewManager extends SimpleViewManager {
    @Override
    public String getName() {
        return "AxmolView";
    }

    @Override
    protected AxmolView createViewInstance(ThemedReactContext reactContext) {
        return new AxmolView(reactContext);
    }
}

Integração na MainActivity e registro de pacote

O registro do pacote nativo é necessário para que o React Native carregue AxmolPackage. O trecho abaixo demonstra a inclusão no método de obtenção de pacotes do aplicativo Android.

// android/app/src/main/java/com/myaxmolgame/MainActivity.java (trecho para registro)
import com.myaxmolgame.AxmolPackage;

@Override
protected List getPackages() {
    List packages = new PackageList(this).getPackages();
    packages.add(new AxmolPackage());
    return packages;
}

Arquivo CMakeLists.txt para build nativo

O CMakeLists define como o código nativo será ligado à biblioteca Axmol pré-compilada e como será gerado o artefato nativo. O exemplo abaixo assume que libaxmol.so está em jniLibs.

cmake_minimum_required(VERSION 3.28.1)
project(MyAxmolGame)

# Diretório base do projeto nativo
set(CMAKE_CXX_STANDARD 17)
include_directories(${CMAKE_SOURCE_DIR}/../../../../axmol/core)

# Considera que libaxmol.so foi colocada em jniLibs/$(ANDROID_ABI)/
add_library(axmol SHARED IMPORTED)
set_target_properties(axmol PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libaxmol.so)

add_library(native-lib SHARED native-lib.cpp)
find_library(log-lib log)

target_link_libraries(native-lib axmol ${log-lib})

Código C++ nativo: native-lib.cpp e lógica do jogo

O código nativo inicializa o Axmol, cria uma cena simples com dois sprites e implementa movimento e colisão. Comentários em português explicam cada parte essencial.

// android/app/src/main/cpp/native-lib.cpp
#include 
#include 
#include 

// Usando namespace do Axmol
USING_NS_AX;

static Scene* g_scene = nullptr;
static Sprite* player = nullptr;
static Sprite* obstacle = nullptr;

// Classe de cena com lógica básica
class GameScene : public Scene {
public:
    static Scene* createScene() {
        return GameScene::create();
    }

    bool init() override {
        if (!Scene::init()) return false;

        auto visibleSize = Director::getInstance()->getVisibleSize();
        Vec2 origin = Director::getInstance()->getVisibleOrigin();

        // Adiciona caminho de recursos
        FileUtils::getInstance()->addSearchPath("assets");

        // Cria jogador
        player = Sprite::create("player.png");
        player->setPosition(visibleSize.width / 4, visibleSize.height / 2);
        this->addChild(player);

        // Cria obstáculo
        obstacle = Sprite::create("obstacle.png");
        obstacle->setPosition(visibleSize.width * 3 / 4, visibleSize.height / 2);
        this->addChild(obstacle);

        // Agendar update para lógica de movimento
        scheduleUpdate();

        return true;
    }

    void update(float dt) override {
        auto pos = player->getPosition();
        pos.x += 150.0f * dt; // movimento contínuo para a direita
        if (pos.x > Director::getInstance()->getVisibleSize().width) {
            pos.x = 0; // reinicia posição no limite
        }
        player->setPosition(pos);

        // Verificação simples de colisão por bounding box
        if (player->getBoundingBox().intersectsRect(obstacle->getBoundingBox())) {
            log("Colisão detectada!");
        }
    }
};

extern "C" JNIEXPORT void JNICALL
Java_com_myaxmolgame_AxmolModule_nativeStartGame(JNIEnv* env, jobject thiz) {
    // Inicializa diretor e executa cena
    auto director = Director::getInstance();

    if (!g_scene) {
        g_scene = GameScene::createScene();
    }

    if (!director->getRunningScene()) {
        director->runWithScene(g_scene);
    } else {
        director->replaceScene(g_scene);
    }
}

extern "C" JNIEXPORT void JNICALL
Java_com_myaxmolgame_AxmolModule_nativeStopGame(JNIEnv* env, jobject thiz) {
    auto director = Director::getInstance();
    director->end();
}

extern "C" JNIEXPORT void JNICALL
Java_com_myaxmolgame_AxmolRenderer_nativeOnSurfaceCreated(JNIEnv* env, jobject thiz) {
    // Inicialização gráfica do Axmol, se necessário
}

extern "C" JNIEXPORT void JNICALL
Java_com_myaxmolgame_AxmolRenderer_nativeOnSurfaceChanged(JNIEnv* env, jobject thiz, jint width, jint height) {
    // Atualizar viewport ou estados dependentes de resolução
}

extern "C" JNIEXPORT void JNICALL
Java_com_myaxmolgame_AxmolRenderer_nativeOnDrawFrame(JNIEnv* env, jobject thiz) {
    // Este método pode permanecer vazio se Axmol fizer loop interno
}

Recursos e pastas de assets

As imagens do jogador e do obstáculo devem ficar em android/app/src/main/assets/ para serem carregadas pelo Axmol usando FileUtils. Abaixo está a estrutura de pastas relevante.

  • android/app/src/main/assets/player.png
  • android/app/src/main/assets/obstacle.png
  • android/app/src/main/jniLibs/armeabi-v7a/libaxmol.so (exemplo ABI)

Alterações no android/app/build.gradle

O arquivo build.gradle do módulo app deve declarar a integração com o CMake e versões do NDK. O trecho abaixo mostra a configuração mínima necessária para compilar o código nativo.

android {
    compileSdkVersion 35

    defaultConfig {
        applicationId "com.myaxmolgame"
        minSdkVersion 21
        targetSdkVersion 35
        versionCode 1
        versionName "1.0"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++17 -frtti -fexceptions"
            }
        }
    }

    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.28.1"
        }
    }

    ndkVersion "25.2.9519653"
}

Arquivo .axproj e alinhamento com Axmol

O arquivo .axproj contém parâmetros que ajudam a ferramenta do Axmol a gerar projetos compatíveis. Abaixo segue um exemplo que deve ficar na raiz do projeto React Native.

# .axproj
packageName=com.myaxmolgame
minSdk=21
targetSdk=35
ndkVer=25.2.9519653
cmakeVer=3.28.1
cmakeOptions=[]

Comandos de build e execução para Android

Os comandos a seguir exemplificam a ordem de compilação e deploy do app Android. A execução pode ser feita em emulador ou dispositivo físico com depuração habilitada.

# Construir e instalar o app via React Native CLI
npx react-native run-android

# Em caso de problemas com build nativo, limpar build e tentar novamente
cd android
./gradlew clean
cd ..
npx react-native run-android

Dicas de depuração e resolução de problemas

Erros comuns incluem incompatibilidades de versão do NDK, paths incorretos para libaxmol.so e problemas no CMake. Mensagens do Gradle e do logcat ajudam a identificar bibliotecas ausentes ou símbolos JNI não encontrados. Verificar se System.loadLibrary utiliza o mesmo nome do artefato gerado evita falhas de carregamento.

Cuidados com performance e gerenciamento de recursos

Manter a renderização do Axmol em uma view separada ajuda a isolar a GPU e reduzir conflitos com componentes React Native. Evitar alocações pesadas no loop de render reduz variações no frame rate. Encerrar corretamente o diretor do Axmol no método stopGame evita vazamentos de memória ao sair da atividade.

Resumo dos arquivos entregues neste guia

Foram apresentados os arquivos Java, C++ e de configuração essenciais para executar o jogo Android integrado com React Native e Axmol Engine. A lista inclui App.js, package.json exemplo, todos os arquivos Java nativos, CMakeLists.txt, native-lib.cpp, .axproj e instruções de build e assets. Com essas peças reunidas, o projeto contém tudo o que é necessário para compilar e executar a versão Android do jogo simples descrito neste documento.