Skip to content

Bring Godot to React Native ๐Ÿ”ฎ. Create immersive 3D experiences or interactive games directly within React Native.

Notifications You must be signed in to change notification settings

GACWR/react-native-godot

ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

34 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

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

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 [email protected] 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 ๐Ÿ‘จโ€โš–๏ธ

Copyright Calico Games 2024. All rights reserved.

This library is released under a Custom License:

  • โœ… Free for non-commercial use - Personal, educational, or open-source projects
  • ๐Ÿ’ผ Commercial use requires license - Companies/individuals with >$50,000 annual revenue need a commercial license
  • โŒ No redistribution - Cannot be redistributed, repackaged, or resold

We support the Godot Foundation by sharing revenue from commercial licenses.

For commercial licensing: [email protected]


Credits ๐Ÿ™

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

About

Bring Godot to React Native ๐Ÿ”ฎ. Create immersive 3D experiences or interactive games directly within React Native.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 66.0%
  • Java 9.4%
  • Ruby 7.3%
  • JavaScript 5.8%
  • Kotlin 4.0%
  • Swift 3.0%
  • Other 4.5%