TypeScript MIT

react-native-godot

Bring Godot to React Native 🔮. Create immersive 3D experiences or interactive games directly within React Native.

C

calico-games

Dernière activité 3 nov. 2025
calico-games/react-native-godot

579

étoiles

16

forks

3

issues ouvertes

3dandroidgltfgodotgodot-enginegodotengineiosmetalopenglreact-nativereact-native-godotrenderingvulkan

Ce README est souvent en anglais.

react-native-godot

React Native Godot

Bring Godot to React Native 🔮. Create immersive 3D experiences or interactive games directly within React Native.

npm version godot engine Expo SDK

Table of Contents

Screenshots 📸

Multiple Cubes demo Earth demo

Features 🚀

  • 🏎️ Native C++ JSI performance - Direct JavaScript to native bindings
  • 🔥 GPU-accelerated rendering - Metal (iOS) and OpenGL/Vulkan support
  • ✅ Full React Native compatibility - Supports old and new architecture
  • 🎮 Complete Godot integration - Access all Godot variants and features
  • 🧙‍♂️ Runtime GDScript compilation - Create and execute scripts dynamically
  • 📦 Easy project import - Simple workflow to bring Godot projects to RN
  • 🔄 Bidirectional communication - React Native ↔ Godot messaging

Device Support 📱

iOS support is implemented, full Android support is coming soon.

Platform Supported
iOS Device ✅
iOS Simulator ❌
Android Device 🚧
Android Emulator 🚧

Requirements 🥸

Installation 🚀

npm install react-native-godot
# or
yarn add react-native-godot

Quick Start 👇

1. Setup GodotProvider

Wrap your app with GodotProvider to initialize Godot properly:

// App.tsx
import React from 'react';
import { GodotProvider } from 'react-native-godot';
import MyGameScreen from './MyGameScreen';

export default function App() {
  return (
    <GodotProvider>
      <MyGameScreen />
    </GodotProvider>
  );
}

2. Create your game component

// MyGameScreen.tsx
import React, { useEffect, useState } from 'react';
import { GodotView, useGodot, useGodotRef } from 'react-native-godot';

const MyGameScreen = () => {
  const godotRef = useGodotRef();
  const { Vector3, Vector2 } = useGodot();
  const [isGodotReady, setIsGodotReady] = useState(false);

  useEffect(() => {
    // Start Godot rendering (call once in your app)
    GodotView.startDrawing();

    return () => {
      // Stop Godot rendering when component unmounts
      GodotView.stopDrawing();
    };
  }, []);

  useEffect(() => {
    if (!isGodotReady || !godotRef.current) {
      return;
    }

    // Use Godot variants
    const position = Vector3(1, 2, 3);
    console.log('Position Y:', position.y);

    // Get nodes from your scene
    const playerNode = godotRef.current.getRoot()?.getNode('Player');
    playerNode?.call('jump', 10);

    // Send data to Godot
    godotRef.current.emitMessage({
      type: 'player_spawn',
      position: position,
      health: 100
    });

  }, [isGodotReady]);

  return (
    <GodotView
      ref={godotRef}
      style={{ flex: 1 }}
      source={require('./assets/game.pck')}
      scene="res://main.tscn"
      onReady={() => setIsGodotReady(true)}
      onMessage={(instance, message) => {
        console.log('Message from Godot:', message);
      }}
    />
  );
};

export default MyGameScreen;

API Reference

GodotView Component

The main component for embedding Godot scenes in React Native. You can use multiple GodotView components on the same screen - each will render a different scene but they all share the same Godot engine instance.

Props

Prop Type Description
source string | ImageSourcePropType Path to your .pck file
scene string Scene path (e.g., "res://main.tscn")
style StyleProp<ViewStyle> React Native style object
onReady (instance: GodotViewRef) => void Called when Godot is ready
onMessage (instance: GodotViewRef, message: any) => void Called when receiving messages from Godot

Instance Methods (via ref)

Method Description
getRoot(): Node Get the root node of the loaded scene
emitMessage(message: any): void Send a message to Godot scripts
pause(): void Pause the Godot instance
resume(): void Resume the Godot instance
isReady(): boolean Check if Godot is ready

Static Methods

Method Description
GodotView.startDrawing(): void Start Godot rendering engine (call once per app)
GodotView.stopDrawing(): void Stop Godot rendering engine

Single GodotView Example:

