Skip to main content

Building a Game with React Native and Axmol Engine

· 11 min read
Engineering Team

Combine React Native product UI with an Axmol-powered native game surface only after validating the native view bridge and lifecycle on both platforms. This revised guide emphasizes supported APIs, production tradeoffs, and an upgrade-friendly path without tying the advice to a calendar year.

Building a Game with React Native and Axmol Engine

Modern implementation baseline

Combine React Native product UI with an Axmol-powered native game surface only after validating the native view bridge and lifecycle on both platforms.

Treat React Native as the product shell and the game engine as a native rendering subsystem with a narrow bridge. Validate engine support, rendering ownership, lifecycle, input, audio, memory, and store packaging before committing to the architecture.

  • Keep engine setup and teardown deterministic.
  • Define a small typed command and event protocol.
  • Measure release frame pacing and memory before feature expansion.

Production checklist

  • Prototype the native view boundary before building gameplay.
  • Profile frame pacing, thermal behavior, memory, and resume flows.
  • Keep gameplay state independent from React component state.

Authoritative references


Why Use React Native with Axmol Engine?

React Native is excellent for building cross-platform mobile apps with a single codebase, leveraging JavaScript and React for UI development. Axmol Engine, a modern fork of Cocos2d-x, excels in high-performance 2D game development with C++ and supports platforms like iOS, Android, Windows, macOS, and more. By integrating these two, you can create a game with a React Native frontend for UI and Axmol for game logic and rendering, offering a balance of ease and performance.

This guide assumes you have basic knowledge of JavaScript and some familiarity with React Native. No prior C++ experience is required, but we'll touch on basic integration steps. We'll build a simple 2D game where a player moves a sprite to avoid obstacles, using modern tools and packages.

Note: This guide focuses on Android integration for simplicity. iOS integration with Axmol requires Objective-C++ (.mm) bridging and Metal support. For iOS-specific steps, refer to Axmol’s official iOS integration guide.


Prerequisites

Before we start, ensure you have the following installed:

  • Node.js (v20.x or later, LTS recommended): Download from nodejs.org.
  • npm (v10.x or later, comes with Node.js).
  • React Native CLI: For setting up the project.
  • CMake (v3.28.1+): Required for Axmol builds.
  • Android Studio: Use the current stable release with compatible Android SDK and NDK tooling.
  • Xcode (15+): For iOS development (macOS only).
  • Python (3.9+): For Axmol setup scripts.
  • Git: For cloning repositories.
  • A code editor like VS Code.

Step-by-Step Guide to Building the Game

Step 1: Set Up the React Native Project

  1. Install React Native CLI: Run the following command to install the React Native CLI globally:

    npm install -g @react-native-community/cli
  2. Create a New React Native Project: Initialize a new project called MyAxmolGame:

    npx @react-native-community/cli@latest init MyAxmolGame --version 0.75.4

    Use an active stable React Native release that is compatible with the native engine integration. The New Architecture is the standard baseline, but compatibility still needs to be proven with a release-build prototype.

  3. Navigate to the Project Directory:

    cd MyAxmolGame
  4. Test the Project: Run the app to ensure the setup works:

    npx react-native run-android

    or, for iOS (on macOS):

    npx react-native run-ios

    This should launch a basic React Native app on your emulator or device.


Step 2: Install and Configure Axmol Engine

  1. Clone the Axmol Engine Repository: Axmol is a C++-based engine, so we need to set it up for native integration. Clone the latest version (v2.3.0+):

    git clone https://github.com/axmolengine/axmol.git

    This clones the Axmol repository, which is actively maintained with updates as recent.

  2. Set Up Axmol Environment: Navigate to the Axmol directory and run the setup script:

    cd axmol
    python setup.py

    After completion, restart your terminal to apply environment variables.

  3. Install CMake and NDK for Android:

    • CMake: Ensure CMake 3.28.1+ is installed. On macOS, use:
      sudo "/Applications/CMake.app/Contents/bin/cmake-gui" --install
    • NDK: Axmol v2.3.0 requires NDK r25+. Install it manually since Android Studio’s SDK Manager may not list it:
      cd axmol
      ./tools/external/install-ndk-r25.sh
      Alternatively, set the NDK version in the .axproj file (created later).
  4. Verify Axmol Installation: Create a test Axmol project to ensure the engine is set up correctly:

    axmol new TestAxmol -p com.example.testaxmol -l cpp
    cd TestAxmol
    axmol build -p android

    This generates and builds an Android project. If successful, Axmol is ready.


