Skip to content
This repository has been archived by the owner on Dec 1, 2021. It is now read-only.

Latest commit

 

History

History
481 lines (352 loc) · 8.98 KB

API.md

File metadata and controls

481 lines (352 loc) · 8.98 KB

API

This is a work in progress.

Engine

Important: must be placed within the Canvas element provided by react-three-fiber.

<Engine
 config                         // optional - object
 worldParams                    // optional - object  
 logicWorker                    // optional - web worker
 logicMappedComponents          // optional - object
 />

config

optional

type config = {
    maxNumberOfDynamicObjects: number // default 100
    updateRate: number // default 1000 / 30 (i.e. physics updates 30 times a second)
}

worldParams

optional

See the planck.js documentation for details regarding worldDef

type worldParams = {
    allowSleep: boolean // default to true
    gravity: Vec2 // default to Vec2(0, 0) (i.e. no gravity)
    ...worldDef
}

logicWorker

optional

A Web Worker that will be run as the logic worker, synchronized with the physics app. Read more here.

logicMappedComponents

optional

Facilitates synchronising components from the logic app with the main app. Read more here.

type logicMappedComponents = {
    [key: string]: ReactComponent
}

See useSyncWithMainComponent for further details.

useFixedUpdate

Inspired by Unity's OnFixedUpDate, useFixedUpdate is a hook that enables you to call a function after each physics update. For Unity this is the recommended way to apply physics effects.

import { useFixedUpdate } from "react-three-game-engine"

useFixedUpdate((delta: number) => {
    // do something
})

Physics

Bodies

BodyApi

type BodyApi = {
    // planck.js methods
    applyForceToCenter: (vec: Vec2, uuid?: string) => void;
    applyLinearImpulse: (vec: Vec2, pos: Vec2, uuid?: string) => void;
    setPosition: (vec: Vec2, uuid?: string) => void;
    setLinearVelocity: (vec: Vec2, uuid?: string) => void;
    setAngle: (angle: number, uuid?: string) => void;
    // custom
    updateBody: (data: UpdateBodyData, uuid?: string) => void;
}

type UpdateBodyData = {
    fixtureUpdate?: {
        groupIndex?: number,
        categoryBits?: number,
        maskBits?: number,
    }
}

For detail on what the planck.js methods do, see the documentation for planck.js.

useBody

useBody(PropsFn, Config): [ref: MutableRefObject<Object3D>, api: BodyApi, uuid: string]
type PropsFn = () => AddBodyDef

type AddBodyDef = {
    bodyType: BodyType,
    fixtures: Fixture[],
    ...BodyDef,
}

type Fixture = BoxFixture | CircleFixture

type BoxFixture = FixtureBase & {
    hx: number,
    hy: number,
    center?: [number, number],
}

type CircleFixture = FixtureBase & {
    radius: number,
}

type FixtureBase = {
    shape: BodyShape,
    fixtureOptions?: Partial<FixtureOpt>,
}

enum BodyShape {
    box = 'box',
    circle = 'circle',
}

enum BodyType {
    static = 'static',
    kinematic = 'kinematic',
    dynamic = 'dynamic'
}
type Config = {
    listenForCollisions?: boolean;
    applyAngle?: boolean;
    cacheKey?: string;
    uuid?: string;
    fwdRef?: MutableRefObject<Object3D>;
}

Note: for BodyDef see the planck.js documentation. Note: for FixtureOpt see the planck.js documentation.

import { useBody, BodyType, BodyShape } from "react-three-game-engine"
import { Vec2 } from "planck-js"

const [ref, api, uuid] = useBody(() => ({
     type: BodyType.dynamic,
     position: Vec2(0, 0),
     linearDamping: 4,
     fixtures: [{
         shape: BodyShape.circle,
         radius: 0.55,
         fixtureOptions: {
             density: 20,
         }
     }],
}))

Note: if you set bodyType to static it will not be synchronised with either the main app or logic app.

createBoxFixture

createBoxFixture = ({
  width = 1,
  height = 1,
  center,
  fixtureOptions = {}
}: {
 width?: number,
 height?: number,
 center?: [number, number],
 fixtureOptions?: Partial<FixtureOpt>
}) => BoxFixture
useBody(() => ({
    type: BodyType.dynamic,
    position: Vec2(0, 0),
    linearDamping: 4,
    fixtures: [
        createBoxFixture({
            width: 2,
            height: 2,
        })      
    ],
}))

