Skip to content

Commit

Permalink
feat: keyboard controls (#68)
Browse files Browse the repository at this point in the history
* feat: first draft keyboard controls needs review

* chore: add slots to keyboardcontrols

* chore: last details, add is2D prop

* docs: add docs

* chore: fix package

* chore: remove files

---------

Co-authored-by: alvarosabu <alvaro.saburido@gmail.com>
  • Loading branch information
JaimeTorrealba and alvarosabu authored Jun 24, 2023
1 parent d8682e1 commit 1b7a624
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 12 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default defineConfig({
{ text: 'OrbitControls', link: '/guide/controls/orbit-controls' },
{ text: 'TransformControls', link: '/guide/controls/transform-controls' },
{ text: 'PointerLockControls', link: '/guide/controls/pointer-lock-controls' },
{ text: 'KeyboardControls', link: '/guide/controls/keyboard-controls' },
{ text: 'MapControls', link: '/guide/controls/map-controls' },
],
},
Expand Down
61 changes: 61 additions & 0 deletions docs/guide/controls/keyboard-controls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# KeyboardControls

KeyboardControls is a special type of controller that allows you to move using your keyboard.

## In combination with PointerLockControls.

Here you need to use the PointerLockControls with make-default props, KeyboardControls will automatically adapt to it, creating a really good effect first player experiences.

```vue{3}
<template>
<TresCanvas shadows alpha>
<TresPerspectiveCamera :position="[0, 0, 3]" />
<PointerLockControls make-default />
<KeyboardControls />
<TresGridHelper :args="[10, 10]" />
</TresCanvas>
</template>
```

Just by doing this, works great but in addition the TresJs team give some special props (only available with this mode) to create more realistic sensation

- headBobbing: To simulate the walk balance of the head
- Jump: to jump
- gravity: to control the gravity related to the jump

## In combination with slots.

Here you can move meshes by using your keyboard, by default you move through the X and Z axis, but you can change it with the is2D prop, to move your meshes on the X and Y axis

```vue{3}
<template>
<TresCanvas shadows alpha>
<TresPerspectiveCamera :position="[0, 0, 3]" />
<KeyboardControls >
<Sphere>
<TresMeshNormalMaterial />
</Sphere>
</KeyboardControls>
</TresCanvas>
</template>
```

::: warning
Is really important that the Perspective camera is set first in the canvas. Otherwise might break.
:::

## Props

| Prop | Description | Default |
| :-------------- | :-------------------------------------------------------------------------------------------- | ---------- |
| **forward** | Keys to go forward. | ['w', 'W'] |
| **back** | Keys to go back. | ['s', 'S'] |
| **left** | Keys to go left. | ['a', 'A'] |
| **right** | Keys to go right. | ['d', 'D'] |
| **headBobbing** | Indicates if the forward movement is in the Z axis or Y axis. (only with PointerLockControls) | false |
| **jump** | Key to jump (only with PointerLockControls). | [' '] |
| **gravity** | Default gravity number for jump. | 9.8 |
| **moveSpeed** | Speed movement | 0.1 |
| **is2D** | Indicates if the forward movement is in the Z axis or Y axis. (only for meshes) | false |
19 changes: 10 additions & 9 deletions playground/src/components/PointerLockControlsDemo.vue
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
import { PointerLockControls, Sphere } from '@tresjs/cientos'
import { BasicShadowMap, SRGBColorSpace, NoToneMapping } from 'three'
import { PointerLockControls, Sphere, KeyboardControls } from '@tresjs/cientos'
import { BasicShadowMap, NoToneMapping } from 'three'
const gl = {
clearColor: '#82DBC5',
shadows: true,
alpha: false,
shadowMapType: BasicShadowMap,
outputColorSpace: SRGBColorSpace,
toneMapping: NoToneMapping,
}
</script>

<template>
<button id="lock">Lock</button>
<TresCanvas v-bind="gl">
<TresPerspectiveCamera :position="[3, 3, 3]" />
<PointerLockControls selector="lock" />
<Sphere>
<TresMeshNormalMaterial />
</Sphere>
<TresPerspectiveCamera :position="[0, 1, 10]" />
<PointerLockControls make-default />
<KeyboardControls head-bobbing >
</KeyboardControls>
<!-- <KeyboardControls forward="i" back="k" left="j" right="l">
</KeyboardControls> -->
<Sphere>
</Sphere>
<TresGridHelper :args="[10, 10]" />
<TresAmbientLight :intensity="1" />
</TresCanvas>
Expand Down
5 changes: 3 additions & 2 deletions playground/src/pages/index.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup lang="ts"></script>
<script setup lang="ts">
</script>
<template>
<Suspense>
<EnvironmentDemo />
<PointerLockControlsDemo />
</Suspense>
</template>
205 changes: 205 additions & 0 deletions src/core/controls/KeyboardControls.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
<script setup lang="ts">
import { ref, shallowRef } from 'vue'
import { useRenderLoop } from '@tresjs/core'
import { PointerLockControls } from 'three-stdlib'
import { onKeyStroke } from '@vueuse/core'
import { useCientos } from '../../core/useCientos'
export type KeyboardControlsProps = {
/**
* Keys to go forward.
* @type {string[]}
* @default '[w, W]'
* @memberof KeyboardControlsProps
*
**/
forward?: string[] | string
/**
* Keys to go back.
* @type {string[]}
* @default '[s, S]'
* @memberof KeyboardControlsProps
*
**/
back?: string[] | string
/**
* Keys to go left.
* @type {string[]}
* @default '[a, A]'
* @memberof KeyboardControlsProps
*
**/
left?: string[] | string
/**
* Keys to go right.
* @type {string[]}
* @default '[d, D]'
* @memberof KeyboardControlsProps
*
**/
right?: string[] | string
/**
* Key to jump (only with PointerLockControls).
* @type {string[]}
* @default 'space'
* @memberof KeyboardControlsProps
*
**/
jump?: string[] | string
/**
* Default gravity number for jump.
* @type {number}
* @default 9.8
* @memberof KeyboardControlsProps
*
**/
gravity?: number
/**
* Speed movement.
* @type {number}
* @default 0.1
* @memberof KeyboardControlsProps
*
**/
moveSpeed?: number
/**
* Activate/deactivate headBobbing effect (only with PointerLockControls).
* @type {boolean}
* @default false
* @memberof KeyboardControlsProps
*
**/
headBobbing?: boolean
/**
* Indicates if the forward movement is in the Z axis or Y axis.
* @type {boolean}
* @default false
* @memberof KeyboardControlsProps
*
**/
is2D?: boolean
}
// TODO: remove disable once eslint is updated to support vue 3.3
// eslint-disable-next-line vue/no-setup-props-destructure
const {
forward = ['w', 'W'],
back = ['s', 'S'],
left = ['a', 'A'],
right = ['d', 'D'],
jump = [' '],
gravity = 9.8,
moveSpeed = 0.1,
headBobbing = false,
is2D = false,
} = defineProps<KeyboardControlsProps>()
const { state } = useCientos()
const initCameraPos: number = state?.camera?.position.y || 0
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 ? 'y' : 'z'
// FORWARD DIRECTION MOVEMENTS
onKeyStroke(
forward,
() => {
isHeadBobbing.value = true
zMove.value = moveSpeed
},
{ eventName: 'keydown' },
)
onKeyStroke(
back,
() => {
isHeadBobbing.value = true
zMove.value = -moveSpeed
},
{ eventName: 'keydown' },
)
onKeyStroke(
[...forward, ...back],
() => {
isHeadBobbing.value = false
zMove.value = 0
},
{ eventName: 'keyup' },
)
// X DIRECTION MOVEMENTS
onKeyStroke(
left,
() => {
isHeadBobbing.value = true
xMove.value = -moveSpeed
},
{ eventName: 'keydown' },
)
onKeyStroke(
right,
() => {
isHeadBobbing.value = true
xMove.value = moveSpeed
},
{ eventName: 'keydown' },
)
onKeyStroke(
[...left, ...right],
() => {
isHeadBobbing.value = false
xMove.value = 0
},
{ eventName: 'keyup' },
)
//JUMP BUTTON
onKeyStroke(jump, () => {
if (!isJumping.value) initJumpTime.value = Date.now()
isJumping.value = true
})
// HEAD BOBBING
const headBobbingMov = (elapsedTime: number) => {
return isHeadBobbing.value ? Math.sin(elapsedTime * HBSpeed) * HBAmplitude + initCameraPos : initCameraPos
}
// JUMP
const getJumpTime = () => ((Date.now() - initJumpTime.value) / 1000) * 3
const getJumpDistance = (jumpTime: number) => {
return initCameraPos + jumpSpeed * jumpTime - 0.5 * gravity * Math.pow(jumpTime, 2)
}
const getJump = () => {
if (isJumping.value) {
const jumpDistance = getJumpDistance(getJumpTime())
if (jumpDistance <= initCameraPos) isJumping.value = false
return jumpDistance
}
return 0
}
const { onLoop } = useRenderLoop()
onLoop(({ elapsed }) => {
// has PointerLockControls?
if (state.controls instanceof PointerLockControls && state?.controls?.isLocked) {
state.controls.moveForward(zMove.value)
state.controls.moveRight(xMove.value)
if (state.camera?.position) {
state.camera.position.y = headBobbing ? headBobbingMov(elapsed) : initCameraPos
state.camera.position.y += getJump()
}
} else if (wrapperRef.value.children.length > 0 && !(state.controls instanceof PointerLockControls)) {
wrapperRef.value.position.x += xMove.value
wrapperRef.value.position[_forward] += is2D ? zMove.value : -zMove.value
}
})
</script>
<template>
<TresGroup ref="wrapperRef">
<slot />
</TresGroup>
</template>
3 changes: 2 additions & 1 deletion src/core/controls/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import OrbitControls from './OrbitControls.vue'
import KeyboardControls from './KeyboardControls.vue'
import TransformControls from './TransformControls.vue'
import PointerLockControls from './PointerLockControls.vue'
import MapControls from './MapControls.vue'

export { OrbitControls, TransformControls, PointerLockControls, MapControls }
export { OrbitControls, TransformControls, PointerLockControls, MapControls, KeyboardControls }
Binary file removed tresjs-cientos-2.0.0.tgz
Binary file not shown.

0 comments on commit 1b7a624

Please sign in to comment.