Core Concepts
Components
Your Viber3D game typically uses React + React Three Fiber. Components can query data from the ECS world and render Three.js objects accordingly.
ECS + React
Koota provides React hooks to connect ECS data (traits) to your components. In Viber3D, you’ll often use React Three Fiber (@react-three/fiber
) for rendering 3D scenes, and these hooks to sync ECS state with visuals.
Common Hooks
useQuery
— returns an array of entities matching certain traits.useQueryFirst
— returns the first matching entity (orundefined
).useTrait
— returns the current value of a given trait on an entity (orundefined
if missing).useTraitEffect
— runs a side effect whenever the trait is added, removed, or changed.useWorld
— returns the current ECS world from context.useActions
— returns a set of centralized actions for mutating the ECS state.
Example: Rendering Entities
import { useQuery, useTrait } from 'koota/react'
import { Transform, IsEnemy } from '../traits'
function EnemyRenderer() {
// get all enemies
const enemies = useQuery(IsEnemy, Transform)
return (
<>
{enemies.map(entity => (
<EnemyView key={entity.id()} entity={entity} />
))}
</>
)
}
function EnemyView({ entity }: { entity: Entity }) {
const transform = useTrait(entity, Transform)
if (!transform) return null
return (
<mesh position={transform.position}>
<boxBufferGeometry />
<meshStandardMaterial color="red" />
</mesh>
)
}
useTraitEffect
Using
To react to changes in a trait without causing rerenders, use useTraitEffect
. For example, you might want to update a mesh’s position in a ref:
function EnemyView({ entity }: { entity: Entity }) {
const ref = useRef<THREE.Mesh>(null!)
// Subscribe to ECS changes outside of React’s render
useTraitEffect(entity, Transform, (transform) => {
if (!transform || !ref.current) return
ref.current.position.copy(transform.position)
ref.current.rotation.copy(transform.rotation)
})
return (
<mesh ref={ref}>
<boxBufferGeometry />
<meshStandardMaterial color="red" />
</mesh>
)
}
React Three Fiber + ECS
You can also integrate useFrame
from R3F for certain updates:
import { useFrame } from '@react-three/fiber'
import { Movement, Transform } from '../traits'
function MovementSystem() {
// This component acts as a "system" in React
const world = useWorld()
useFrame(() => {
const time = world.get(Time)
if (!time) return
const { delta } = time
world.query(Transform, Movement).updateEach(([t, m]) => {
t.position.add(m.velocity.clone().multiplyScalar(delta))
})
})
return null
}
Performance Optimizations
- Memoize expensive sub-components if they always render the same geometry.
- Instancing (in R3F) for large numbers of identical objects.
- Culling & LOD for objects not in view or too far away.
Next Steps
- Explore Actions for centralizing ECS changes in your React components.
- Check out official React Three Fiber docs for advanced rendering.
- Combine with Systems for heavy logic outside of React’s render cycle.