I am setting up a very basic game and learning how to think in terms of R3F/ three – which is providing difficult after a decade of practicing React philosophy almost exclusively.
My game has two main concerns: Player and WorldItem.
// A world item can be selected from the
// shop to be placed into the player's world.
type WorldItemT = {
id: string
modelId: string
modelName: string
position: THREE.Vector3
rotation: THREE.Euler
scale: THREE.Vector3
isGlowing: boolean
glowColor: THREE.Color
glowIntensity: number
}
// The player can run / jump around their
// world, but thats about it for v1.
type PlayerT = {
modelId: string
position: THREE.Vector3
rotation: THREE.Euler
scale: THREE.Vector3
isVisible: boolean
}
I’ve learned through trial and error that I can not expect to manage WorldItem positioning changes to be fluid and consistently linear if they are dictated by reactive state. So I got them set up with useFrame and it works smoothly. And then I added handling for rotation in useFrame – all good.
Then I moved on to Player movement, attempting to handle it the same way with the difference being (mainly) the camera following the Player. I set up a reactive store of key names to booleans so when the user pressed W, for example, the Player began moving upwards smoothly and the camera followed by using camera.lookAt(player.position). It was all good until the user released the key and caused the React component to re-render. The camera suddenly reset back to 0 0 0. I don’t yet fully understand why this is the case, but I do understand that it has something to do with components re-rendering with their own values, rather than what is actually present on the mesh / whatever in three’s world.
Now I am working to mentally grasp what should be / can be reactive state and what basically just needs to be held inside of an object that I mutate to signal different functions to be called in useFrame.
I feel a little bit crazy about this train of thought, but…
I am under the impression that I need to depend purely on React for declarative initial rendering of objects in my 3D world, and from there forward handle everything with conditions invoked every frame with useFrame + regularly querying the state of all the items in the world from three and syncing that for auto-save.
I’ve been considering using Mobx to handle the few React re-renders I need to trigger (for example, a WorldItem is added/removed from the world, WorldItemManager component need’s to re-render to render the new array of WorldItem.)
But I am struggling with… position and rotation can signal to useFrame that they need to update based on something like { isPressedW: true }, so it knows to start moving a selected WorldItem / Player / whatever. But if the user, for example selects a WorldItem and toggles an option to turn on its isGlowing… oh, right… that wouldn’t need to be in useFrame because it is not a repeated condition based update that needs to happen…
So I should just handle something like that in a function that takes the selected item ref and modifies it using the three API it exposes, rather than updating Reactive state and causing the component to re-render, right?
Am I on the right path or am I missing something?