Effective way to keep track of 3D objects in React

The question is about connecting the Objects3D’s on the Canvas created by react-three-fiber / drei with constructs in canvas.

To learn react-three-fiber I created a 3D Chess project for myself. I use chess.js for the chess backend (JS with types). I layout the initial pieces from chess.js internals, I can click the pieces and have them check possible moves using chess.js with each piece’s local constructs, etc.

What I want to add is:

  • Manual Play: Click the piece and move it to another cell, with some animation.
  • Play against the computer (AI): That part decides on the move, I can make the move in chess.js, but I need to reach the 3D object and make the animated move.

All of that without reconstructing everything of course… This might seem more like react question, and it partially is, but finding the object and manipulating/animating the move is my major question.

Do I need to use classic search in the scene? Or is there a better way like using keys, refs etc? Keep the whole list of pieces in the Store and work on them?

Which one is natural and ideal?

Have you seen gltfjsx? It seems like a lot of R3F developers use that to organize things.

1 Like

I’ve been using it to load the models, but in the end, they become group mesh(geo,material)) - say a pawn. Then I create pawn instances and 3D position them from chess.js’s 2d array of the board.

I want to handle these instances. For now, they live in a list on the Store, but the actual move is done in the chess.js instance. My problem is connecting them in a feasible manner.

Disregarding chess, that should be a general problem, connecting app data to r3f…

Maybe onEffect on chess change where I check previous board with current board, find the changed piece(s), loacate them in the list of pieces and animate them in 3D?

I think you probably don’t want useEffect running at 60 FPS, but doing something like that in useFrame would be reasonable.

Thank you for the pointers @donmccurdy

I think I took care of those culprits, by connecting only related state variables as the second parameter of useEffect and/or having an if statement at the very start of useEffect, checking some flags(s).

I think I’ll use react-spring for animation, not tried it yet.

My question is on how to reach that object.

Keeping a list of refs in the Store (that would be static after the creation)? Would that work?

Keeping a list of refs in the Store (that would be static after the creation)? Would that work?

tbh that’s pulling the cart in front of the horse. state should drive the app, the app shouldn’t drive state. if you have a state model and all your players bind to it, they just react to changes. the state model usually doesn’t need to know the view, it just keeps the minimal information needed, raw positions for instance.

components can react to that without causing render via zustand and useframe, though again, i’m not sure you want to push animations through the state model either, it is better to update a position and animation in the individual component via useframe.

I think I’ll use react-spring for animation, not tried it yet.

i would first go with plain lerp. it’s the easiest and fastest way to animate. if you need physical springs later on no problem, but they usually make sense for interaction, interruptible movements, etc. my advice would be, keep it as simple as possible.

btw moving things across a board with an animation, i have some very basic example code: Plane project - CodeSandbox

3 Likes

That’s really good work.

Thank you @drcmda, that was very helpful. I was thinking everything should go from zustand to view, but keeping interactive things with the objects, like you’ve implemented in your example (with useThree) helps a lot…

I’m rather new to React, as my 30+ language and I have trouble thinking the React way. I’m more a C/C++ guy with objects, methods and all. Now I have to decrypt what you’ve been doing in that example :slight_smile:

oh, i come from c++ as well, long time ago. :smiley: it helps to always hammer this in: the view is a reflection of state. (putting refs into state would make state a reflection of the view). the critical part of that code is event.ray.intersectPlane which tells you the plane-projected coordinates.

Yep, I saw that, but it won’t work on some cases like knights jumping over :slight_smile: I was thinking of a 3D move (conditionally), maybe I can take it to a higher plane and do the same there…

For me the whole useDrag hook is a gem :gem:

To give some more insight: This is actually a multi-lingual voice-driven project I wrote in a Voice AI related hackathon during the Mozilla Festival last week. I trained a couple of languages. It mainly works for English, sometimes with other languages (acoustic models are not so good for them yet). I could command it and moved pieces on chess.js domain, but could not reflect it to the actual 3D move, even non-animated.

I’ll be working on this to make it open-source, so please bear with me :pray: