Skip to content

Commit

Permalink
WIP: Drone Game (experimental) (#255)
Browse files Browse the repository at this point in the history
  • Loading branch information
hmans authored Sep 12, 2022
1 parent 60a26a8 commit fe47d51
Show file tree
Hide file tree
Showing 37 changed files with 796 additions and 140 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ _TODO_

_TODO_

### State Composer

![react]

_TODO_

## Development 🛠

### Core Tenets
Expand Down
24 changes: 24 additions & 0 deletions apps/drone-game/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
13 changes: 13 additions & 0 deletions apps/drone-game/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
42 changes: 42 additions & 0 deletions apps/drone-game/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "drone-game",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@hmans/things": "^0.0.6",
"@react-three/drei": "^9.26.0",
"@react-three/fiber": "^8.7.0",
"@react-three/rapier": "^0.6.7",
"camera-composer": "workspace:^0.0.1",
"fp-ts": "^2.12.3",
"input-composer": "workspace:^0.0.1",
"material-composer": "workspace:^0.2.0",
"material-composer-r3f": "workspace:^0.2.0",
"randomish": "^0.1.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"render-composer": "workspace:^0.1.5",
"shader-composer": "workspace:^0.4.0",
"shader-composer-r3f": "workspace:^0.4.0",
"shader-composer-toybox": "workspace:^0.1.3",
"state-composer": "workspace:^0.0.1",
"three": "^0.143.0",
"three-stdlib": "^2.15.0",
"timeline-composer": "workspace:^0.1.6",
"vfx-composer-r3f": "workspace:^0.2.3"
},
"devDependencies": {
"@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6",
"@types/three": "^0.144.0",
"@vitejs/plugin-react": "^2.1.0",
"typescript": "^4.6.4",
"vite": "^3.1.0"
}
}
1 change: 1 addition & 0 deletions apps/drone-game/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions apps/drone-game/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Suspense } from "react"
import { RenderCanvas, RenderPipeline } from "render-composer"
import { GameplayScene } from "./scenes/gameplay/GameplayScene"
import { FSM } from "./state"

export function App() {
return (
<RenderCanvas>
<RenderPipeline bloom antiAliasing vignette>
<Suspense>
<FSM.MatchState state="gameplay">
<GameplayScene />
</FSM.MatchState>
</Suspense>
</RenderPipeline>
</RenderCanvas>
)
}
Binary file added apps/drone-game/src/assets/smoke.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions apps/drone-game/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}

div#root {
width: 100vw;
height: 100vh;
}
10 changes: 10 additions & 0 deletions apps/drone-game/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from "react"
import ReactDOM from "react-dom/client"
import { App } from "./App"
import "./index.css"

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
60 changes: 60 additions & 0 deletions apps/drone-game/src/scenes/gameplay/Debris.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useConst } from "@hmans/things"
import { composable, modules } from "material-composer-r3f"
import { between, plusMinus, upTo } from "randomish"
import { Layers, useRenderPipeline } from "render-composer"
import {
Float,
GlobalTime,
Input,
InstanceID,
Mul,
Rotation3DZ,
Time,
Vec2,
Vec3
} from "shader-composer"
import { useUniformUnit } from "shader-composer-r3f"
import { DoubleSide, Vector3 } from "three"
import { Emitter, Particles, useParticleAttribute } from "vfx-composer-r3f"
import { PSRDNoise2D } from "shader-composer-toybox"

export const Debris = () => {
const time = useConst(() => Time())
const rotationSpeed = useParticleAttribute(() => 0 as number)
const scale = useParticleAttribute(() => 1 as number)

const id = Float(InstanceID, { varying: true })
const getNoise = (offset: Input<"float">) => PSRDNoise2D(Vec2([offset, id]))

return (
<group>
<Particles layers-mask={Layers.TransparentFX}>
<planeGeometry />

<composable.meshStandardMaterial
transparent
side={DoubleSide}
color="#888"
>
<modules.Rotate rotation={Rotation3DZ(Mul(time, rotationSpeed))} />
<modules.Velocity
direction={Mul(Vec3([getNoise(1), getNoise(2), getNoise(3)]), 5)}
time={GlobalTime}
/>
<modules.Scale scale={scale} />
</composable.meshStandardMaterial>

<Emitter
limit={1000}
rate={Infinity}
setup={({ position, rotation }) => {
position.set(plusMinus(30), upTo(15), plusMinus(30))
rotation.random()
rotationSpeed.value = plusMinus(0.1)
scale.value = between(0.01, 0.05)
}}
/>
</Particles>
</group>
)
}
187 changes: 187 additions & 0 deletions apps/drone-game/src/scenes/gameplay/GameplayScene.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { Environment, PerspectiveCamera } from "@react-three/drei"
import { GroupProps, useFrame } from "@react-three/fiber"
import {
CuboidCollider,
Physics,
RigidBody,
RigidBodyApi
} from "@react-three/rapier"
import { useRef } from "react"
import {
PerspectiveCamera as PerspectiveCameraImpl,
Quaternion,
Vector3
} from "three"
import { Debris } from "./Debris"
import { GroundFog } from "./GroundFog"
import { useInput } from "input-composer"