createCircleFixture

createCircleFixture = ({
  radius = 1,
  fixtureOptions = {}
}: {
 radius?: number,
 fixtureOptions?: Partial<FixtureOpt>
}) => CircleFixture
useBody(() => ({
    type: BodyType.dynamic,
    position: Vec2(0, 0),
    linearDamping: 4,
    fixtures: [
        createCircleFixture({
            radius: 2,
        })      
    ],
}))

useBodyApi

useBodyApi: (uuid: string) => BodyApi
const api = useBodyApi('player')

// ...

api.setLinearVelocity(Vec2(1, 1))

useSubscribeMesh

useSubscribeMesh: (
                    uuid: string, 
                    object: Object3D, 
                    applyAngle: boolean = true, 
                    isDynamic: boolean = true
)
const ref = useRef<Object3D>(new Object3D())
useSubscribeMesh('player', ref.current, false)

Logic

Logic Worker

logicWorkerHandler

logicWorkerHandler: (worker: Worker, appComponent: ReactComponent)
/* eslint-disable no-restricted-globals */
import { logicWorkerHandler } from "react-three-game-engine";

// because of some weird react/dev/webpack/something quirk
(self).$RefreshReg$ = () => {};
(self).$RefreshSig$ = () => () => {};

logicWorkerHandler(self, require("../path/to/logic/app/component").LgApp)

Logic App

withLogicWrapper

withLogicWrapper: (appComponent: ReactComponent) => ReactComponent
import { withLogicWrapper } from "react-three-game-engine";

const App = () => {
    // ... your new logic app goes here
}

export const LgApp = withLogicWrapper(App)

useSyncWithMainComponent

useSyncWithMainComponent: (componentKey: string, uniqueKey: string, props: {}) => UpdateProps

type UpdateProps = (props: {}) => void

const updateProps = useSyncWithMainComponent("player", "uniqueKey", {
    foo: "bar",
    blah: "blah",
})

// ...

updateProps({
    foo: "updated"
})

See logicMappedComponents for implementation details.

Communication

useSendMessage

useSendMessage: () => (messageKey: string, data: any) => void
import { useSendMessage } from "react-three-game-engine"

// ...

const sendMessage = useSendMessage()

// ...

sendMessage('messageKey', "any-data")

useOnMessage

useOnMessage: () => (messageKey: string, callback: (data: any) => void) => UnsubscribeFn
import { useOnMessage } from "react-three-game-engine"

// ...

const onMessage = useOnMessage()

// ...

const unsubscribe = onMessage('messageKey', (data) => {
  // data -> "any-data"
})

// ...

unsubscribe()

Misc

Mesh Storage

useStoreMesh

useStoreMesh: (uuid: string, mesh: Object3D)

useStoredMesh

useStoredMesh: (uuid: string) => Object3D | null

Mesh Instancing

You'll need to install @react-three/drei

InstancesProvider

Place inside of <Engine/>

<Engine>
    <InstancesProvider>
        {/*...*/}
    </InstancesProvider>
</Engine>

InstancedMesh

<InstancedMesh
    meshKey                     // unique key
    maxInstances                // maximum number of instances, smaller = better performance
    gltfPath                    // e.g. /model.gltf
    meshProps                   // optional - params that go on <instancedMesh/> for r3f, e.g. castShadow
/>

type Props = {
    meshKey: string,
    maxInstances: number,
    gltfPath: string,
    meshProps?: JSX.IntrinsicElements['instancedMesh']
}

gltfPath is passed to useGltf from @react-three/drei

Place inside of <InstancesProvider/>

You need to wrap it with React's <Suspense/>

<InstancesProvider>
    <Suspense fallback={null}>
        <InstancedMesh meshKey={"example"} 
                       maxInstances={10} 
                       gltfPath={"path/to/model.gltf"}
                       meshProps={{
                            castShadow: true, 
                            receiveShadow: true
                       }}/>
    </Suspense>
</InstancesProvider>

Instance

<Instance
    meshKey         // unique key
    position        // optional    
    rotation        // optional 
    scale           // optional 
    />

type Props = {
    meshKey: string,
    position?: [number, number, number],
    rotation?: [number, number, number],
    scale?: [number, number, number]
}

<Instance meshKey={"example"} position={[10, 20, 0]}/>

useInstancedMesh

todo...

useAddInstance

todo...