const MyGame = () => {
  const godotRef = useGodotRef();

  useEffect(() => {
    // Start rendering when app launches
    GodotView.startDrawing();
    return () => GodotView.stopDrawing();
  }, []);

  return (
    <GodotView
      ref={godotRef}
      source={require('./game.pck')}
      scene="res://main.tscn"
      onReady={(instance) => {
        console.log('Godot ready!');
        instance.emitMessage({ type: 'game_start' });
      }}
      onMessage={(instance, message) => {
        console.log('From Godot:', message);
      }}
    />
  );
};

Multiple GodotView Example:

const MultiSceneApp = () => {
  const gameRef = useGodotRef();
  const uiRef = useGodotRef();
  const minimapRef = useGodotRef();

  useEffect(() => {
    // Start rendering once for all GodotView instances
    GodotView.startDrawing();
    return () => GodotView.stopDrawing();
  }, []);

  return (
    <View style={{ flex: 1 }}>
      {/* Main game view */}
      <GodotView
        ref={gameRef}
        source={require('./game.pck')}
        scene="res://game_world.tscn"
        style={{ flex: 1 }}
      />
      
      {/* UI overlay */}
      <GodotView
        ref={uiRef}
        source={require('./ui.pck')}
        scene="res://hud.tscn"
        style={{ 
          position: 'absolute',
          top: 0,
          left: 0,
          width: '100%',
          height: 100 
        }}
      />
      
      {/* Minimap */}
      <GodotView
        ref={minimapRef}
        source={require('./game.pck')}
        scene="res://minimap.tscn"
        style={{ 
          position: 'absolute',
          top: 20,
          right: 20,
          width: 150,
          height: 150 
        }}
      />
    </View>
  );
};

💡 Note: All GodotView instances share the same Godot engine, so you only need to call GodotView.startDrawing() once per app, regardless of how many views you have. Use pause() and resume() on individual view instances to control which scenes are actively rendering.

Example: Controlling individual scenes:

// Pause the minimap when not needed
minimapRef.current?.pause();

// Resume it later
minimapRef.current?.resume();

// Pause game but keep UI active
gameRef.current?.pause();
// UI continues running for menus, etc.

Godot Variants 🍭

All Godot variant types are available with full method and property support:

Available Types: AABB | Basis | Color | Plane | Projection | Quaternion | Rect2 | Rect2i | Transform2D | Transform3D | Vector2 | Vector2i | Vector3 | Vector3i | Vector4 | Vector4i

Usage:

const { Vector3, Color, Transform3D } = useGodot();

// Create variants
const position = Vector3(1, 2, 3);
const color = Color(1, 0, 0, 1); // Red
const transform = Transform3D();

// Use methods and properties
console.log('Distance:', position.length());
console.log('Normalized:', position.normalized());
console.log('Red component:', color.r);

Complete documentation: Godot Variant Types

Runtime GDScript & Node Creation 🧙‍♂️

Create and compile GDScript at runtime, then attach to dynamically created nodes:

const { Script, Node } = useGodot();

// Create and compile a script
const script = Script();
const success = script.setSourceCode(`
extends Node

@onready var health = 100

func _ready():
    print("Dynamic script loaded!")

func take_damage(amount: int) -> int:
    health -= amount
    return health

func heal(amount: int):
    health += amount
    print("Healed for ", amount, " HP")
`);

if (success) {
  // Create node and attach script
  const dynamicNode = Node();
  dynamicNode.setScript(script);
  dynamicNode.setName("DynamicPlayer");
  
  // Add to scene
  godotRef.current?.getRoot()?.addChild(dynamicNode);
  
  // Call script methods
  const remainingHealth = dynamicNode.call("take_damage", 25);
  console.log('Health remaining:', remainingHealth);
  
  // Alternative syntax with TypeScript casting
  (dynamicNode as any).heal(10);
}

Script API

Method Description
Script() Create a new empty script
setSourceCode(source: string): boolean Set and compile GDScript source code

Node API

Method Description
Node() Create a new empty node
getNode(path: string): Node | null Get child node by path
getParent(): Node | null Get parent node
getChildren(): Node[] Get all child nodes
getChildCount(): number Get number of child nodes
addChild(child: Node) Add a child node
setName(name: string) Set the node's name
setScript(script: Script) Attach a script to the node
call(method: string, ...args: any[]): any Call a method defined in the attached script

💡 Tip: For better TypeScript ergonomics, you can call script methods directly using (node as any).methodName(args) instead of node.call("methodName", args).

Scene Node Access 🎯

Access and interact with nodes from your loaded Godot scenes:

