aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorLibravatar Silas Bartha <silas@exvacuum.dev>2025-02-10 01:29:10 -0500
committerLibravatar Silas Bartha <silas@exvacuum.dev>2025-02-10 01:29:10 -0500
commit55a0a3de6550f0142b79ab40645c20b465ddded8 (patch)
treecc767e6442ad71d7be782b0890726528107a41f0 /src/components
parent848d2b02064e2454edb85cd016e7b6840ab77e36 (diff)
first person controls
Diffstat (limited to 'src/components')
-rw-r--r--src/components/chatbubble.jsx42
-rw-r--r--src/components/ground.jsx17
-rw-r--r--src/components/notes.jsx14
-rw-r--r--src/components/player.jsx78
4 files changed, 143 insertions, 8 deletions
diff --git a/src/components/chatbubble.jsx b/src/components/chatbubble.jsx
index b3f4ad5..a1ef114 100644
--- a/src/components/chatbubble.jsx
+++ b/src/components/chatbubble.jsx
@@ -1,25 +1,51 @@
-import { useRef, useState } from 'react'
+import { useContext, useRef, useState } from 'react'
import * as everforest from '../_everforest.module.scss'
import { useGLTF } from '@react-three/drei';
-import { useFrame } from '@react-three/fiber';
+import { useFrame, useThree } from '@react-three/fiber';
+import { Html } from '@react-three/drei';
+import { AppContext } from '../App';
+import Color from 'color';
+import { Vector3 } from 'three';
-export default function ChatBubble(props) {
+export default function ChatBubble({ position, text }) {
const meshRef = useRef();
const [hovered, setHovered] = useState(false);
+ const [activatable, setActivatable] = useState(false);
const [active, setActive] = useState(false);
- useFrame((_, delta) => (meshRef.current.rotation.y += delta));
- const {nodes} = useGLTF('../assets/message-bubble.glb');
+ const { keysPressed } = useContext(AppContext);
+ const { camera } = useThree();
+ useFrame((_, delta) => {
+ if (active) {
+ meshRef.current.rotation.y += delta;
+ }
+ if(hovered) {
+ let cameraPos = new Vector3();
+ camera.getWorldPosition(cameraPos);
+ setActivatable(cameraPos.distanceToSquared(meshRef.current.position) < 9);
+ } else {
+ setActivatable(false);
+ }
+ if (keysPressed.includes('KeyE') && activatable) {
+ setActive(!active);
+ }
+ });
+ const { nodes } = useGLTF('../assets/message-bubble.glb');
+ let color = Color(active ? everforest.blue : everforest.orange);
+ if (activatable) {
+ color = color.lighten(.1);
+ }
return (
<mesh
- {...props}
+ position={position}
ref={meshRef}
scale={active ? 3 : 2}
- onClick={(_) => setActive(!active)}
geometry={nodes.Curve.geometry}
+ castShadow
onPointerOver={(_) => setHovered(true)}
onPointerOut={(_) => setHovered(false)}>
- <meshStandardMaterial color={active ? everforest.blue : everforest.orange} />
+ <meshStandardMaterial color={color.toString()} />
+ {active && <Html center position={[0, .5, 0]} className='unselectable textPopup'><span>{text}</span></Html>}
</mesh>
)
}
diff --git a/src/components/ground.jsx b/src/components/ground.jsx
new file mode 100644
index 0000000..4042538
--- /dev/null
+++ b/src/components/ground.jsx
@@ -0,0 +1,17 @@
+import { useRef } from 'react';
+import * as everforest from '../_everforest.module.scss'
+import { RigidBody } from '@react-three/rapier';
+import { DoubleSide } from 'three';
+import { useGLTF } from '@react-three/drei';
+
+export default function Ground() {
+ const meshRef = useRef();
+ const { nodes } = useGLTF('../assets/terrain.glb');
+ return (
+ <RigidBody type='fixed' colliders="trimesh">
+ <mesh ref={meshRef} position={[0, 0, 0]} rotation={[-Math.PI / 2, 0, 0]} receiveShadow geometry={nodes.Plane.geometry}>
+ <meshStandardMaterial color={everforest.yellow} side={DoubleSide}/>
+ </mesh>
+ </RigidBody>
+ );
+}
diff --git a/src/components/notes.jsx b/src/components/notes.jsx
new file mode 100644
index 0000000..0dd267a
--- /dev/null
+++ b/src/components/notes.jsx
@@ -0,0 +1,14 @@
+import ChatBubble from "./chatbubble";
+
+const chatbubbles = [
+ {position: [0,0,-3], text: "ugh. really struggling with double bleeds in my ankles. makes it hard to do very much of anything, let alone focus for my hobbies"},
+ {position: [-1,0,-5], text: "reddit is everywhere on google and i am sick of it... why can't there be a good forum site?"},
+];
+
+export default function Notes() {
+ return (<>
+ {chatbubbles.map((chatbubble, index) =>
+ <ChatBubble key={index} position={chatbubble.position} text={chatbubble.text}/>
+ )}
+ </>);
+}
diff --git a/src/components/player.jsx b/src/components/player.jsx
new file mode 100644
index 0000000..bd8e392
--- /dev/null
+++ b/src/components/player.jsx
@@ -0,0 +1,78 @@
+import { Capsule, PerspectiveCamera, PointerLockControls } from "@react-three/drei";
+import { useFrame } from "@react-three/fiber";
+import { useContext, useEffect, useRef } from "react";
+import { AppContext } from "../App";
+import { CapsuleCollider, RapierCollider, RapierRigidBody, RigidBody, useRapier, vec3 } from "@react-three/rapier";
+import { quat } from "@react-three/rapier";
+import { Euler, Object3D, Vector3 } from "three";
+import { useBeforePhysicsStep } from "@react-three/rapier";
+
+const _movespeed = 3.0;
+
+export default function Player() {
+ const controlsRef = useRef();
+ const { keys } = useContext(AppContext);
+ const rapier = useRapier();
+ const controller = useRef();
+ const collider = useRef();
+ const rigidbody = useRef();
+ const camera = useRef();
+
+ const refState = useRef({
+ grounded: false,
+ jumping: false,
+ velocity: vec3(),
+ });
+
+ useEffect(() => {
+ const c = rapier.world.createCharacterController(0.1);
+ c.setApplyImpulsesToDynamicBodies(true);
+ c.setCharacterMass(0.2);
+ controller.current = c;
+ }, [rapier]);
+
+ useBeforePhysicsStep((world) => {
+ if (controller.current && rigidbody.current && collider.current) {
+ const move_axis_x = +(keys.includes('KeyD')) - +(keys.includes('KeyA'));
+ const move_axis_z = +(keys.includes('KeyW')) - +(keys.includes('KeyS'));
+
+ const { velocity } = refState.current;
+ const position = vec3(rigidbody.current.translation());
+ const movement = vec3();
+
+ const forward = new Vector3();
+ camera.current.getWorldDirection(forward);
+ const left = new Vector3().crossVectors(forward, camera.current.up);
+
+ movement.x += move_axis_z * world.timestep * _movespeed * forward.x;
+ movement.z += move_axis_z * world.timestep * _movespeed * forward.z;
+ movement.x += move_axis_x * world.timestep * _movespeed * left.x;
+ movement.z += move_axis_x * world.timestep * _movespeed * left.z;
+
+ if (refState.current.grounded) {
+ velocity.y = 0;
+ } else {
+ velocity.y -= 9.81 * world.timestep * world.timestep;
+ }
+
+ movement.add(velocity);
+
+ controller.current.computeColliderMovement(collider.current, movement);
+ refState.current.grounded = controller.current.computedGrounded();
+
+ let correctedMovement = controller.current.computedMovement();
+ position.add(vec3(correctedMovement));
+
+ rigidbody.current.setNextKinematicTranslation(position);
+ }
+ });
+
+ return (
+ <RigidBody type="kinematicPosition" colliders={false} ref={rigidbody} position={[0, 2, 0]}>
+ <PerspectiveCamera makeDefault position={[0, .9, 0]} fov={90} ref={camera} />
+ <PointerLockControls ref={controlsRef} />
+ <CapsuleCollider ref={collider} args={[1, 0.5]} />
+ </RigidBody>
+ );
+}
+