Step 3: Integrate Axmol with React Native

To combine Axmol’s rendering with React Native’s UI, we’ll use a native module to bridge the C++ game logic with the JavaScript frontend. React Native and Axmol render in separate layers: Axmol renders via OpenGL in its own surface (GLSurfaceView), while React Native renders native views. You can place buttons, scores, and overlays using standard React Native components above or below the Axmol surface.

  1. Create a Native Module: In your React Native project, create a native module to interface with Axmol. Create the following files in MyAxmolGame/android/app/src/main/java/com/myaxmolgame/:

    • AxmolModule.java
    • AxmolPackage.java
    // 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 {
    AxmolModule(ReactApplicationContext context) {
    super(context);
    }

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

    @ReactMethod
    public void startGame() {
    // Placeholder for starting Axmol game loop
    // Will be linked to C++ code
    }
    }
    // 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<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
    List<NativeModule> modules = new ArrayList<>();
    modules.add(new AxmolModule(reactContext));
    return modules;
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
    }
    }
  2. Register the Native Module: Update MainActivity.java to include the package:

    // android/app/src/main/java/com/myaxmolgame/MainActivity.java
    import com.myaxmolgame.AxmolPackage;

    @Override
    protected List<ReactPackage> getPackages() {
    @SuppressWarnings("UnnecessaryLocalVariable")
    List<ReactPackage> packages = new PackageList(this).getPackages();
    packages.add(new AxmolPackage());
    return packages;
    }
  3. Link Axmol to the Native Module:

    • Copy the axmol folder to MyAxmolGame/android/app/src/main/jniLibs/.
    • Create a CMakeLists.txt in MyAxmolGame/android/app/src/main/cpp/:
      cmake_minimum_required(VERSION 3.28.1)
      project(MyAxmolGame)

      include_directories(${CMAKE_SOURCE_DIR}/../../../../axmol/core)
      add_library(axmol SHARED IMPORTED)
      set_target_properties(axmol PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/libaxmol.so)

      add_library(native-lib SHARED native-lib.cpp)
      target_link_libraries(native-lib axmol)
    • Create native-lib.cpp in the same directory:
      #include <axmol.h>
      USING_NS_AX;

      extern "C" JNIEXPORT void JNICALL Java_com_myaxmolgame_AxmolModule_startGame(JNIEnv* env, jobject thiz) {
      auto director = Director::getInstance();
      auto scene = Scene::create();
      auto sprite = Sprite::create("player.png");
      sprite->setPosition(Vec2(100, 100));
      scene->addChild(sprite);
      director->runWithScene(scene);
      }
  4. Update Android Build Configuration:

    • In android/app/build.gradle, add:
      android {
      // Existing config
      externalNativeBuild {
      cmake {
      path "src/main/cpp/CMakeLists.txt"
      }
      }
      }
    • Create a .axproj file in the project root with:
      packageName=com.myaxmolgame
      minSdk=21
      targetSdk=35
      ndkVer=25.2.9519653
      cmakeVer=3.28.1
      cmakeOptions=[]
      This aligns with Axmol requirements for Android 16 KB page alignment and NDK r25+.

Step 4: Create the Game Logic

  1. Set Up Game Assets:

    • Place a player.png sprite in MyAxmolGame/assets/. This will be the player’s character.
    • Ensure Axmol can load it by updating the resource path in native-lib.cpp:
      FileUtils::getInstance()->addSearchPath("assets");
  2. Implement Basic Game Logic: Update native-lib.cpp to add movement and collision detection:

    #include <axmol.h>
    USING_NS_AX;

    class GameScene : public Scene {
    public:
    static Scene* createScene() {
    return GameScene::create();
    }

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

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

    // Add player sprite
    player = Sprite::create("player.png");
    player->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2));
    this->addChild(player);

    // Add obstacle
    obstacle = Sprite::create("obstacle.png");
    obstacle->setPosition(Vec2(visibleSize.width - 100, visibleSize.height / 2));
    this->addChild(obstacle);

    // Schedule updates
    scheduleUpdate();

    return true;
    }

    void update(float dt) override {
    auto pos = player->getPosition();
    pos.x += 100 * dt; // Move right
    if (pos.x > Director::getInstance()->getVisibleSize().width) {
    pos.x = 0; // Reset position
    }
    player->setPosition(pos);

    // Basic collision detection
    if (player->getBoundingBox().intersectsRect(obstacle->getBoundingBox())) {
    log("Collision detected!");
    }
    }

    private:
    Sprite* player;
    Sprite* obstacle;
    };

    extern "C" JNIEXPORT void JNICALL Java_com_myaxmolgame_AxmolModule_startGame(JNIEnv* env, jobject thiz) {
    auto director = Director::getInstance();
    auto scene = GameScene::createScene();
    director->runWithScene(scene);
    }
  3. Add Obstacle Asset: Place an obstacle.png in MyAxmolGame/assets/. This represents an obstacle the player must avoid.


