Declarative 'react-babylonjs'
This page demonstrates how to create a truly reactive Babylon.js app. We'll
learn how to make custom Babylon React components, and how to use some helpful
hooks like useScene
, useBeforeRender
.
Creating a Basic Scene
Let's take a look at a basic react-babylonjs
scene using the Engine
and
Scene
components of react-bablylonjs
. These will connect and create an HTML
canvas, Engine and Scene with a render loop. Everything you might like to add to
a scene can be added declaratively in the Scene
. Note we can also pass in
props like below with antialias
/antialias={true}
to the Babylon.js Engine.
import { Engine, Scene } from 'react-babylonjs'const BabylonApp: FC = ({ children }) => (<div style={{ flex: 1, display: 'flex' }}><Engine antialias adaptToDeviceRatio canvasId="babylon-canvas"><Scene>{children}</Scene></Engine></div>)
If you wanted to re-use some components you could flow down your scene graph as shown below. The React concept composition will be demonstrated further in these guides in a Babylon context.
import { FC } from 'react'import { Engine, Scene } from 'react-babylonjs'const BasicScene: FC = ({ children }) => (<Engine antialias adaptToDeviceRatio canvasId="babylon-canvas"><Scene>{children}</Scene></Engine>)const BabylonApp: FC = ({ children }) => (<div style={{ flex: 1, display: 'flex' }}><BasicScene>{children}</BasicScene></div>)
Adding light, camera, and surface
The scene above is empty and won't display anything. To see something, we need to add light, a camera, and something to look at. Let's do that now.
As we go along, it's important to understand a concept known as
intrinsic JSX.
Renderers treat them as Host Elements
as opposed to React Components, much
like the react-dom
renderer would for HTML elements like div
or span
. We
had to explicitly import Engine
and Scene
, but there are many Babylon.js
elements that will be understood by React thanks to this library. We will use
<freeCamera ... />
, <hemisphericLight ... />
, and <ground ... />
. Those
are all intrinsic, meaning we don't need an import
statement to use them.
They are understood by your IDE and include intellisense and compile time
warnings. In a Babylon.js application it means you can add your cameras, lights,
etc. in that familiar declarative way to your scene:
<Scene><freeCameraname="camera1"position={new Vector3(0, 5, -10)}setTarget={[Vector3.Zero()]}/><hemisphericLightname="light1"intensity={0.7}direction={new Vector3(0, 1, 0)}/><ground name="ground" width={6} height={6} /></Scene>
Click the "code" tab to see the code and you can toggle between TypeScript or JavaScript.
- Output
- Code
Adding an object to the scene
Before we move on, let's add a standard box to the scene and see how the props work.
If we create a box in Babylon.js imperatively you could call CreateBox:
// doesn't need to be MeshBuilder - there are creation methods that can be imported directly.import { MeshBuilder } from '@babylonjs/core'// NOTE: you need to pass in `scene` object yourself.const box = MeshBuilder.CreateBox('box', { size: 2 }, scene);box.position = new Vector3(0, 1, 0);box.rotation = Vector3.Zero();
The equivalent way using react-babylonjs
:
<boxname="box"size={2}position={new Vector3(0, 1, 0)}rotation={Vector3.Zero()}/>
You may be wondering why the element is called "box" - that's because we are using the factory creation method name (without "Create" prefix).
What's important to note is that the constructor arguments name
and size
are
passed in and are used once only. The resulting object that you have props
available for is a Mesh object, which has properties like position
and
rotation
. When non-constructor props get new values they are set on the
underlying Babylon.js object automatically - the same as you may be accustomed
to with CSS or element attributes in DOM. Lastly, notice that we didn't need to
pass in a scene object - that is done automatically.
- Output
- Code
Let's quickly look with FreeCamera at a regular Babylon.js object that's not using a creation method to see how most objects are created.
This is how we'd create a FreeCamera declaratively:
import { FreeCamera, Vector3 } from '@babylonjs/core'var camera = new FreeCamera('camera1', new Vector3(0, 5, -10), scene)// This targets the camera to scene origincamera.setTarget(Vector3.Zero())
This is the equivalent in react-babylonjs
:
<freeCameraname="camera1"position={new Vector3(0, 5, -10)}setTarget={[Vector3.Zero()]}/>
In this case we passed in the name
"camera1" and initial position
. When
calling a method we can use an array for the parameters. The position
is also
a property, so we can update that prop to change it as well.