parent
a143c7b1e3
commit
0b2fb528b2
Binary file not shown.
Binary file not shown.
@ -1,231 +0,0 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react'
|
|
||||||
import { useThree, useFrame } from 'react-three-fiber'
|
|
||||||
import {
|
|
||||||
Object3D,
|
|
||||||
Geometry,
|
|
||||||
Event,
|
|
||||||
Group,
|
|
||||||
PerspectiveCamera,
|
|
||||||
Vector3,
|
|
||||||
Raycaster,
|
|
||||||
Mesh,
|
|
||||||
CircleGeometry,
|
|
||||||
} from 'three'
|
|
||||||
import WorldCollisions from './models/WorldCollisions'
|
|
||||||
|
|
||||||
import useStore from '../store'
|
|
||||||
import { MeshBasicMaterial } from 'three/src/materials/MeshBasicMaterial'
|
|
||||||
|
|
||||||
const SPEED = 1
|
|
||||||
const HEIGHT = 1.5
|
|
||||||
const CIRCLE_RADIUS = 0.9
|
|
||||||
const CIRCLE_SEGMENTS = 8
|
|
||||||
|
|
||||||
const keys: Record<string, string> = {
|
|
||||||
KeyW: 'forward',
|
|
||||||
KeyS: 'backward',
|
|
||||||
KeyA: 'left',
|
|
||||||
KeyD: 'right',
|
|
||||||
ArrowUp: 'forward',
|
|
||||||
ArrowDown: 'backward',
|
|
||||||
ArrowLeft: 'left',
|
|
||||||
ArrowRight: 'right',
|
|
||||||
ShiftLeft: 'run',
|
|
||||||
Space: 'jump',
|
|
||||||
}
|
|
||||||
const moveFieldByKey = (key: string) => keys[key]
|
|
||||||
|
|
||||||
function FirstPersonCamera(props: JSX.IntrinsicElements['perspectiveCamera']) {
|
|
||||||
const ref = useRef<PerspectiveCamera>()
|
|
||||||
const { setDefaultCamera } = useThree()
|
|
||||||
// Make the camera known to the system
|
|
||||||
useEffect(() => {
|
|
||||||
if (ref.current) setDefaultCamera(ref.current)
|
|
||||||
}, [ref.current])
|
|
||||||
// Update it every frame
|
|
||||||
useFrame(() => ref.current?.updateMatrixWorld())
|
|
||||||
return <perspectiveCamera ref={ref} {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
const usePlayerControls = () => {
|
|
||||||
const [movement, setMovement] = useState({
|
|
||||||
forward: false,
|
|
||||||
backward: false,
|
|
||||||
left: false,
|
|
||||||
right: false,
|
|
||||||
run: false,
|
|
||||||
jump: false,
|
|
||||||
})
|
|
||||||
useEffect(() => {
|
|
||||||
const handleKeyDown = (e: Event) => {
|
|
||||||
setMovement((m) => ({ ...m, [moveFieldByKey(e.code)]: true }))
|
|
||||||
}
|
|
||||||
const handleKeyUp = (e: Event) => {
|
|
||||||
setMovement((m) => ({ ...m, [moveFieldByKey(e.code)]: false }))
|
|
||||||
}
|
|
||||||
document.addEventListener('keydown', handleKeyDown)
|
|
||||||
document.addEventListener('keyup', handleKeyUp)
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('keydown', handleKeyDown)
|
|
||||||
document.removeEventListener('keyup', handleKeyUp)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
return movement
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Improve physics in player
|
|
||||||
const Player = () => {
|
|
||||||
const socket = useStore((state) => state.socket)
|
|
||||||
const pointerLocked = useStore((state) => state.pointerLocked)
|
|
||||||
const { forward, backward, left, right, run } = usePlayerControls()
|
|
||||||
const { camera } = useThree()
|
|
||||||
const groupRef = useRef<Group>()
|
|
||||||
const collisionsRef = useRef<Mesh<CircleGeometry, MeshBasicMaterial>>(null)
|
|
||||||
|
|
||||||
const bottomRaycaster = useRef(
|
|
||||||
new Raycaster(new Vector3(), new Vector3(0, -1, 0), 0, HEIGHT + 0.5),
|
|
||||||
)
|
|
||||||
|
|
||||||
const collisionCircle = useRef<Mesh>()
|
|
||||||
const rotationObject = new Object3D()
|
|
||||||
rotationObject.rotation.x = Math.PI / 2
|
|
||||||
rotationObject.updateMatrix()
|
|
||||||
const collisionCircleGeometry = new CircleGeometry(CIRCLE_RADIUS, CIRCLE_SEGMENTS)
|
|
||||||
collisionCircleGeometry.applyMatrix4(rotationObject.matrix)
|
|
||||||
const collisionCircleMaterial = new MeshBasicMaterial({
|
|
||||||
color: 0xff0000,
|
|
||||||
wireframe: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const wallRaycasters = useRef<Raycaster[]>(
|
|
||||||
Array(CIRCLE_SEGMENTS)
|
|
||||||
.fill(undefined)
|
|
||||||
.map(() => new Raycaster(new Vector3(), new Vector3(0, 0, -1), 0, CIRCLE_RADIUS)),
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const socketEmitTransformInterval = setInterval(() => {
|
|
||||||
if (socket && groupRef.current && camera) {
|
|
||||||
const cameraRotation = camera.rotation.toArray()
|
|
||||||
socket.emit('transform', {
|
|
||||||
position: [
|
|
||||||
groupRef.current?.position.x,
|
|
||||||
groupRef.current?.position.y,
|
|
||||||
groupRef.current?.position.z,
|
|
||||||
],
|
|
||||||
rotation: [cameraRotation[0], cameraRotation[1], cameraRotation[2]],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, 50)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearInterval(socketEmitTransformInterval)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const velocity = useRef<Vector3>(new Vector3())
|
|
||||||
const direction = useRef<Vector3>(new Vector3())
|
|
||||||
const worldOrigin = useRef<Vector3>(new Vector3())
|
|
||||||
|
|
||||||
const moveForward = (distance: number) => {
|
|
||||||
if (groupRef.current) {
|
|
||||||
worldOrigin.current.setFromMatrixColumn(camera.matrix, 0)
|
|
||||||
worldOrigin.current.crossVectors(camera.up, worldOrigin.current)
|
|
||||||
groupRef.current.position.addScaledVector(worldOrigin.current, distance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const moveRight = (distance: number) => {
|
|
||||||
if (groupRef.current) {
|
|
||||||
worldOrigin.current.setFromMatrixColumn(camera.matrix, 0)
|
|
||||||
groupRef.current.position.addScaledVector(worldOrigin.current, distance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useFrame((_, delta) => {
|
|
||||||
if (!groupRef.current || !collisionsRef.current || !collisionCircle.current) return
|
|
||||||
|
|
||||||
if (pointerLocked) {
|
|
||||||
// Slowdown
|
|
||||||
velocity.current.x -= velocity.current.x * 10.0 * delta
|
|
||||||
velocity.current.z -= velocity.current.z * 10.0 * delta
|
|
||||||
|
|
||||||
// Fall
|
|
||||||
bottomRaycaster.current.ray.origin.copy(groupRef.current.position)
|
|
||||||
|
|
||||||
let intersections = []
|
|
||||||
intersections = bottomRaycaster.current.intersectObject(
|
|
||||||
collisionsRef.current,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (intersections.length < 1) {
|
|
||||||
velocity.current.y -= 9.8 * 10 * delta
|
|
||||||
} else {
|
|
||||||
velocity.current.y = 0
|
|
||||||
groupRef.current.position.y = intersections[0].point.y + HEIGHT
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direction
|
|
||||||
direction.current.z = Number(forward) - Number(backward)
|
|
||||||
direction.current.x = Number(left) - Number(right)
|
|
||||||
direction.current.normalize()
|
|
||||||
|
|
||||||
// Running
|
|
||||||
const speed = run ? 1.5 : 1
|
|
||||||
|
|
||||||
// Move
|
|
||||||
if (forward || backward)
|
|
||||||
velocity.current.z -= direction.current.z * SPEED * delta * speed
|
|
||||||
if (left || right) velocity.current.x -= direction.current.x * SPEED * delta * speed
|
|
||||||
|
|
||||||
// Wall collisions
|
|
||||||
|
|
||||||
const collisionCircleGeometry = collisionCircle.current.geometry as Geometry
|
|
||||||
|
|
||||||
if (collisionCircleGeometry.vertices) {
|
|
||||||
for (let i = 0; i < CIRCLE_SEGMENTS; i++) {
|
|
||||||
const localVertex = collisionCircleGeometry.vertices[i + 1].clone()
|
|
||||||
console.log(i + 1, localVertex)
|
|
||||||
const globalVertex = localVertex.applyMatrix4(groupRef.current.matrix)
|
|
||||||
const directionVector = globalVertex.sub(collisionCircle.current.position)
|
|
||||||
|
|
||||||
wallRaycasters.current[i].ray.origin.copy(collisionCircle.current.position)
|
|
||||||
wallRaycasters.current[i].ray.direction.copy(directionVector.normalize())
|
|
||||||
|
|
||||||
let wallIntersections = []
|
|
||||||
wallIntersections = bottomRaycaster.current.intersectObject(
|
|
||||||
collisionsRef.current,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
if (wallIntersections.length > 1) console.log(`${i} Hit`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply speed
|
|
||||||
moveForward(-velocity.current.x * speed)
|
|
||||||
moveRight(-velocity.current.z * speed)
|
|
||||||
groupRef.current.position.y += velocity.current.y * delta
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO enable jump if cheating
|
|
||||||
//if (jump && Math.abs(parseFloat(velocity.current[1].toFixed(2))) < 0.05)
|
|
||||||
// api.velocity.set(velocity.current[0], 10, velocity.current[2])
|
|
||||||
})
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<group ref={groupRef} position={[0, 5, 0]} rotation={[0, -Math.PI / 2, 0]}>
|
|
||||||
<FirstPersonCamera rotation={[0, Math.PI / 2, 0]} />
|
|
||||||
<mesh
|
|
||||||
ref={collisionCircle}
|
|
||||||
position={[0, -HEIGHT / 2, 0]}
|
|
||||||
geometry={collisionCircleGeometry}
|
|
||||||
material={collisionCircleMaterial}
|
|
||||||
/>
|
|
||||||
</group>
|
|
||||||
<WorldCollisions ref={collisionsRef} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Player
|
|
Loading…
Reference in new issue