Using Spline with Playroom Kit
This section is under development and very experimental.
Spline is a free, real-time collaborative 3D design tool to create interactive experiences within the browser.
We, at Playroom love making game prototypes with Spline. Instead of remaking them in a game engine, wouldn’t it be cool if we could just export the Spline project and add game logic and multiplayer on top of it?
Spline publishes @splinetool/runtime NPM package. This package can be used to load Spline projects in your own website and has minimal API to control the scene.
This demo uses some internal APIs of Spline. Which is why it’s not very stable for now.
Touch the screen to move the ball.
You can see the full source code here.
How does this work?
We create a Spline project with physics enabled. We also pre-create all player objects in the scene and name them Ball1, Ball2, etc.
Then, in a local HTML project, we initialize Playroom Kit to show the UI and connect players:
const { onPlayerJoin, insertCoin, isHost, myPlayer } = Playroom;
// ...
await insertCoin();Then, we use Spline’s runtime to load the Spline project in the browser. Which is essentially something like this:
import { Application } from '@splinetool/runtime';
const canvas = document.getElementById('canvas3d');
const app = new Application(canvas);
await app.load('https://path/to/scene.splinecode'); // Fetch this by clicking export on your Spline projectThen, we connect a joystick and send state to Playroom Kit:
const joystick = nipplejs.create();
joystick.on("move", (e, data) => {
myPlayer().setState("dir", data.vector);
});
joystick.on("end", () => {
myPlayer().setState("dir", {x:0, y:0});
});After this, we use Spline’s internal APIs to get the right ball for joining players:
let players = [];
function getObjectFromSpline(name){
return app._scene.children.find((child)=> child.name === name);
};
onPlayerJoin((state) => {
const ball = getObjectFromSpline("Ball" + (players.length+1));
players.push({state, ball});
});Finally, we want to move the ball when player moves the joystick. We do this in a loop (see full code for details):
const loop = new AnimationFrame(120, () => {
if (isHost()){
for (const player of players) {
const controls = player.state.getState("dir") || {};
player.ball.rigidBody.setLinvel({
x: controls?.x * 4.0 || 0.0,
y:player.ball.rigidBody.linvel().y,
z: controls?.y * -4.0 || 0.0
}, true);
player.state.setState("pos", player.ball.rigidBody.translation());
}
}
else{
for (const player of players) {
const pos = player.state.getState("pos");
if (pos){
player.ball.rigidBody.setTranslation(pos, true);
}
}
}
});