Rearrange touch/step/teleport logic

This commit is contained in:
Tony Garnock-Jones 2023-02-16 14:29:37 +01:00
parent e865e00e56
commit 9bdc37bda1
2 changed files with 85 additions and 36 deletions

View File

@ -61,31 +61,46 @@ export type EngineOptions = {
canvas: HTMLCanvasElement | null,
};
export type ButtonState = { [button: number]: boolean };
export type ButtonState = {
checkTime: number,
isDown: boolean,
wasPressed: boolean,
wasReleased: boolean,
};
export class GamepadState {
buttons: ButtonState = {};
buttons: { [button: number]: ButtonState } = {};
checkTime = 0;
constructor (
public gp: Gamepad, // NB. browser's Gamepad class, not Babylon's Gamepad class.
) {}
latch(button: number): boolean {
let result = false;
const b = this.gp.buttons[button];
if (b) {
if (b.pressed) {
if (!this.isDown(button)) result = true;
this.buttons[button] = true;
} else {
this.buttons[button] = false;
}
}
return result;
tick() {
this.checkTime++;
}
isDown(button: number): boolean {
return this.buttons[button] ?? false;
b(button: number): ButtonState {
if (!(button in this.buttons)) {
this.buttons[button] = {
checkTime: -1,
isDown: false,
wasPressed: false,
wasReleased: false,
};
}
const result = this.buttons[button];
const b = this.gp.buttons[button];
if (b && result.checkTime !== this.checkTime) {
result.checkTime = this.checkTime;
const wasDown = result.isDown;
result.isDown = b.pressed;
result.wasPressed = result.isDown && !wasDown;
result.wasReleased = !result.isDown && wasDown;
}
return result;
}
}
@ -97,6 +112,7 @@ export class RunningEngine {
gamepadInput: FreeCameraGamepadInput;
xrCamera: WebXRCamera | null = null;
xrTeleportTimer: any = null;
leanBase: { position: Vector3 } | null = null;
recenterBase: { rotation: Quaternion } | null = null;
@ -257,29 +273,50 @@ export class RunningEngine {
checkGamepadInput(gp: Gamepad) {
const state = this.padStateFor(gp);
if (state.latch(9 /* options */)) location.reload();
if (state.latch(2 /* square */)) this.jump();
if (state.latch(3 /* triangle */)) this.turn180();
if (state.b(9 /* options */).wasPressed) location.reload();
if (state.b(2 /* square */).wasPressed) this.jump();
if (state.b(3 /* triangle */).wasPressed) this.turn180();
if (this.xrAvailable) {
if (state.latch(16 /* ps */)) this.xrToggle();
if (state.b(16 /* ps */).wasPressed) this.xrToggle();
}
if (this.inXR) {
this.xrUpdateLean(gp);
if (state.latch(0 /* cross */)) this.xrTeleport();
if (state.latch(1 /* circle */)) this.xrTouch();
if (state.latch(8 /* share */)) {
const actionButton = state.b(0 /* cross */);
if (actionButton.wasPressed) {
this.clearTeleportTimer();
this.xrTeleportTimer = setTimeout(() => {
this.clearTeleportTimer();
this.xrTeleport();
}, 333);
}
if (actionButton.wasReleased && this.xrTeleportTimer !== null) {
this.clearTeleportTimer();
this.xrStepOrTouch();
}
const shareButton = state.b(8 /* share */);
if (shareButton.wasPressed) {
this.recenterBase = {
rotation: this.xrCamera!.rotationQuaternion.clone(),
};
}
if (state.isDown(8 /* share */) && this.recenterBase) {
if (shareButton.isDown && this.recenterBase) {
this.xrCamera!.rotationQuaternion.copyFrom(
this.recenterBase.rotation);
}
}
state.tick();
}
clearTeleportTimer() {
if (this.xrTeleportTimer !== null) {
clearTimeout(this.xrTeleportTimer);
this.xrTeleportTimer = null;
}
}
checkKeys() {
@ -332,28 +369,35 @@ export class RunningEngine {
}
}
xrTouch() {
xrStepOrTouch() {
if (!this.inXR) return;
const ray = this.xrCamera!.getForwardRay();
const meshes = this.interactivity.touchableMeshes();
const hit = this.scene.pickWithRay(ray, m => meshes.indexOf(m as any) !== -1);
const hit = this.scene.pickWithRay(ray);
if (hit === null) return;
if (meshes.indexOf(hit.pickedMesh as any) === -1) return;
if (hit !== null
&& hit.distance <= 1
&& this.interactivity.touchableMeshes().indexOf(hit.pickedMesh as any) !== -1)
{
this.camera.onCollide?.(hit.pickedMesh!);
return;
}
this.camera.onCollide?.(hit.pickedMesh!);
const stepDistance =
hit && hit.hit && hit.distance <= 1 ? hit.distance * 0.5 : 1;
const pos = this.xrCamera!.position.add(ray.direction.normalizeToNew().scale(stepDistance));
this.xrCamera!.position = pos;
if (this.leanBase !== null) this.leanBase.position = pos;
}
xrTeleport() {
if (!this.inXR) return;
const ray = this.xrCamera!.getForwardRay();
const meshes = this.interactivity.floorMeshes();
const hit = this.scene.pickWithRay(ray, m => meshes.indexOf(m as any) !== -1);
const hit = this.scene.pickWithRay(ray);
if (hit === null) return;
if (meshes.indexOf(hit.pickedMesh as any) === -1) return;
if (hit.pickedPoint === null) return;
if (this.interactivity.floorMeshes().indexOf(hit.pickedMesh as any) === -1) return;
const pos = hit.pickedPoint.add(new Vector3(0, 1.6, 0));
this.xrCamera!.position = pos;
this.xrCamera!.rotation = Vector3.Zero();

View File

@ -4,6 +4,7 @@ import {
Assertable,
Assertion,
Dictionary,
Double,
Facet,
Ref,
Turn,
@ -36,7 +37,11 @@ export function logValues(values: Assertable[]) {
const d = new Dictionary<Ref>();
if (logId !== null) d.set(Symbol.for('pid'), logId);
if (logService !== null) d.set(Symbol.for('service'), logService);
d.set(Symbol.for('line'), values.map(v => typeof(v) === 'string' ? v : stringify(v)).join(' '));
d.set(Symbol.for('line'), values.map(v => {
if (typeof(v) === 'string') return v;
if (typeof(v) === 'number' && Math.round(v) !== v) return Double(v);
return stringify(v);
}).join(' '));
send message LogEntry((new Date()).toISOString(), d);
}
});