Hey @gunpowderfans,
Sorry but I don’t know how to save the result in the playcode.
But here you have it:
import React, { useEffect, useRef } from 'react';
import {
Engine,
Scene,
HemisphericLight,
Vector3,
ArcRotateCamera,
MeshBuilder,
} from '@babylonjs/core';
import {
AdvancedDynamicTexture,
TextBlock,
Control,
Rectangle,
Button,
} from '@babylonjs/gui';
export default function App() {
const reactCanvas = useRef();
const startEngineLoop = ({canvas, engine, scene}) => {
// Light
const light = new HemisphericLight('light', Vector3.Up(), scene);
light.intensity = 0.5;
// Camera
const camera: ArcRotateCamera = new ArcRotateCamera(
'camera',
Math.PI,
Math.PI / 4,
10,
new Vector3(0, -5, 0),
scene
);
scene.activeCamera = camera;
scene.activeCamera.attachControl(canvas, true);
camera.attachControl(canvas, true);
engine.runRenderLoop(() => {
scene.render();
});
return { camera };
};
useEffect(() => {
const { current: canvas } = reactCanvas;
if (!canvas) return;
const engine = new Engine(canvas);
const scene = new Scene(engine);
const {camera} = startEngineLoop({canvas, engine, scene});
// Some objects
const ground = MeshBuilder.CreateGround(
'ground',
{ height: 20, width: 20, subdivisions: 4 },
scene
);
const box = MeshBuilder.CreateBox('box', { size: 2 }, scene);
const ball = MeshBuilder.CreateSphere(
'ball',
{ segments: 8, diameter: 2 },
scene
);
ball.position.set(0, 1, 0);
camera.setTarget(ball);
// UI Text
const advancedTexture = AdvancedDynamicTexture.CreateFullscreenUI(
'textUI',
undefined,
scene
);
const uiText = new TextBlock('instructions');
uiText.text = 'Test Multiple Scenes';
uiText.color = '#f0ff00';
uiText.fontFamily = 'Roboto';
uiText.fontSize = 48;
uiText.textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
uiText.textVerticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
uiText.paddingBottom = '10px';
advancedTexture.addControl(uiText);
// A button to click
const clickBox = MeshBuilder.CreateBox('clickBox', { size: 1 }, scene);
clickBox.isPickable = true;
clickBox.position.set(5, 1, -5);
const rect = new Rectangle();
rect.width = '125px';
rect.height = '20px';
rect.color = 'black';
rect.background = 'white';
rect.thickness = 0;
advancedTexture.addControl(rect);
rect.linkWithMesh(clickBox);
const button = Button.CreateSimpleButton('button', 'Goto another scene');
button.width = 2;
button.height = 2;
button.color = 'white';
button.background = 'black';
button.fontSize = 12;
rect.addControl(button);
button.onPointerClickObservable.add(() => {
console.log('Button Clicked');
scene.dispose();
engine.stopRenderLoop();
const sceneAlt = new Scene(engine);
startEngineLoop({canvas, engine, scene: sceneAlt});
// UI
const advancedTexture = AdvancedDynamicTexture.CreateFullscreenUI(
'textUI',
undefined,
sceneAlt
);
const uiText = new TextBlock('instructions');
uiText.text = 'Welcome to another scene';
uiText.color = '#f0ff00';
uiText.fontFamily = 'Roboto';
uiText.fontSize = 48;
uiText.textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
uiText.textVerticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
uiText.paddingBottom = '10px';
advancedTexture.addControl(uiText);
// Some items
const box = MeshBuilder.CreateBox('box', { size: 2 }, sceneAlt);
const ball = MeshBuilder.CreateSphere(
'player',
{ segments: 8, diameter: 2 },
sceneAlt
);
ball.position.set(0, 1, 0);
});
// Actually I have no idea how the latter lines work, it's not important now
// https://doc.babylonjs.com/communityExtensions/Babylon.js+ExternalLibraries/BabylonJS_and_ReactJS
const resize = () => {
scene.getEngine().resize();
};
if (window) {
window.addEventListener('resize', resize);
}
return () => {
scene.getEngine().dispose();
if (window) {
window.removeEventListener('resize', resize);
}
};
}, []);
return (
<canvas
style={{ width: '100%', height: '100%' }}
ref={reactCanvas}
></canvas>
);
}
// Log to console
console.log('Hello console');
I don’t know if it’s the best approach but in general what I do is:
- I have function
startEngineLoop
which I call when I want to start the engine loop. I call it twice here: at the beginning and after clicking the button
- second thing is to call
scene.dispose();
when you want to switch to new scene
Let me know if that helped!