Skip to content

Commit

Permalink
Merge next into master (#151)
Browse files Browse the repository at this point in the history
* chore: bump @types/react and @types/react-dom

* feat: defer addition and removal of objects while stepping instead of throwing an error

* feat(sandbox): extract header from app.tsx, minor refactors, update deps

* feat(sandbox): move body and spring entity logic from app.tsx to PhysicsSystem

* chore(sandbox): update arancini to v5

* chore(actions): use node 20

* chore(sandbox): update arancini to v6

* Revert "feat: defer addition and removal of objects while stepping instead of throwing an error"

This reverts commit 0fa49ba.
  • Loading branch information
isaac-mason authored Feb 6, 2024
1 parent ec9d5ac commit 5e88a6c
Show file tree
Hide file tree
Showing 17 changed files with 416 additions and 311 deletions.
5 changes: 5 additions & 0 deletions .changeset/brown-parrots-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@p2-es/sandbox": patch
---

feat(sandbox): move body and spring entity logic from app.tsx to PhysicsSystem
2 changes: 1 addition & 1 deletion .changeset/cool-rocks-pump.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"@p2-es/sandbox": patch
---

chore: update arancini to v4
chore: update arancini to v5
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ jobs:
- name: Checkout Repo
uses: actions/checkout@v2

- name: Setup Node.js 16.x
- name: Setup Node.js 20.x
uses: actions/setup-node@v2
with:
node-version: 16.x
node-version: 20.x

- name: Install Dependencies
run: yarn
Expand Down
8 changes: 4 additions & 4 deletions packages/p2-es-sandbox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"dependencies": {
"@pixi/core": "7.3.1",
"arancini": "^4.0.1",
"arancini": "^6.0.0",
"leva": "^0.9.34",
"p2-es": "1.2.3",
"pixi.js": "^7.2.4",
Expand All @@ -41,8 +41,8 @@
"@storybook/client-api": "^7.3.2",
"@storybook/react": "^7.3.2",
"@storybook/react-vite": "^7.3.2",
"@types/react": "^18.0.14",
"@types/react-dom": "^18.2.8",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@types/styled-components": "^5.1.26",
"@typescript-eslint/eslint-plugin": "^6.5.0",
"@typescript-eslint/parser": "^6.2.1",
Expand All @@ -56,7 +56,7 @@
"eslint-plugin-storybook": "^0.6.13",
"prettier": "^2.8.7",
"storybook": "^7.3.2",
"typescript": "^4.5.4",
"typescript": "^5.3.3",
"vite": "^4.4.8",
"vite-plugin-dts": "^3.6.0"
},
Expand Down
159 changes: 27 additions & 132 deletions packages/p2-es-sandbox/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { createECS } from 'arancini/react'
import * as p2 from 'p2-es'
import React, { useEffect, useRef, useState } from 'react'
import { ThemeProvider } from 'styled-components'
import { Controls } from './controls'
import {
CircleTool,
EcsProvider, Entity, PickPanTool,
EcsContext,
EcsProvider,
PickPanTool,
PolygonTool,
RectangleTool,
SandboxSettings,
Expand All @@ -16,31 +17,15 @@ import {
useFrame,
useSingletonComponent
} from './ecs'
import { useConst } from './hooks'
import { SandboxFunction, Scenes, createSandbox } from './sandbox'
import {
CanvasWrapper,
CodeSvg,
ControlsWrapper,
ExternalLink,
ExternalLinkSvg,
Header,
HeaderButton,
HeaderButtons,
HeaderMiddle,
HeaderSandboxTitle,
Main,
PencilSvg,
RefreshSvg,
SandboxContainer,
styledComponentsTheme,
} from './ui'
import { CanvasWrapper, ControlsWrapper, Main, SandboxContainer, styledComponentsTheme } from './ui'
import { Header } from './ui/header'

export type AppProps = {
setup: SandboxFunction | Scenes
title?: string
codeLink?: string
ecs: ReturnType<typeof createECS>
ecs: EcsContext
showControls: boolean
showHeader: boolean
enablePanning?: boolean
Expand All @@ -56,7 +41,7 @@ const AppInner = ({
enablePanning = true,
enableZooming = true,
}: AppProps) => {
const ecs = useECS()
const { world, executor, react: { Entity, Component }} = useECS()

const sandboxContainerRef = useRef<HTMLDivElement>(null)
const canvasWrapperElement = useRef<HTMLDivElement>(null)
Expand All @@ -81,9 +66,6 @@ const AppInner = ({
/* user-land handlers */
const [sandboxUpdateHandlers, setSandboxUpdateHandlers] = useState<Set<(delta: number) => void> | undefined>()

const bodyEntities: Map<p2.Body, Entity> = useConst(() => new Map())
const springEntities: Map<p2.Spring, Entity> = useConst(() => new Map())

const pixi = useSingletonComponent('pixi')
const pointer = useSingletonComponent('pointer')
const settings = useSingletonComponent('sandboxSettings')
Expand All @@ -107,7 +89,7 @@ const AppInner = ({

/* dirty sprites state when settings change */
useEffect(() => {
ecs.world
world
.filter((e) => e.with('physicsBody', 'sprite'))
.forEach((e) => {
e.sprite.dirty = true
Expand All @@ -118,7 +100,7 @@ const AppInner = ({
if (!pixi || !pixi.application.renderer || !pointer) return

const {
world,
world: physicsWorld,
tools,
updateHandlers,
sandboxContext,
Expand All @@ -141,86 +123,36 @@ const AppInner = ({
setTool(tools.default)
}

// create entities for existing physics bodies and springs
const addBodyEntity = (body: p2.Body) => {
const entity = ecs.world.create({ physicsBody: body })
bodyEntities.set(body, entity)
}

const removeBodyEntity = (body: p2.Body) => {
const entity = bodyEntities.get(body)
if (!entity) return
ecs.world.destroy(entity)
bodyEntities.delete(body)
}

const addSpringEntity = (spring: p2.Spring) => {
const entity = ecs.world.create({ physicsSpring: spring })
springEntities.set(spring, entity)
}

const removeSpringRemoveSpringEntity = (spring: p2.Spring) => {
const entity = springEntities.get(spring)!
if (!entity) return
ecs.world.destroy(entity)
springEntities.delete(spring)
}

for (const body of world.bodies) {
addBodyEntity(body)
}

for (const spring of world.springs) {
addSpringEntity(spring)
}

// add physics body and spring entities on world events
const addBodyHandler = ({ body }: { body: p2.Body }) => addBodyEntity(body)
const removeBodyHandler = ({ body }: { body: p2.Body }) => removeBodyEntity(body)

const addSpringHandler = ({ spring }: { spring: p2.Spring }) => addSpringEntity(spring)
const removeSpringHandler = ({ spring }: { spring: p2.Spring }) => removeSpringRemoveSpringEntity(spring)

world.on('addBody', addBodyHandler)
world.on('addSpring', addSpringHandler)
world.on('removeBody', removeBodyHandler)
world.on('removeSpring', removeSpringHandler)

// set the sandbox update handlers
setSandboxUpdateHandlers(updateHandlers)

// create singleton physics entity
const physicsEntity = ecs.world.create({ physicsWorld: world })
// create physics world singleton
const physicsEntity = world.create({ physicsWorld: physicsWorld })

// set window globals
const globalWindow = window as unknown as Record<string, unknown>
globalWindow.world = world
globalWindow.world = physicsWorld
globalWindow.p2 = p2
globalWindow.sandbox = sandboxContext
globalWindow.ecs = ecs.world
globalWindow.ecs = world

return () => {
previousScene.current = scene

setSandboxUpdateHandlers(undefined)

world.off('addBody', addBodyHandler)
world.off('addSpring', addSpringHandler)
world.off('removeBody', removeBodyHandler)
world.off('removeSpring', removeSpringHandler)

destroySandbox()

const entities = [physicsEntity, ...ecs.world.query((e) => e.some('physicsBody', 'physicsSpring'))]
const entities = [physicsEntity, ...world.query((e) => e.some('physicsBody', 'physicsSpring'))]

entities.forEach((entity) => {
ecs.world.destroy(entity)
world.destroy(entity)
})
}
}, [pixi, pointer, scene, sceneVersion])

useFrame((delta) => {
ecs.step(delta)
executor.update(delta)

if (!sandboxUpdateHandlers) return
sandboxUpdateHandlers.forEach((fn) => fn(delta))
Expand All @@ -230,51 +162,14 @@ const AppInner = ({
<>
<SandboxContainer ref={sandboxContainerRef} tabIndex={0}>
{showHeader && (
<Header>
<a href="https://p2-es.pmnd.rs" target="_blank">
<ExternalLink>
p2-es
<ExternalLinkSvg />
</ExternalLink>
</a>

<HeaderMiddle>
<HeaderSandboxTitle>
{title}
{title && sceneNames.length > 1 ? ' - ' : ''}
{scene !== 'default' ? scene : ''}
</HeaderSandboxTitle>

<HeaderButtons>
<HeaderButton title="Reset">
<button onClick={() => resetScene()}>
<RefreshSvg />
</button>
</HeaderButton>

<HeaderButton title="Settings">
<button onClick={() => setShowControls((current) => !current)}>
<PencilSvg />
</button>
</HeaderButton>

{codeLink !== undefined ? (
<HeaderButton title="Sandbox Source Code">
<a href={codeLink} target="_blank">
<CodeSvg />
</a>
</HeaderButton>
) : null}
</HeaderButtons>
</HeaderMiddle>

<a href="https://p2-es.pmnd.rs/docs" target="_blank">
<ExternalLink>
docs
<ExternalLinkSvg />
</ExternalLink>
</a>
</Header>
<Header
title={title}
codeLink={codeLink}
sceneNames={sceneNames}
scene={scene}
resetScene={resetScene}
toggleShowSceneControls={() => setShowControls((s) => !s)}
/>
)}
<Main className={showControls ? 'settings-enabled' : ''}>
<CanvasWrapper ref={canvasWrapperElement} className={showControls ? '' : 'settings-hidden'} />
Expand Down Expand Up @@ -308,9 +203,9 @@ const AppInner = ({
</>
)}

<ecs.Entity>
<ecs.Component name="sandboxSettings" data={sandboxSettings} />
</ecs.Entity>
<Entity>
<Component name="sandboxSettings" value={sandboxSettings} />
</Entity>
</>
)
}
Expand Down
16 changes: 12 additions & 4 deletions packages/p2-es-sandbox/src/ecs/context.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { ECS, createECS } from 'arancini/react'
import { World } from 'arancini'
import { ReactAPI } from 'arancini/react'
import { Executor } from 'arancini/systems'
import React, { createContext, useContext } from 'react'
import { Entity } from './entity'

const ecsContext = createContext<ReturnType<typeof createECS>>(null!)
export type EcsContext = {
world: World<Entity>
executor: Executor<Entity>
react: ReactAPI<Entity>
}

const ecsContext = createContext<EcsContext>(null!)

export const useECS = (): ECS<Entity> => {
export const useECS = (): EcsContext => {
return useContext(ecsContext)
}

export const EcsProvider = ({ children, ecs }: { children: React.ReactNode; ecs: ReturnType<typeof createECS> }) => {
export const EcsProvider = ({ children, ecs }: { children: React.ReactNode; ecs: EcsContext }) => {
return <ecsContext.Provider value={ecs}>{children}</ecsContext.Provider>
}
Loading

0 comments on commit 5e88a6c

Please sign in to comment.