XR controllers on scene

This commit is contained in:
2026-03-08 20:30:41 +02:00
parent 47f1552000
commit 5e142a2757
7 changed files with 71 additions and 55 deletions
@@ -48,7 +48,7 @@ class PairMatchingGame extends EventManager {
uv[xi].array[8 * i + 7] = s[1];
}
let mesh = new Mesh(b[xi], material.clone());
mesh.position.set(xi * dx, o[xi][i] * dy, 0);
mesh.position.set(xi * dx + Math.random() * (xi+0.5) * dx, o[xi][i] * dy, Math.random() * (xi+0.5) * dx);
mesh.userData.gd = {
pair: xi,
id: i
@@ -75,7 +75,7 @@ class PairMatchingGame extends EventManager {
let actionDef = {material:{emissiveIntensity:k=>(1+Math.sin((k*2+1.5)*Math.PI))*4 }};
let clickFn = (i) => {
var clickFn = (i) => {
this.dispatchEvent({type:'interaction'});
let oc = clicked;
if (done.includes(i.object.userData.gd.id)) return;
+19 -14
View File
@@ -1,7 +1,7 @@
import { Raycaster, Vector3 } from "three"
class Clickable {
constructor(defaultDistance) {
constructor(engine, defaultDistance) {
const objects = [];
const raycaster = new Raycaster();
let v = new Vector3;
@@ -21,34 +21,39 @@ class Clickable {
objects.splice(0, objects.length);
}
this.update = function (mouse, camera, event) {
raycaster.setFromCamera(mouse, camera);
this.handleController = function(controller){
raycaster.setFromXRController(controller);
this.handle();
}
this.handleMouse = function (mouse, event) {
raycaster.setFromCamera(mouse, engine.camera);
this.handle();
};
this.handle = function(){
let forExecute = [];
objects.filter(o=>{
do {
if (o.__active === false) return false;
if (o.__active === false || o.visible === false) return false;
o = o.parent;
} while (o);
return true;
}).forEach(o => {
o.getWorldPosition(v);
if (camera.position.distanceTo(v) <= o._clickable.distance && o.visible) {
if (engine.cameraWorld.position.distanceTo(v) <= o._clickable.distance) {
const intersects = raycaster.intersectObject(o);
// intersects.forEach(i=>{
// forExecute.push({o, i})
// })
if (intersects[0]) forExecute.push({ o, i: intersects[0] });
if (intersects[0]) {
forExecute.push({ o, i: intersects[0] });
}
}
});
if (forExecute[0]) {
let sorted = forExecute.sort((a, b) => a.i.distance - b.i.distance);
sorted[0].o._clickable.fn(sorted[0].i);
if (this.onclick) {
this.onclick(sorted[0].o, sorted[0].i, event);
}
}
};
}
}
}
export {Clickable}
export { Clickable }
+2 -2
View File
@@ -29,7 +29,7 @@ class DashBoard extends EventManager {
dash.visible = false;
const k = 3.11;
const dashWidth = engine.aspect * k, dashHeight = k;
const dashDistance = 2.5;
const dashDistance = 1.77;
this.width = dashWidth;
this.height = dashHeight;
@@ -202,7 +202,7 @@ class DashBoard extends EventManager {
//dashPlacement, rotate = false, plane = false
this.attach = (object, opts = {})=>{
this.cameraFix = engine.hero.cameraZ;
engine.hero.cameraZ = 12;
engine.hero.cameraZ = 6;
hud.visible = true;
hudPlane.visible = !!opts.plane;
if (opts.plane){
+16 -7
View File
@@ -1,7 +1,7 @@
import { Raycaster, Vector3 } from "three"
class Draggable{
constructor(defaultDistance){
constructor(engine, defaultDistance){
const objects = [];
const raycaster = new Raycaster();
let v = new Vector3;
@@ -16,13 +16,22 @@ class Draggable{
objects.splice(objects.indexOf(object), 1);
}
this.update = function(pointer, camera, action){
raycaster.setFromCamera(pointer, camera);
if (action == 'start'){
this.handleController = function(controller, action){
raycaster.setFromXRController(controller);
this.handle(action);
}
this.handleMouse = function (mouse, action) {
raycaster.setFromCamera(mouse, engine.camera);
this.handle(action);
};
this.handle = function(action){
if (['start', 'selectstart'].includes(action)){
let forExecute = [];
objects.forEach(o=>{
o.getWorldPosition(v);
if (camera.position.distanceTo(v) <= o._draggable.distance && o.visible){
if (engine.cameraWorld.position.distanceTo(v) <= o._draggable.distance && o.visible){
const intersects = raycaster.intersectObject(o);
if (intersects[0]) forExecute.push({o, i:intersects[0]})
}
@@ -34,10 +43,10 @@ class Draggable{
dragging = s;
dragging.zone = raycaster.intersectObject(s.o._draggable.dragZone)[0];
}
}else if (action == 'end' && dragging){
}else if (['end', 'selectend'].includes(action) && dragging){
dragging.o._draggable.fn.end && dragging.o._draggable.fn.end(dragging);
dragging = null;
}else if(action == 'drag' && dragging){
}else if(['drag', 'move'].includes(action) && dragging){
const intersect = raycaster.intersectObject(dragging.o._draggable.dragZone)[0];
if (intersect?.uv && dragging.zone?.uv){
dragging.o.position.x += -4*(dragging.zone.uv.x - intersect.uv.x);
+26 -27
View File
@@ -245,8 +245,8 @@ class GameEngine extends EventManager{
this.initXr();
}
this.clickable = new Clickable(20);
this.draggable = new Draggable(20);
this.clickable = new Clickable(this, 20);
this.draggable = new Draggable(this, 20);
}
initXr() {
@@ -261,7 +261,7 @@ class GameEngine extends EventManager{
// this.session = this.renderer.xr.getSession();
// this.session.addEventListener('selectstart', this.onControllerEvent.bind(this));
})
this.scene.add(c1);
this.cameraRig.add(c1);
let c2 = this.renderer.xr.getController(1);
c2.addEventListener('select', this.onSelect.bind(this));
@@ -272,17 +272,17 @@ class GameEngine extends EventManager{
c2.addEventListener('connected', e => {
c2.gamepad = e.data.gamepad;
})
this.scene.add(c2);
this.cameraRig.add(c2);
const controllerModelFactory = new XRControllerModelFactory();
let controllerGrip1 = this.renderer.xr.getControllerGrip(0);
controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1));
this.scene.add(controllerGrip1);
this.cameraRig.add(controllerGrip1);
let controllerGrip2 = this.renderer.xr.getControllerGrip(1);
controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2));
this.scene.add(controllerGrip2);
this.cameraRig.add(controllerGrip2);
const geometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, - 1)]);
@@ -379,14 +379,10 @@ class GameEngine extends EventManager{
}
onControllerEvent(event) {
//this.handleXrAction(event, this);
//event.type !== 'move' && console.log(event)
const controller = event.target;
//console.log(event)
if (!controller.userData?.active) return;
if (this.opts.designMode){
if (!controller.userData) return
if (controller.userData.active === false) return;
this.transformControls.getRaycaster().setFromXRController(controller);
switch (event.type) {
case 'selectstart':
@@ -400,26 +396,27 @@ class GameEngine extends EventManager{
this.transformControls.pointerMove(null);
break;
}
}else{
this.draggable?.handleController(controller, event.type)
}
}
onSelect(event) {
const controller = event.target;
this.xrController1.userData.active = false;
this.xrController2.userData.active = false;
if (controller === this.xrController1) {
this.xrController1.userData.active = true;
this.xrController1.add(this.controllerLine);
}
if (controller === this.xrController2) {
this.xrController2.userData.active = true;
this.xrController2.add(this.controllerLine);
}
if (this.opts.designMode){
this.xrController1.userData.active = false;
this.xrController2.userData.active = false;
if (controller === this.xrController1) {
this.xrController1.userData.active = true;
this.xrController1.add(this.controllerLine);
}
if (controller === this.xrController2) {
this.xrController2.userData.active = true;
this.xrController2.add(this.controllerLine);
}
this.raycaster.setFromXRController(controller);
const intersects = this.raycaster.intersectObjects(this.activeObjects.children, true);
@@ -434,6 +431,8 @@ class GameEngine extends EventManager{
this.transformControls.attach(intersects[0].object);
}, 100);
}
}else{
this.clickable.handleController(controller, event);
}
}
@@ -525,14 +524,14 @@ class GameEngine extends EventManager{
onClick(mouseEvent, domElement){
let mouse = this.getMouseVector(mouseEvent, domElement);
this.raycaster.setFromCamera(mouse, this.camera);
this.clickable.update(mouse, this.camera, mouseEvent);
//this.raycaster.setFromCamera(mouse, this.camera);
this.clickable.handleMouse(mouse, mouseEvent);
this.hero?.idleReset();
}
onPointer(mouseEvent, domElement, type){
let mouse = this.getMouseVector(mouseEvent, domElement);
this.draggable?.update(mouse, this.camera, type);
this.draggable?.handleMouse(mouse, type);
}
setCamera(camera) {
+4 -2
View File
@@ -95,11 +95,13 @@ class Hero{
this.updateCharacterControls(delta)
if (this.engine.renderer.xr.isPresenting){
this.cameraMode = 'fixed'
this.cameraY = this.size.y * 1;
//this.camera = this.engine.cameraWorld;
// this.engine.activeObjects.position.x = -this.camera.position.x;
// this.engine.activeObjects.position.z = -this.camera.position.z;
}else{
this.cameraMode = 'rotate'
this.cameraY = this.size.y * 1.5;
// this.camera = this.engine.cameraWorld;
// this.engine.camera.position.set(0,0,0);
// this.engine.camera.rotation.set(0,0,0);
@@ -163,7 +165,7 @@ class Hero{
this.fadeDuration = 1;
}
if (this.currentAction.startsWith('idle') && play != 'idle' && !pc.isLocked){
if (this.currentAction.startsWith('idle') && play != 'idle' && !pc.isLocked && !this.engine.renderer.xr.isPresenting){
this.cameraIdleDelta += delta * 0.33;
}else {
this.cameraIdleDelta = 0;
@@ -261,7 +263,7 @@ class Hero{
)
}
cameraPosition.lerp(cameraDesiredPosition, this.cameraMode == 'fixed' ? 1 : delta*2)
cameraPosition.lerp(cameraDesiredPosition, this.cameraMode == 'fixed' ? delta*2 : delta*2)
this.camera.position.copy(cameraPosition)
if (!pc.isLocked){
this.camera.lookAt(
+2 -1
View File
@@ -127,7 +127,8 @@ video{
.canvas-wrapper{
width: 100%;
max-width: 100vw;
height: calc(100vh - 288px);
//height: calc(100vh - 288px);
aspect-ratio: 16 / 9;
}
.canvas-wrapper:has(canvas:fullscreen){