Step 5: Build the React Native UI

  1. Create a Game UI: Update App.js to include a button to start the game, display the score, and render the Axmol view:

    import React, { useState, useEffect } from 'react';
    import { View, Text, Button, StyleSheet } from 'react-native';
    import { 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();
    // Simulate score update (in a real game, this would come from Axmol)
    setScore(score + 1);
    };

    useEffect(() => {
    return () => {
    // Clean up Axmol engine on unmount
    AxmolModule.stopGame && AxmolModule.stopGame();
    };
    }, []);

    return (
    <View style={styles.container}>
    <Text style={styles.title}>My Axmol Game</Text>
    <AxmolView style={styles.gameView} />
    <Text>Score: {score}</Text>
    <Button title="Start Game" onPress={startGame} />
    </View>
    );
    }

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

    Note: The score is simulated here. In production, use the native bridge (e.g., a callback from Axmol C++ to JS using RCTDeviceEventEmitter) to send game state to the UI.

  2. Add a Game View: To render Axmol’s OpenGL context, create a custom view. In MyAxmolGame/android/app/src/main/java/com/myaxmolgame/, add:

    // 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 for Axmol
    setRenderer(new AxmolRenderer());
    }
    }
    // AxmolRenderer.java
    package com.myaxmolgame;

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

    public class AxmolRenderer implements GLSurfaceView.Renderer {
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    // Initialize Axmol
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
    // Update viewport
    }

    @Override
    public void onDrawFrame(GL10 gl) {
    // Render Axmol frame
    }
    }

    To expose AxmolView to React Native, create AxmolViewManager.java:

    // AxmolViewManager.java
    package com.myaxmolgame;

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

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

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

    Update AxmolPackage.java to include the view manager:

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

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

    Note: To avoid memory leaks, ensure to stop the Axmol engine properly when the component is unmounted or the app exits. Add a stopGame method to AxmolModule.java and call it in the useEffect cleanup function in App.js.


Step 6: Build and Test the Game

  1. Build for Android:

    cd MyAxmolGame
    npx react-native run-android

    Ensure your Android emulator or device is running with API level 21+.

  2. Build for iOS (macOS only):

    npx react-native run-ios

    Use Xcode to configure the .xcodeproj generated by Axmol if needed, following Axmol’s iOS guide.

  3. Test the Game:

    • Launch the app, press “Start Game,” and verify the player sprite moves and detects collisions with the obstacle in the AxmolView.
    • The score updates in the React Native UI (simulated for now).

Step 7: Enhance the Game

  • Add Input Handling: Use React Native’s PanResponder to capture touch events and pass them to Axmol via the native module to control the player sprite.
  • Improve Collision Detection: Use Axmol’s built-in physics engine (e.g., Chipmunk) for more accurate collisions.
  • Add Animations: Use Axmol’s Animation class to animate sprites.
  • Polish the UI: Use Tailwind CSS (via react-native-tailwindcss) for styling the React Native UI.

Troubleshooting Tips

  • Build Errors: Ensure CMake, NDK, and Xcode versions match the requirements. Check the .axproj file for correct configurations.
  • Axmol Setup Issues: If you encounter path errors, wrap paths with spaces in single quotes or move to a directory without spaces.
  • Performance: Optimize sprite rendering by using Axmol’s sprite batching and ensure React Native’s New Architecture is enabled.

Conclusion

By combining React Native for the UI and Axmol Engine for game logic, you’ve built a simple cross-platform 2D game that’s performant and extensible. This setup leverages React Native’s ease of use and Axmol’s high-performance rendering, making it ideal for indie developers. Explore Axmol’s documentation for advanced features like physics and particle effects, and enhance your React Native UI with modern libraries like Tailwind CSS.

For further help, join the Axmol community on GitHub or Discord, and check React Native’s official docs at reactnative.dev.

Additional Resources