-
-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(app)!: 227 Change the keyboardcontrols implementation
- Loading branch information
1 parent
f10476e
commit fed0f86
Showing
4 changed files
with
132 additions
and
175 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<script setup lang="ts"> | ||
import { TresCanvas } from '@tresjs/core' | ||
import { BasicShadowMap, NoToneMapping, Vector3 } from 'three' | ||
import { PointerLockControls, KeyboardControls, StatsGl, Sky, Box } from '@tresjs/cientos' | ||
const gl = { | ||
clearColor: '#82DBC5', | ||
shadows: true, | ||
alpha: false, | ||
shadowMapType: BasicShadowMap, | ||
toneMapping: NoToneMapping, | ||
} | ||
const isActive = (state: boolean) => console.log(state) | ||
const hasChange = (state: any) => console.log('change', state) | ||
</script> | ||
|
||
<template> | ||
<TresCanvas v-bind="gl"> | ||
<TresPerspectiveCamera :position="[0, 3, 10]" /> | ||
<StatsGl /> | ||
<Sky /> | ||
<KeyboardControls | ||
@change="state => hasChange(state)" | ||
@is-lock="state => isActive(state)" | ||
/> | ||
<Box /> | ||
<Box :position="[15, 0, 0]" /> | ||
<Box :position="[-15, 0, 0]" /> | ||
|
||
<TresAxesHelper | ||
:args="[10]" | ||
:position-y="5" | ||
/> | ||
<TresGridHelper :args="[100, 100]" /> | ||
<TresAmbientLight :intensity="1" /> | ||
</TresCanvas> | ||
</template> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,208 +1,122 @@ | ||
<script setup lang="ts"> | ||
import { ref, shallowRef, toRefs } from 'vue' | ||
import { ref, toRefs, watchEffect } from 'vue' | ||
import { useRenderLoop, useTresContext } from '@tresjs/core' | ||
import { PointerLockControls } from 'three-stdlib' | ||
import { onKeyStroke } from '@vueuse/core' | ||
import { useMagicKeys } from '@vueuse/core' | ||
import { PointerLockControls as PointerLockControlsType } from 'three-stdlib' | ||
import { Vector3, Quaternion } from 'three' | ||
import type { Camera } from 'three' | ||
import { PointerLockControls } from './index' | ||
export interface KeyboardControlsProps { | ||
/** | ||
* Keys to go forward. | ||
* @type {string[]} | ||
* @default '[w, W]' | ||
* @memberof KeyboardControlsProps | ||
* Whether to make this the default controls. | ||
* | ||
**/ | ||
forward?: string[] | string | ||
/** | ||
* Keys to go back. | ||
* @type {string[]} | ||
* @default '[s, S]' | ||
* @default true | ||
* @type {boolean} | ||
* @memberof KeyboardControlsProps | ||
* | ||
**/ | ||
back?: string[] | string | ||
* @see https://threejs.org/docs/index.html?q=pointe#examples/en/controls/PointerLockControls | ||
*/ | ||
makeDefault?: boolean | ||
/** | ||
* Keys to go left. | ||
* @type {string[]} | ||
* @default '[a, A]' | ||
* @memberof KeyboardControlsProps | ||
* The camera to control. | ||
* | ||
**/ | ||
left?: string[] | string | ||
/** | ||
* Keys to go right. | ||
* @type {string[]} | ||
* @default '[d, D]' | ||
* @default false | ||
* @type {boolean} | ||
* @memberof KeyboardControlsProps | ||
* | ||
**/ | ||
right?: string[] | string | ||
* @see https://threejs.org/docs/index.html?q=pointe#examples/en/controls/PointerLockControls | ||
*/ | ||
camera?: Camera | ||
/** | ||
* Key to jump (only with PointerLockControls). | ||
* @type {string[]} | ||
* @default 'space' | ||
* @memberof KeyboardControlsProps | ||
* The dom element to listen to. | ||
* | ||
**/ | ||
jump?: string[] | string | ||
/** | ||
* Default gravity number for jump. | ||
* @type {number} | ||
* @default 9.8 | ||
* @type {HTMLElement} | ||
* @memberof KeyboardControlsProps | ||
* | ||
**/ | ||
gravity?: number | ||
* @see https://threejs.org/docs/index.html?q=pointe#examples/en/controls/PointerLockControls | ||
*/ | ||
domElement?: HTMLElement | ||
/** | ||
* Speed movement. | ||
* Indicates the movement speed. | ||
* @type {number} | ||
* @default 0.1 | ||
* @default 0.25 | ||
* @memberof KeyboardControlsProps | ||
* | ||
**/ | ||
moveSpeed?: number | ||
/** | ||
* Activate/deactivate headBobbing effect (only with PointerLockControls). | ||
* @type {boolean} | ||
* @default false | ||
* @memberof KeyboardControlsProps | ||
* The trigger id. | ||
* | ||
**/ | ||
headBobbing?: boolean | ||
/** | ||
* Indicates if the forward movement is in the Z axis or Y axis. | ||
* @type {boolean} | ||
* @default false | ||
* @type {string} | ||
* @memberof KeyboardControlsProps | ||
* | ||
**/ | ||
is2D?: boolean | ||
* @see https://threejs.org/docs/index.html?q=pointe#examples/en/controls/PointerLockControls | ||
*/ | ||
selector?: string | ||
} | ||
// TODO: remove disable once eslint is updated to support vue 3.3 | ||
// eslint-disable-next-line vue/no-setup-props-destructure | ||
const props = withDefaults(defineProps<KeyboardControlsProps>(), { | ||
forward: () => ['w', 'W'], | ||
back: () => ['s', 'S'], | ||
left: () => ['a', 'A'], | ||
right: () => ['d', 'D'], | ||
jump: () => [' '], | ||
gravity: 9.8, | ||
moveSpeed: 0.1, | ||
headBobbing: false, | ||
is2D: false, | ||
moveSpeed: 0.25, | ||
makeDefault: true, | ||
}) | ||
const emit = defineEmits(['isLock', 'change']) | ||
const { moveSpeed } = toRefs(props) | ||
const { camera: activeCamera, controls, renderer } = useTresContext() | ||
const sidewardMove = ref(0) | ||
const forwardMove = ref(0) | ||
const { w, s, a, d } = useMagicKeys() | ||
watchEffect(() => { | ||
if (a.value) sidewardMove.value = -moveSpeed.value | ||
else if (d.value) sidewardMove.value = moveSpeed.value | ||
else sidewardMove.value = 0 | ||
if (w.value) forwardMove.value = moveSpeed.value | ||
else if (s.value) forwardMove.value = -moveSpeed.value | ||
else forwardMove.value = 0 | ||
}) | ||
const { forward, back, left, right, jump, gravity, moveSpeed, headBobbing, is2D } = toRefs(props) | ||
const { camera: activeCamera, controls } = useTresContext() | ||
const xMove = ref(0) | ||
const zMove = ref(0) | ||
const isHeadBobbing = ref(false) | ||
const isJumping = ref(false) | ||
const HBSpeed = 5 | ||
const jumpSpeed = 6 | ||
const HBAmplitude = 0.3 | ||
const initJumpTime = ref(0) | ||
const wrapperRef = shallowRef() | ||
const _forward = is2D.value ? 'y' : 'z' | ||
const initCameraPos = activeCamera.value.position?.y || 0 | ||
// FORWARD DIRECTION MOVEMENTS | ||
onKeyStroke( | ||
forward.value, | ||
() => { | ||
isHeadBobbing.value = true | ||
zMove.value = moveSpeed.value | ||
}, | ||
{ eventName: 'keydown' }, | ||
) | ||
onKeyStroke( | ||
back.value, | ||
() => { | ||
isHeadBobbing.value = true | ||
zMove.value = -moveSpeed.value | ||
}, | ||
{ eventName: 'keydown' }, | ||
) | ||
onKeyStroke( | ||
[...forward.value, ...back.value], | ||
() => { | ||
isHeadBobbing.value = false | ||
zMove.value = 0 | ||
}, | ||
{ eventName: 'keyup' }, | ||
) | ||
// X DIRECTION MOVEMENTS | ||
onKeyStroke( | ||
left.value, | ||
() => { | ||
isHeadBobbing.value = true | ||
xMove.value = -moveSpeed.value | ||
}, | ||
{ eventName: 'keydown' }, | ||
) | ||
onKeyStroke( | ||
right.value, | ||
() => { | ||
isHeadBobbing.value = true | ||
xMove.value = moveSpeed.value | ||
}, | ||
{ eventName: 'keydown' }, | ||
) | ||
onKeyStroke( | ||
[...left.value, ...right.value], | ||
() => { | ||
isHeadBobbing.value = false | ||
xMove.value = 0 | ||
}, | ||
{ eventName: 'keyup' }, | ||
) | ||
//JUMP BUTTON | ||
onKeyStroke(jump.value, () => { | ||
if (!isJumping.value) initJumpTime.value = Date.now() | ||
isJumping.value = true | ||
defineExpose({ | ||
value: controls, | ||
}) | ||
// HEAD BOBBING | ||
const headBobbingMov = (elapsedTime: number) => isHeadBobbing.value | ||
? Math.sin(elapsedTime * HBSpeed) * HBAmplitude + initCameraPos | ||
: initCameraPos | ||
const isActive = (isLock: boolean) => emit('isLock', isLock) | ||
// JUMP | ||
const getJumpTime = () => ((Date.now() - initJumpTime.value) / 1000) * 3 | ||
const getJumpDistance = (jumpTime: number) => initCameraPos + jumpSpeed * jumpTime - 0.5 * gravity.value * jumpTime ** 2 | ||
const hasChange = (state: any) => emit('change', state) | ||
const getJump = () => { | ||
if (isJumping.value) { | ||
const jumpDistance = getJumpDistance(getJumpTime()) | ||
if (jumpDistance <= initCameraPos) isJumping.value = false | ||
return jumpDistance | ||
} | ||
return 0 | ||
const moveVector = new Vector3(0, 0, 0) | ||
const rotationVector = new Vector3(0, 0, 0) | ||
const tmpQuaternion = new Quaternion() | ||
const moveForward = (delta: number, movementSpeed: number) => { | ||
if (!activeCamera.value?.position && !moveVector) return | ||
const camera = activeCamera.value | ||
const rotMult = delta * 0.001 | ||
camera?.translateZ(-movementSpeed) | ||
tmpQuaternion.set(rotationVector.x * rotMult, rotationVector.y * rotMult, rotationVector.z * rotMult, 1).normalize() | ||
camera?.quaternion.multiply(tmpQuaternion) | ||
if (sidewardMove.value || forwardMove.value) emit('change', controls.value) | ||
} | ||
const { onLoop } = useRenderLoop() | ||
onLoop(({ elapsed }) => { | ||
// has PointerLockControls? | ||
if (controls.value instanceof PointerLockControls && controls.value?.isLocked) { | ||
controls.value.moveForward(zMove.value) | ||
controls.value.moveRight(xMove.value) | ||
if (activeCamera.value.position) { | ||
activeCamera.value.position.y = headBobbing.value ? headBobbingMov(elapsed) : initCameraPos | ||
activeCamera.value.position.y += getJump() | ||
} | ||
} | ||
else if (wrapperRef.value.children.length > 0 && !(controls.value instanceof PointerLockControls)) { | ||
wrapperRef.value.position.x += xMove.value | ||
wrapperRef.value.position[_forward] += is2D.value ? zMove.value : -zMove.value | ||
onLoop(({ delta }) => { | ||
if (controls.value instanceof PointerLockControlsType && controls.value?.isLocked) { | ||
moveForward(delta, forwardMove.value) | ||
controls.value.moveRight(sidewardMove.value) | ||
} | ||
}) | ||
</script> | ||
|
||
<template> | ||
<TresGroup ref="wrapperRef"> | ||
<slot /> | ||
</TresGroup> | ||
<PointerLockControls | ||
:selector="selector" | ||
:make-default="makeDefault" | ||
:camera="camera || activeCamera" | ||
:dom-element="domElement || renderer.domElement" | ||
@is-lock="isActive" | ||
@change="hasChange" | ||
/> | ||
</template> |