Kurz: Hraní 3D klavíru
V předchozím kurzu jsme úspěšně vytvořili model plné klavírní klávesnice s 88 klávesy. Teď ho nastavíme jako hratelnou v prostoru XR.
V tomto kurzu se naučíte, jak:
- Přidání interaktivních klavírních prvků pomocí událostí ukazatele
- Škálování sítí na jinou velikost
- Povolení podpory teleportace a více ukazatelů v XR
Než začnete
Ujistěte se, že jste prošli předchozím kurzem v této sérii , jste připraveni pokračovat v přidávání do kódu.
index.html
<html>
<head>
<title>Piano in BabylonJS</title>
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<script src="scene.js"></script>
<style>
body,#renderCanvas { width: 100%; height: 100%;}
</style>
</head>
<body>
<canvas id="renderCanvas"></canvas>
<script type="text/javascript">
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
createScene(engine).then(sceneToRender => {
engine.runRenderLoop(() => sceneToRender.render());
});
// Watch for browser/canvas resize events
window.addEventListener("resize", function () {
engine.resize();
});
</script>
</body>
</html>
scene.js
const buildKey = function (scene, parent, props) {
if (props.type === "white") {
/*
Props for building a white key should contain:
note, topWidth, bottomWidth, topPositionX, wholePositionX, register, referencePositionX
As an example, the props for building the middle C white key would be
{type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4, register: 4, referencePositionX: 0}
*/
// Create bottom part
const bottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: props.bottomWidth, height: 1.5, depth: 4.5}, scene);
// Create top part
const top = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: props.topWidth, height: 1.5, depth: 5}, scene);
top.position.z = 4.75;
top.position.x += props.topPositionX;
// Merge bottom and top parts
// Parameters of BABYLON.Mesh.MergeMeshes: (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
const key = BABYLON.Mesh.MergeMeshes([bottom, top], true, false, null, false, false);
key.position.x = props.referencePositionX + props.wholePositionX;
key.name = props.note + props.register;
key.parent = parent;
return key;
}
else if (props.type === "black") {
/*
Props for building a black key should contain:
note, wholePositionX, register, referencePositionX
As an example, the props for building the C#4 black key would be
{type: "black", note: "C#", wholePositionX: -13.45, register: 4, referencePositionX: 0}
*/
// Create black color material
const blackMat = new BABYLON.StandardMaterial("black");
blackMat.diffuseColor = new BABYLON.Color3(0, 0, 0);
// Create black key
const key = BABYLON.MeshBuilder.CreateBox(props.note + props.register, {width: 1.4, height: 2, depth: 5}, scene);
key.position.z += 4.75;
key.position.y += 0.25;
key.position.x = props.referencePositionX + props.wholePositionX;
key.material = blackMat;
key.parent = parent;
return key;
}
}
const createScene = async function(engine) {
const scene = new BABYLON.Scene(engine);
const alpha = 3*Math.PI/2;
const beta = Math.PI/50;
const radius = 220;
const target = new BABYLON.Vector3(0, 0, 0);
const camera = new BABYLON.ArcRotateCamera("Camera", alpha, beta, radius, target, scene);
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.6;
const keyParams = [
{type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4},
{type: "black", note: "C#", wholePositionX: -13.45},
{type: "white", note: "D", topWidth: 1.4, bottomWidth: 2.4, topPositionX: 0, wholePositionX: -12},
{type: "black", note: "D#", wholePositionX: -10.6},
{type: "white", note: "E", topWidth: 1.4, bottomWidth: 2.3, topPositionX: 0.45, wholePositionX: -9.6},
{type: "white", note: "F", topWidth: 1.3, bottomWidth: 2.4, topPositionX: -0.55, wholePositionX: -7.2},
{type: "black", note: "F#", wholePositionX: -6.35},
{type: "white", note: "G", topWidth: 1.3, bottomWidth: 2.3, topPositionX: -0.2, wholePositionX: -4.8},
{type: "black", note: "G#", wholePositionX: -3.6},
{type: "white", note: "A", topWidth: 1.3, bottomWidth: 2.3, topPositionX: 0.2, wholePositionX: -2.4},
{type: "black", note: "A#", wholePositionX: -0.85},
{type: "white", note: "B", topWidth: 1.3, bottomWidth: 2.4, topPositionX: 0.55, wholePositionX: 0},
]
// Transform Node that acts as the parent of all piano keys
const keyboard = new BABYLON.TransformNode("keyboard");
// Register 1 through 7
var referencePositionX = -2.4*14;
for (let register = 1; register <= 7; register++) {
keyParams.forEach(key => {
buildKey(scene, keyboard, Object.assign({register: register, referencePositionX: referencePositionX}, key));
})
referencePositionX += 2.4*7;
}
// Register 0
buildKey(scene, keyboard, {type: "white", note: "A", topWidth: 1.9, bottomWidth: 2.3, topPositionX: -0.20, wholePositionX: -2.4, register: 0, referencePositionX: -2.4*21});
keyParams.slice(10, 12).forEach(key => {
buildKey(scene, keyboard, Object.assign({register: 0, referencePositionX: -2.4*21}, key));
})
// Register 8
buildKey(scene, keyboard, {type: "white", note: "C", topWidth: 2.3, bottomWidth: 2.3, topPositionX: 0, wholePositionX: -2.4*6, register: 8, referencePositionX: 84});
// Transform node that acts as the parent of all piano components
const piano = new BABYLON.TransformNode("piano");
keyboard.parent = piano;
// Import and scale piano frame
BABYLON.SceneLoader.ImportMesh("frame", "https://raw.githubusercontent.com/MicrosoftDocs/mixed-reality/docs/mixed-reality-docs/mr-dev-docs/develop/javascript/tutorials/babylonjs-webxr-piano/files/", "pianoFrame.babylon", scene, function(meshes) {
const frame = meshes[0];
frame.parent = piano;
});
// Lift the piano keyboard
keyboard.position.y += 80;
const xrHelper = await scene.createDefaultXRExperienceAsync();
return scene;
}
Zajištění hratelné klávesnice pro klavír
Právě teď je klavírní klávesnice, kterou jsme vytvořili, statickým modelem, který nereaguje na žádné interakce uživatelů. V této části naprogramujeme klávesy tak, aby se pohybovaly dolů a přehrály zvuk, když je někdo stiskne.
Babylon.js poskytuje různé druhy událostí neboli pozorovatelných událostí, se kterými můžeme pracovat. V našem případě se budeme zabývat klávesami
onPointerObservable
, protože chceme naprogramovat klávesy tak, aby prováděly akce, když na ně někdo stiskne ukazatel, což může být kliknutí myší, dotyk, kliknutí na tlačítko ovladače XR atd.Tady je základní struktura, jak můžeme přidat jakékoli chování k objektu
onPointerObservable
:scene.onPointerObservable.add((pointerInfo) => { // do something });
I když Babylon.js poskytuje mnoho různých typů událostí ukazatele, k naprogramování chování kláves klavíru budeme používat
POINTERDOWN
pouze události aPOINTERUP
s využitím následující struktury:scene.onPointerObservable.add((pointerInfo) => { switch (pointerInfo.type) { case BABYLON.PointerEventTypes.POINTERDOWN: // When the pointer is down on a piano key, // move the piano key downward (to show that it is pressed) // and play the sound of the note break; case BABYLON.PointerEventTypes.POINTERUP: // When the pointer is released, // move the piano key upward to its original position // and stop the sound of the note of the key that is released break; } });
Pojďme nejprve zapracovat na přesunutí klávesy pro klavír směrem dolů a nahoru, když klávesu stiskneme a uvolníme.
Při události ukazatele dolů musíme zjistit síť, na kterou klikáte, ujistit se, že se jedná o klávesu klavíru, a změnit souřadnici y sítě o malou část záporně, aby to vypadalo, že byla klávesa stisknuta dolů.
Pro událost ukazatele nahoru je to trochu složitější, protože ukazatel, který stiskl klávesu, nemusí být uvolněn na klávesu. Někdo například může kliknout dolů na klávesu C4, přetáhnout myš na E4 a pak kliknutí uvolnit. V tomto případě chceme uvolnit stisknutou klávesu (C4) místo místa výskytu
pointerUp
události (E4).Pojďme se podívat, jak následující kód dosáhne toho, co chceme:
const pointerToKey = new Map(); scene.onPointerObservable.add((pointerInfo) => { switch (pointerInfo.type) { case BABYLON.PointerEventTypes.POINTERDOWN: if(pointerInfo.pickInfo.hit) { const pickedMesh = pointerInfo.pickInfo.pickedMesh; const pointerId = pointerInfo.event.pointerId; if (pickedMesh.parent === keyboard) { pickedMesh.position.y -= 0.5; // play the sound of the note pointerToKey.set(pointerId, { mesh: pickedMesh }); } } break; case BABYLON.PointerEventTypes.POINTERUP: const pointerId = pointerInfo.event.pointerId; if (pointerToKey.has(pointerId)) { pointerToKey.get(pointerId).mesh.position.y += 0.5; // stop the sound of the note of the key that is released pointerToKey.delete(pointerId); } break; } });
Je
pointerId
jedinečný pro každý ukazatel a může nám pomoct identifikovat ukazatel, pokud máme více ovladačů nebo pokud používáme dotykovou obrazovku. Zde jsme inicializovaliMap
objekt s názvempointerToKey
pro uložení relace, který ukazatel stiskl na jakou klávesu, abychom věděli, který klíč se má uvolnit při uvolnění ukazatele, bez ohledu na to, kde k uvolnění dojde.Interakce s výše uvedeným kódem by vypadala takto:
Teď zapracujme na přehrávání a zastavení zvuku při stisknutí a uvolnění klávesy. Abychom toho dosáhli, využijeme javascriptovou knihovnu soundfont-player, která nám umožňuje snadno přehrávat midi zvuky zvoleného nástroje.
Stáhněte si minifikovaný kód knihovny, uložte ho do stejné složky jako index.htmla zahrňte ho do značky
<header>
v index.html:<head> <title>Babylon Template</title> <script src="https://cdn.babylonjs.com/babylon.js"></script> <script src="scene.js"></script> <script src="soundfont-player.min.js"></script> <style> body,#renderCanvas { width: 100%; height: 100%;} </style> </head>
Po importu knihovny můžeme inicializovat nástroj a přehrávat nebo zastavit zvuky MIDI pomocí knihovny:
const pianoSound = await Soundfont.instrument(new AudioContext(), 'acoustic_grand_piano'); const C4 = piano.play("C4"); // Play note C4 C4.stop(); // Stop note C4
Teď to začleněme do událostí ukazatele a dokončíme kód pro tento oddíl:
const pointerToKey = new Map() const piano = await Soundfont.instrument(new AudioContext(), 'acoustic_grand_piano'); scene.onPointerObservable.add((pointerInfo) => { switch (pointerInfo.type) { case BABYLON.PointerEventTypes.POINTERDOWN: if(pointerInfo.pickInfo.hit) { let pickedMesh = pointerInfo.pickInfo.pickedMesh; let pointerId = pointerInfo.event.pointerId; if (keys.has(pickedMesh)) { pickedMesh.position.y -= 0.5; // Move the key downward pointerToKey.set(pointerId, { mesh: pickedMesh, note: pianoSound.play(pointerInfo.pickInfo.pickedMesh.name) // Play the sound of the note }); } } break; case BABYLON.PointerEventTypes.POINTERUP: let pointerId = pointerInfo.event.pointerId; if (pointerToKey.has(pointerId)) { pointerToKey.get(pointerId).mesh.position.y += 0.5; // Move the key upward pointerToKey.get(pointerId).note.stop(); // Stop the sound of the note pointerToKey.delete(pointerId); } break; } });
Vzhledem k tomu, že jsme pojmenovali síť jednotlivých klíčů podle poznámky, kterou představuje, můžeme snadno určit, kterou poznámku se má přehrát, předáním názvu sítě funkci
pianoSound.play()
. Všimněte si také, že zvuk ukládáme dopointerToKey
mapy, abychom věděli, jaký zvuk se má při uvolnění klávesy zastavit.
Škálování klavíru pro imerzivní vr režim
Teď už jste pravděpodobně hráli s klavírem pomocí myši (nebo dokonce s dotykovou obrazovkou), když jste přidali interaktivní funkce. V této části se přesuneme do imerzivního prostoru VR.
Pokud chcete stránku otevřít v imerzivní náhlavní soupravě vr, musíte nejdřív připojit náhlavní soupravu k vývojářskému počítači a ujistit se, že je nastavená pro použití v aplikaci Windows Mixed Reality. Pokud používáte simulátor Windows Mixed Reality, ujistěte se, že je povolený.
V pravém dolním rohu webové stránky se teď zobrazí tlačítko Asistivní VR. Klikněte na něj a uvidíte klavír v zařízení XR, ke kterému jste připojeni.
Jakmile jste ve virtuálním prostoru, můžete si všimnout, že piano, které jsme vytvořili, je velmi obrovské. Ve světě VR můžeme jen stát na jeho konci a přehrávat ji tak, že najezdíme ukazatelem na klávesy v dálce.
Pojďme vertikálně snížit kapacitu klavíru tak, aby jeho velikost byla více jako normální standup piano v reálném životě. K tomu budeme muset použít funkci nástroje, která nám umožní škálovat síť vzhledem k bodu v prostoru. Přidejte tuto funkci do scene.js (mimo ):
createScene()
const scaleFromPivot = function(transformNode, pivotPoint, scale) { const _sx = scale / transformNode.scaling.x; const _sy = scale / transformNode.scaling.y; const _sz = scale / transformNode.scaling.z; transformNode.scaling = new BABYLON.Vector3(_sx, _sy, _sz); transformNode.position = new BABYLON.Vector3(pivotPoint.x + _sx * (transformNode.position.x - pivotPoint.x), pivotPoint.y + _sy * (transformNode.position.y - pivotPoint.y), pivotPoint.z + _sz * (transformNode.position.z - pivotPoint.z)); }
Tato funkce přijímá 3 parametry:
-
transformNode: objekt, který
TransformNode
se má škálovat -
pivotPoint:
Vector3
objekt, který označuje bod, ke kterému je měřítko relativní - scale: faktor škálování
-
transformNode: objekt, který
Tuto funkci použijeme k měřítku klavírního rámu a kláves faktorem 0,015 s bodem otáčení na počátku. Připojte volání funkce k
createScene()
funkci tak, že ho umístíte zakeyboard.position.y += 80;
:// Put this line at the beginning of createScene() const scale = 0.015;
// Put this function call after keyboard.position.y += 80; // Scale the entire piano scaleFromPivot(piano, new BABYLON.Vector3(0, 0, 0), scale);
Nezapomeňme také škálovat polohu kamery:
const alpha = 3*Math.PI/2; const beta = Math.PI/50; const radius = 220*scale; // scale the radius const target = new BABYLON.Vector3(0, 0, 0); const camera = new BABYLON.ArcRotateCamera("Camera", alpha, beta, radius, target, scene); camera.attachControl(canvas, true);
Teď, když znovu vstoupíme do prostoru VR, bude mít klavír velikost obyčejného standup klavíru.
Povolení funkcí WebXR
Teď, když jsme v prostoru VR zvětšili velikost klavíru na správnou velikost, pojďme povolit některé skvělé funkce WebXR, abychom vylepšili naše prostředí pro hraní klavírů v prostoru.
Pokud jste hráli na klavír pomocí imerzivních ovladačů VR, možná jste si všimli, že můžete používat pouze jeden ovladač najednou. Pojďme povolit podporu více ukazatelů v prostoru XR pomocí správce funkcí WebXR Babylon.js.
Za inicializační řádek přidejte do
createScene()
funkcexrHelper
následující kód:const featuresManager = xrHelper.baseExperience.featuresManager; const pointerSelection = featuresManager.enableFeature(BABYLON.WebXRFeatureName.POINTER_SELECTION, "stable", { xrInput: xrHelper.input, enablePointerSelectionOnAllControllers: true });
Kromě toho, v závislosti na tom, kde je váš výchozí bod, může být pro vás trochu obtížné umístit se před klavír. Pokud jste obeznámeni s imerzivním prostředím VR, možná už víte o teleportaci, což je funkce, která vám umožní okamžitě přejít na jiné místo v prostoru tím, že na něj nasměrujete.
Abychom mohli používat funkci teleportace Babylon.js, musíme mít nejprve uzemněnou síť, na které se můžeme "postavit" v prostoru VR. Přidejte do
createScene()
funkce následující kód, který vytvoří základ:const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 400, height: 400});
Součástí podpory teleportace je také velmi užitečná funkce, která se nazývá přichycení k pozicím. Stručně řečeno, pozice přichycení jsou konkrétní pozice, na které chceme, aby se uživatelé dostali.
Můžeme například nastavit pozici přichycení před klavírem, aby se uživatelé mohli snadno teleportovat na toto místo, když namíří ukazateli blízko klavíru.
Připojte následující kód, který povolí funkci teleportace a určí bod přichycení:
const teleportation = featuresManager.enableFeature(BABYLON.WebXRFeatureName.TELEPORTATION, "stable", { xrInput: xrHelper.input, floorMeshes: [ground], snapPositions: [new BABYLON.Vector3(2.4*3.5*scale, 0, -10*scale)], });
Nyní byste měli být schopni snadno se umístit před klavír teleportováním na přichycením bodu před klavírem a měli byste být schopni hrát dvě klávesy najednou pomocí obou ovladačů.
Souhrn
Blahopřejeme! Dokončili jste naši řadu kurzů Babylon.js vytváření klavírů a naučili jste se:
- Vytvoření, umístění a sloučení sítí pro vytvoření modelu klavírní klávesnice
- Import Babylon.js modelu standup piano frame
- Přidání interakcí s ukazatelem ke každé klavírní klávese
- Škálování velikosti sítí na základě bodu otáčení
- Povolení klíčových funkcí WebXR, jako je teleportace a podpora více bodů
Tady je konečný kód proscene.js a index.html:
scene.js
const buildKey = function (scene, parent, props) {
if (props.type === "white") {
/*
Props for building a white key should contain:
note, topWidth, bottomWidth, topPositionX, wholePositionX, register, referencePositionX
As an example, the props for building the middle C white key would be
{type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4, register: 4, referencePositionX: 0}
*/
// Create bottom part
const bottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: props.bottomWidth, height: 1.5, depth: 4.5}, scene);
// Create top part
const top = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: props.topWidth, height: 1.5, depth: 5}, scene);
top.position.z = 4.75;
top.position.x += props.topPositionX;
// Merge bottom and top parts
// Parameters of BABYLON.Mesh.MergeMeshes: (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
const key = BABYLON.Mesh.MergeMeshes([bottom, top], true, false, null, false, false);
key.position.x = props.referencePositionX + props.wholePositionX;
key.name = props.note + props.register;
key.parent = parent;
return key;
}
else if (props.type === "black") {
/*
Props for building a black key should contain:
note, wholePositionX, register, referencePositionX
As an example, the props for building the C#4 black key would be
{type: "black", note: "C#", wholePositionX: -13.45, register: 4, referencePositionX: 0}
*/
// Create black color material
const blackMat = new BABYLON.StandardMaterial("black");
blackMat.diffuseColor = new BABYLON.Color3(0, 0, 0);
// Create black key
const key = BABYLON.MeshBuilder.CreateBox(props.note + props.register, {width: 1.4, height: 2, depth: 5}, scene);
key.position.z += 4.75;
key.position.y += 0.25;
key.position.x = props.referencePositionX + props.wholePositionX;
key.material = blackMat;
key.parent = parent;
return key;
}
}
const scaleFromPivot = function(transformNode, pivotPoint, scale) {
const _sx = scale / transformNode.scaling.x;
const _sy = scale / transformNode.scaling.y;
const _sz = scale / transformNode.scaling.z;
transformNode.scaling = new BABYLON.Vector3(_sx, _sy, _sz);
transformNode.position = new BABYLON.Vector3(pivotPoint.x + _sx * (transformNode.position.x - pivotPoint.x), pivotPoint.y + _sy * (transformNode.position.y - pivotPoint.y), pivotPoint.z + _sz * (transformNode.position.z - pivotPoint.z));
}
const createScene = async function(engine) {
const scale = 0.015;
const scene = new BABYLON.Scene(engine);
const alpha = 3*Math.PI/2;
const beta = Math.PI/50;
const radius = 220*scale;
const target = new BABYLON.Vector3(0, 0, 0);
const camera = new BABYLON.ArcRotateCamera("Camera", alpha, beta, radius, target, scene);
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.6;
const keyParams = [
{type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4},
{type: "black", note: "C#", wholePositionX: -13.45},
{type: "white", note: "D", topWidth: 1.4, bottomWidth: 2.4, topPositionX: 0, wholePositionX: -12},
{type: "black", note: "D#", wholePositionX: -10.6},
{type: "white", note: "E", topWidth: 1.4, bottomWidth: 2.3, topPositionX: 0.45, wholePositionX: -9.6},
{type: "white", note: "F", topWidth: 1.3, bottomWidth: 2.4, topPositionX: -0.55, wholePositionX: -7.2},
{type: "black", note: "F#", wholePositionX: -6.35},
{type: "white", note: "G", topWidth: 1.3, bottomWidth: 2.3, topPositionX: -0.2, wholePositionX: -4.8},
{type: "black", note: "G#", wholePositionX: -3.6},
{type: "white", note: "A", topWidth: 1.3, bottomWidth: 2.3, topPositionX: 0.2, wholePositionX: -2.4},
{type: "black", note: "A#", wholePositionX: -0.85},
{type: "white", note: "B", topWidth: 1.3, bottomWidth: 2.4, topPositionX: 0.55, wholePositionX: 0},
]
// Transform Node that acts as the parent of all piano keys
const keyboard = new BABYLON.TransformNode("keyboard");
// Register 1 through 7
var referencePositionX = -2.4*14;
for (let register = 1; register <= 7; register++) {
keyParams.forEach(key => {
buildKey(scene, keyboard, Object.assign({register: register, referencePositionX: referencePositionX}, key));
})
referencePositionX += 2.4*7;
}
// Register 0
buildKey(scene, keyboard, {type: "white", note: "A", topWidth: 1.9, bottomWidth: 2.3, topPositionX: -0.20, wholePositionX: -2.4, register: 0, referencePositionX: -2.4*21});
keyParams.slice(10, 12).forEach(key => {
buildKey(scene, keyboard, Object.assign({register: 0, referencePositionX: -2.4*21}, key));
})
// Register 8
buildKey(scene, keyboard, {type: "white", note: "C", topWidth: 2.3, bottomWidth: 2.3, topPositionX: 0, wholePositionX: -2.4*6, register: 8, referencePositionX: 84});
// Transform node that acts as the parent of all piano components
const piano = new BABYLON.TransformNode("piano");
keyboard.parent = piano;
// Import and scale piano frame
BABYLON.SceneLoader.ImportMesh("frame", "https://raw.githubusercontent.com/MicrosoftDocs/mixed-reality/docs/mixed-reality-docs/mr-dev-docs/develop/javascript/tutorials/babylonjs-webxr-piano/files/", "pianoFrame.babylon", scene, function(meshes) {
const frame = meshes[0];
frame.parent = piano;
});
// Lift the piano keyboard
keyboard.position.y += 80;
// Scale the entire piano
scaleFromPivot(piano, new BABYLON.Vector3(0, 0, 0), scale);
const pointerToKey = new Map()
const pianoSound = await Soundfont.instrument(new AudioContext(), 'acoustic_grand_piano');
scene.onPointerObservable.add((pointerInfo) => {
switch (pointerInfo.type) {
case BABYLON.PointerEventTypes.POINTERDOWN:
// Only take action if the pointer is down on a mesh
if(pointerInfo.pickInfo.hit) {
let pickedMesh = pointerInfo.pickInfo.pickedMesh;
let pointerId = pointerInfo.event.pointerId;
if (pickedMesh.parent === keyboard) {
pickedMesh.position.y -= 0.5; // Move the key downward
pointerToKey.set(pointerId, {
mesh: pickedMesh,
note: pianoSound.play(pointerInfo.pickInfo.pickedMesh.name) // Play the sound of the note
});
}
}
break;
case BABYLON.PointerEventTypes.POINTERUP:
let pointerId = pointerInfo.event.pointerId;
// Only take action if the released pointer was recorded in pointerToKey
if (pointerToKey.has(pointerId)) {
pointerToKey.get(pointerId).mesh.position.y += 0.5; // Move the key upward
pointerToKey.get(pointerId).note.stop(); // Stop the sound of the note
pointerToKey.delete(pointerId);
}
break;
}
});
const xrHelper = await scene.createDefaultXRExperienceAsync();
const featuresManager = xrHelper.baseExperience.featuresManager;
featuresManager.enableFeature(BABYLON.WebXRFeatureName.POINTER_SELECTION, "stable", {
xrInput: xrHelper.input,
enablePointerSelectionOnAllControllers: true
});
const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 400, height: 400});
featuresManager.enableFeature(BABYLON.WebXRFeatureName.TELEPORTATION, "stable", {
xrInput: xrHelper.input,
floorMeshes: [ground],
snapPositions: [new BABYLON.Vector3(2.4*3.5*scale, 0, -10*scale)],
});
return scene;
}
index.html
<html>
<head>
<title>Babylon Template</title>
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<script src="scene.js"></script>
<script src="soundfont-player.min.js"></script>
<style>
body,#renderCanvas { width: 100%; height: 100%;}
</style>
</head>
<body>
<canvas id="renderCanvas"></canvas>
<script>
const canvas = document.getElementById("renderCanvas"); // Get the canvas element
const engine = new BABYLON.Engine(canvas, true); // Generate the BABYLON 3D engine
// Register a render loop to repeatedly render the scene
createScene(engine).then(sceneToRender => {
engine.runRenderLoop(() => sceneToRender.render());
});
// Watch for browser/canvas resize events
window.addEventListener("resize", function () {
engine.resize();
});
</script>
</body>
</html>
Další kroky
Další informace o vývoji Mixed Reality JavaScriptu najdete v tématu Přehled vývoje pro JavaScript.