useEffect(() => {
  if (!isGodotReady || !godotRef.current) return;

  // Get the root node
  const root = godotRef.current.getRoot();
  
  // Navigate the scene tree
  const player = root?.getNode('Player');
  const ui = root?.getNode('UI/HealthBar');
  
  // Access node hierarchy
  const parent = player?.getParent();
  const children = player?.getChildren();
  const siblingCount = parent?.getChildCount();
  
  // Call methods defined in the node's GDScript
  player?.call('set_health', 100);
  ui?.call('update_display', 100, 100);
  
  // Alternative direct method calls
  (player as any)?.jump(15);
  (ui as any)?.show_damage_effect();
  
}, [isGodotReady]);

React Native ↔ Godot Communication 📡

React Native → Godot

Send messages from React Native to your Godot scripts:

// Send structured data to Godot
godotRef.current?.emitMessage({
  type: 'player_action',
  action: 'attack',
  target: 'enemy_1',
  position: Vector3(10, 0, 5),
  damage: 50
});

Godot → React Native

Receive messages in React Native from Godot scripts:

<GodotView
  onMessage={(instance, message) => {
    console.log('Received from Godot:', message);
    
    // Handle different message types
    switch (message.type) {
      case 'game_over':
        showGameOverScreen(message.score);
        break;
      case 'level_complete':
        advanceToNextLevel();
        break;
      case 'item_collected':
        updateInventory(message.item);
        break;
    }
  }}
/>

Godot Script Implementation

extends Node

@onready var rn_singleton = Engine.get_singleton("ReactNative")

func _ready():
    if rn_singleton:
        # Listen for messages from React Native
        rn_singleton.on_message(_on_react_native_message)

func _on_react_native_message(message: Dictionary):
    print("Message from React Native: ", message)
    
    match message.type:
        "player_action":
            handle_player_action(message)
        "game_state_change":
            update_game_state(message.state)

func send_to_react_native(data: Dictionary):
    if rn_singleton:
        rn_singleton.emit_message(data)

func _on_enemy_defeated():
    send_to_react_native({
        "type": "enemy_defeated",
        "enemy_id": "goblin_1",
        "exp_gained": 50
    })

Project Setup

Importing Godot Projects 📥

To use your existing Godot project in React Native:

  1. Add export preset configuration

Create export_presets.cfg in your Godot project directory:

[preset.0]

name="main"
platform="iOS"
runnable=true
advanced_options=false
dedicated_server=false
custom_features=""
export_filter=""
include_filter="project.godot"
exclude_filter=""
export_path=""
encryption_include_filters=""
encryption_exclude_filters=""
encrypt_pck=false
encrypt_directory=false
script_export_mode=2

[preset.0.options]

export/distribution_type=1
binary_format/architecture="universal"
binary_format/embed_pck=false
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=0
display/high_res=true
  1. Generate PCK file

Run the provided script (modify path for your OS):

./gen-pck PROJECT_FOLDER_PATH
  1. Add to React Native project

Move the generated .pck file to your React Native assets folder.

  1. Include project.godot in iOS

Add your project.godot file to your Xcode project bundle.

Metro Configuration 🚇

Add PCK file support to your metro.config.js:

const config = getDefaultConfig(__dirname);

// Add pck files as assets
config.resolver.assetExts.push('pck');

module.exports = config;

Limitations & Known Issues 🚧

Texture Import Settings

When importing textures or 3D models, avoid using VRAM Compressed format as it may not export properly in PCK files.

VRAM Compressed

PCK Asset Swapping

Currently, you cannot swap PCK assets at runtime. You need to restart the app to load a new PCK file. This appears to be a Godot engine limitation that we're investigating.

Platform Support

  • iOS Simulator is not supported due to architecture differences
  • Android support is in development

Contributing 🤝

We welcome contributions! The core development happens in a private repository, but if you'd like to contribute:

  1. Open an issue to discuss your idea
  2. Contact us at team@calico.games for access to the private repo
  3. Experience with Godot Engine, C++, and React Native is preferred
  4. Knowledge of Bazel is a plus

License 👨‍⚖️

MIT License. See LICENSE for details.


Credits 🙏

  • Special thanks to the Godot Engine contributors
  • Huge appreciation to Migeran for their invaluable help

Projets similaires

React Native Godot - Embed Godot Engine in React Native apps

C++androidgodotgodot-engine
Bborndotcom
2,7 k étoiles131

Godot Engine – Multi-platform 2D and 3D game engine

C++game-developmentgame-enginegamedev
Ggodotengine
117,9 k étoiles26,9 k

🐧 A real-time physically based 3D rendering engine for React Native

C++3dandroidengine
Mmargelo
1,4 k étoiles61