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