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