export const GameplayScene = () => {
return (
<>
<ambientLight intensity={0.2} />
<directionalLight
color="white"
intensity={0.7}
position={[10, 10, 10]}
castShadow
/>
<directionalLight
color="white"
intensity={0.2}
position={[-10, 5, 10]}
castShadow
/>

<Physics colliders={false} gravity={[0, 0, 0]}>
{/* <Debug color="red" sleepColor="blue" /> */}
<Player position={[0, 10, 20]} />

{/* Scenery */}
<RigidBody colliders="hull">
<mesh castShadow rotation={[0.2, 0.2, 0.2]} scale={5}>
<dodecahedronGeometry />
<meshStandardMaterial
color="#222"
metalness={0.3}
roughness={0.8}
/>
</mesh>
</RigidBody>

<GroundFog />
<Debris />
</Physics>

<fogExp2 args={["#000", 0.03]} attach="fog" />
<Environment preset="sunset" />
</>
)
}

const Ground = (props: GroupProps) => (
<group {...props}>
<mesh rotation-x={-Math.PI / 2} receiveShadow>
<planeGeometry args={[1000, 1000]} />
<meshStandardMaterial color="#111" />
</mesh>
</group>
)

const tmpVec3 = new Vector3()
const tmpQuat = new Quaternion()

const Player = (props: Parameters<typeof RigidBody>[0]) => {
const body = useRef<RigidBodyApi>(null!)
const camera = useRef<PerspectiveCameraImpl>(null!)

const input = useInput()

useFrame(() => {
const gamepad = input.gamepad(0)
const keyboard = input.keyboard

const inputs = {
leftStick: {
keyboard: keyboard.vector("KeyS", "KeyW", "KeyA", "KeyD"),
gamepad: gamepad.vector(0, 1)
},

rightStick: {
keyboard: keyboard.vector(
"ArrowUp",
"ArrowDown",
"ArrowLeft",
"ArrowRight"
),
gamepad: gamepad.vector(2, 3)
},

leftBumper: {
gamepad: gamepad.button(4),
keyboard: keyboard.key("KeyQ")
},

rightBumper: {
gamepad: gamepad.button(5),
keyboard: keyboard.key("KeyE")
},

leftTrigger: {
keyboard: keyboard.key("ControlLeft"),
gamepad: gamepad.button(6)
},

rightTrigger: {
keyboard: keyboard.key("Space"),
gamepad: gamepad.button(7)
}
}

/* Extract inputs depending on current device */
const currentDevice = "gamepad"
const leftStick = inputs.leftStick[currentDevice]
const rightStick = inputs.rightStick[currentDevice]

const leftTrigger = inputs.leftTrigger[currentDevice]
const rightTrigger = inputs.rightTrigger[currentDevice]

const leftBumper = inputs.leftBumper[currentDevice]
const rightBumper = inputs.rightBumper[currentDevice]

body.current.resetForces()
body.current.resetTorques()

camera.current.getWorldQuaternion(tmpQuat)

/* Thrust */
body.current.addForce(
tmpVec3
.set(0, 0, -(rightTrigger * 1000 - leftTrigger * 500))
.applyQuaternion(tmpQuat)
)

/* Lateral movement */
body.current.addForce(
tmpVec3.set(0, rightStick.y * -200, 0).applyQuaternion(tmpQuat)
)

body.current.addTorque(
tmpVec3.set(0, rightStick.x * -100, 0).applyQuaternion(tmpQuat)
)

body.current.addTorque(
tmpVec3
.set(0, 0, (rightBumper - leftBumper) * -100)
.applyQuaternion(tmpQuat)
)

body.current.addTorque(
tmpVec3
.set(leftStick.y * 100, leftStick.x * -100, leftStick.x * -100)
.applyQuaternion(tmpQuat)
)
})

return (
<RigidBody
restitution={2}
{...props}
ref={body}
angularDamping={1}
linearDamping={1}
onCollisionEnter={({ manifold }) => {
console.log(
"Collision at world position ",
manifold.solverContactPoint(0)
)
}}
onCollisionExit={() => {
console.log("Collision ended")
}}
>
<PerspectiveCamera ref={camera} makeDefault />
<CuboidCollider args={[2, 2, 2]} />
</RigidBody>
)
}
Loading

0 comments on commit fe47d51

Please sign in to comment.