camera lock control
This commit is contained in:
parent
5850596481
commit
b58853af51
@ -1,6 +1,6 @@
|
||||
import { Socket } from 'socket.io-client';
|
||||
import { Color, DoubleSide, MeshBasicMaterial, Vector3 } from 'three';
|
||||
import { isMobileOrTablet } from '../common/helper';
|
||||
import { clamp, isMobileOrTablet } from '../common/helper';
|
||||
import { CharacterPacket, CompositePacket } from '../common/types/packet';
|
||||
import { IcyNetUser } from '../common/types/user';
|
||||
import { ThirdPersonCamera } from './object/camera';
|
||||
@ -30,6 +30,7 @@ export class Game {
|
||||
private _loading = LoadingManagerWrapper.getInstance();
|
||||
private character: CharacterPacket = {};
|
||||
private party: string[] = [];
|
||||
private _locked = false;
|
||||
|
||||
public world!: ClientWorld;
|
||||
public renderer = new Renderer();
|
||||
@ -149,6 +150,19 @@ export class Game {
|
||||
this.renderer.scene.add(flowers);
|
||||
this.renderer.scene.add(flowers2);
|
||||
this._loading.isConnecting();
|
||||
|
||||
window.addEventListener('keyup', (ev) => {
|
||||
if (ev.key === 'Shift') {
|
||||
this.toggleCamLock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public toggleCamLock(): boolean {
|
||||
this._locked = !this._locked;
|
||||
this.thirdPersonCamera?.setLock(this._locked);
|
||||
this.player?.setCameraLock(this._locked);
|
||||
return this._locked;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
@ -207,9 +221,18 @@ export class Game {
|
||||
this.renderer.canvas,
|
||||
);
|
||||
this.thirdPersonCamera.initialize();
|
||||
this.thirdPersonCamera.registerAltMoveFunction((x, y) => {
|
||||
this.player.angularVelocity.set(
|
||||
0,
|
||||
clamp(x * 0.5, -Math.PI, Math.PI),
|
||||
0,
|
||||
);
|
||||
});
|
||||
|
||||
this.joystick = new Joystick(player);
|
||||
this.joystick.initialize();
|
||||
this.joystick.addButton(-60, -20, 'LOCK', () => this.toggleCamLock());
|
||||
this.joystick.addButton(135, -20, 'JUMP', () => this.player.jump());
|
||||
|
||||
if (isMobileOrTablet()) {
|
||||
this.joystick.show();
|
||||
|
@ -21,8 +21,11 @@ export class ThirdPersonCamera {
|
||||
|
||||
private panning = false;
|
||||
private pinching = false;
|
||||
private locked = false;
|
||||
private previousPinchLength = 0;
|
||||
|
||||
private _moveFn?: (x: number, y: number) => void;
|
||||
|
||||
constructor(
|
||||
private camera: PerspectiveCamera,
|
||||
private target: Object3D,
|
||||
@ -32,10 +35,16 @@ export class ThirdPersonCamera {
|
||||
private dragEvent = (x: number, y: number) => {
|
||||
this.prevMousePos.copy(this.mousePos);
|
||||
this.mousePos = this.mousePos.fromArray([x, y]);
|
||||
const offset = this.prevMousePos.clone().sub(this.mousePos);
|
||||
|
||||
if (this.locked) {
|
||||
if (this._moveFn && this.panning) {
|
||||
this._moveFn(offset.x, offset.y);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.panning) {
|
||||
const offset = this.prevMousePos.clone().sub(this.mousePos);
|
||||
|
||||
this.angleAroundPlayer =
|
||||
this.angleAroundPlayer + ((offset.x * 0.3) % 360);
|
||||
this.pitch = clamp(this.pitch + offset.y * 0.3, -90, 90);
|
||||
@ -52,12 +61,21 @@ export class ThirdPersonCamera {
|
||||
}
|
||||
},
|
||||
mouseup: (e: MouseEvent) => {
|
||||
const wasPanning = this.panning === true;
|
||||
if (e.button === 2) {
|
||||
this.panning = false;
|
||||
}
|
||||
|
||||
if (wasPanning && this.locked && this._moveFn && !this.panning) {
|
||||
this._moveFn(0, 0);
|
||||
}
|
||||
},
|
||||
mouseleave: (e: MouseEvent) => {
|
||||
const wasPanning = this.panning === true;
|
||||
this.panning = false;
|
||||
if (wasPanning && this.locked && this._moveFn && !this.panning) {
|
||||
this._moveFn(0, 0);
|
||||
}
|
||||
},
|
||||
mousemove: (e: MouseEvent) => this.dragEvent(e.clientX, e.clientY),
|
||||
wheel: (e: WheelEvent) => {
|
||||
@ -98,9 +116,14 @@ export class ThirdPersonCamera {
|
||||
}
|
||||
},
|
||||
touchend: (ev: TouchEvent) => {
|
||||
const wasPanning = this.panning === true;
|
||||
this.pinching = false;
|
||||
this.panning = false;
|
||||
this.previousPinchLength = 0;
|
||||
|
||||
if (wasPanning && this.locked && this._moveFn && !this.panning) {
|
||||
this._moveFn(0, 0);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@ -131,6 +154,14 @@ export class ThirdPersonCamera {
|
||||
this.camera.lookAt(this.currentLookAt);
|
||||
}
|
||||
|
||||
public setLock(isLocked: boolean) {
|
||||
this.locked = isLocked;
|
||||
}
|
||||
|
||||
public registerAltMoveFunction(fn: (x: number, y: number) => void) {
|
||||
this._moveFn = fn;
|
||||
}
|
||||
|
||||
private calculateCameraOffset() {
|
||||
const hdist = this.distance * Math.cos(deg2rad(this.pitch));
|
||||
const vdist = this.distance * Math.sin(deg2rad(this.pitch));
|
||||
|
@ -3,6 +3,7 @@ import { Player } from './player';
|
||||
|
||||
export class Joystick {
|
||||
public element = document.createElement('div');
|
||||
private _inner = document.createElement('div');
|
||||
private knob = document.createElement('div');
|
||||
|
||||
private mousePos = new Vector2();
|
||||
@ -20,19 +21,21 @@ export class Joystick {
|
||||
constructor(private player: Player, public radius = 60) {}
|
||||
|
||||
initialize() {
|
||||
this.element.classList.add('joystick', 'joystick__wrapper');
|
||||
this.element.classList.add('joystick__wrapper');
|
||||
this._inner.classList.add('joystick');
|
||||
this.knob.classList.add('joystick__knob');
|
||||
this.element.append(this.knob);
|
||||
this._inner.append(this.knob);
|
||||
this.element.append(this._inner);
|
||||
document.body.append(this.element);
|
||||
|
||||
this.element.addEventListener('touchstart', (e) => {
|
||||
this._inner.addEventListener('touchstart', (e) => {
|
||||
e.preventDefault();
|
||||
const touch = e.touches[0] || e.changedTouches[0];
|
||||
this.mousePos.fromArray([touch.pageX, touch.pageY]);
|
||||
this.dragging = true;
|
||||
});
|
||||
|
||||
this.element.addEventListener('touchmove', (e) => {
|
||||
this._inner.addEventListener('touchmove', (e) => {
|
||||
e.preventDefault();
|
||||
if (this.dragging) {
|
||||
this.prevMousePos.copy(this.mousePos);
|
||||
@ -49,7 +52,7 @@ export class Joystick {
|
||||
}
|
||||
});
|
||||
|
||||
this.element.addEventListener('touchend', (e) => {
|
||||
this._inner.addEventListener('touchend', (e) => {
|
||||
this.dragging = false;
|
||||
this.centerKnob();
|
||||
});
|
||||
@ -93,6 +96,32 @@ export class Joystick {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
public addButton(
|
||||
x: number,
|
||||
y: number,
|
||||
text: string,
|
||||
toggle: () => void | boolean,
|
||||
) {
|
||||
const btn = document.createElement('button');
|
||||
btn.innerText = text;
|
||||
btn.style.setProperty('--button-x', `${x}px`);
|
||||
btn.style.setProperty('--button-y', `${y}px`);
|
||||
btn.classList.add('joystick__button');
|
||||
btn.addEventListener('click', (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const value = toggle();
|
||||
if (typeof value === 'boolean') {
|
||||
if (value) {
|
||||
btn.classList.add('joystick__button--active');
|
||||
} else {
|
||||
btn.classList.remove('joystick__button--active');
|
||||
}
|
||||
}
|
||||
});
|
||||
this.element.append(btn);
|
||||
}
|
||||
|
||||
private _windowEvent() {
|
||||
this.reset();
|
||||
}
|
||||
|
@ -38,6 +38,11 @@ export class Player extends PonyEntity {
|
||||
*/
|
||||
private _wasTurning = false;
|
||||
|
||||
/**
|
||||
* When true, left and right movement will not rotate, instead will move.
|
||||
*/
|
||||
private _cameraLock = false;
|
||||
|
||||
constructor(public user: IcyNetUser) {
|
||||
super();
|
||||
}
|
||||
@ -84,7 +89,7 @@ export class Player extends PonyEntity {
|
||||
vector.copy(this._direction);
|
||||
}
|
||||
|
||||
if (vector.x !== 0) {
|
||||
if (vector.x !== 0 && !this._cameraLock) {
|
||||
this.angularVelocity.set(0, Math.PI * vector.x, 0);
|
||||
this.changes.angular = this.angularVelocity.toArray();
|
||||
|
||||
@ -96,10 +101,20 @@ export class Player extends PonyEntity {
|
||||
this.changes.rotation = this.container.rotation.toArray();
|
||||
}
|
||||
|
||||
if (vector.y !== 0) {
|
||||
if (vector.y !== 0 || (this._cameraLock && vector.x !== 0)) {
|
||||
const directional = this._lookVector
|
||||
.clone()
|
||||
.multiplyScalar(vector.y * 2.5);
|
||||
|
||||
if (this._cameraLock && vector.x !== 0) {
|
||||
const sideways = new Vector3();
|
||||
sideways.copy(this._lookVector);
|
||||
sideways.applyAxisAngle(new Vector3(0, 1, 0), Math.PI / 2);
|
||||
sideways.normalize();
|
||||
sideways.multiplyScalar(vector.x * 2.5);
|
||||
directional.add(sideways);
|
||||
}
|
||||
|
||||
this.velocity.set(directional.x, this.velocity.y, directional.z);
|
||||
this.changes.velocity = this.velocity.toArray();
|
||||
|
||||
@ -145,4 +160,8 @@ export class Player extends PonyEntity {
|
||||
|
||||
this._prevKeydownMap = { ...this.keydownMap };
|
||||
}
|
||||
|
||||
public setCameraLock(isLocked: boolean) {
|
||||
this._cameraLock = isLocked;
|
||||
}
|
||||
}
|
||||
|
@ -14,17 +14,20 @@ body {
|
||||
}
|
||||
|
||||
.joystick {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
bottom: 10px;
|
||||
left: calc(50% - var(--size) / 2);
|
||||
background-color: #e7e7e7b3;
|
||||
border-radius: 100%;
|
||||
border: 2px solid #ddd;
|
||||
z-index: 1000;
|
||||
|
||||
&__wrapper {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: calc(50% - var(--size) / 2);
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
&__knob {
|
||||
width: calc(var(--size) / 2);
|
||||
height: calc(var(--size) / 2);
|
||||
@ -35,6 +38,32 @@ body {
|
||||
cursor:grab;
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
&__button {
|
||||
position: absolute;
|
||||
appearance: none;
|
||||
left: var(--button-x);
|
||||
top: var(--button-y);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 100%;
|
||||
background-color: rgba(231, 231, 231, 0.7019607843);
|
||||
border: 2px solid #ddd;
|
||||
z-index: 1001;
|
||||
color: #5c5c5c;
|
||||
font-weight: bold;
|
||||
transition: border 250ms linear, background-color 250ms linear;
|
||||
|
||||
&--active {
|
||||
border-width: 4px;
|
||||
border-color: #db8f00;
|
||||
background-color: rgb(231 231 231 / 78%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat {
|
||||
|
Loading…
Reference in New Issue
Block a user