pmndrs/racing-game

before this gets too complex, let's discuss some directions

drcmda opened this issue · 7 comments

some changes i'd like to see before this project goes too far (new levels, etc). the infrastructure should be really, really tight and safe - and although we're making good progress i can see how some parts still feel fragile.

typescript

i am normally all for plain javascript, but light annotation can go a long way in making it easier to change things. right now i change a single variable in the store model and then i have to run every functionality to be sure it works. with ts my editor would already show all broken files in red. for this game the annotations could be very simple and that would also unlock auto complete.

valtio

zustand is nice but property access, especially if nested, can get messy. zustand has a brother called valtio, its the same thing but proxy based. all the setter/getters go away, nesting is no problem, you don't need actions to update. with TS it would be typesafe as well. i have used this with threejs before and the experience was wonderful (the shoe configurator).

testing

???

I am comfortable working with typescript, if we get started with it early enough it shouldn't be too bad.

This is my first time using zustand and so far it seems fine. It kind of feels like there needs to be multiple stores because nesting is really uncomfortable. If you think valtio will be a nicer experience I'm willing to give it a try.

heres an example using valtio https://codesandbox.io/s/shoe-configurator-qxjoj it's similar to zustand + immer, but slimmer than that still.

Also comfortable with ts and welcome the shift with open arms! Makes everything sooo much smoother especially refactoring.

Not used valtio before, but I'd love to learn! (state performance is my weakness)

Regarding the testing, I'm happy to set something up but I would like to discuss:

  1. what we are using to test
  2. what we plan on testing

I'm all ears for Typescript. I wouldn't mind to do the PR and the refactor to migrate it to to Typescript + the interfaces that should be written - 1 concern the current open PR's will receive a lot of big conflicts once such PR is merged into the main branch.

@bjornstar I don't like the amount of commits going into master and that will be need to be ported, I think about merging the typescript branch into the main and fixes the type issues along the way when they get annoying.

I'm frankly happy that this project migrated to typescript. It's far more easier in my opinion with typing, especially when using libraries you are not used to. And the project getting bigger and bigger it will definitely help.

For testing, I think we need first to define an architecture and to follow its principles. Games usually follow entity component system (ECS) principles. We could implement them but it would require a lot of rework on the whole project.
You can see a good example here: https://github.com/coldi/r3f-game-demo
But it also depends of what we want to do here. If the goal is to create a real game that you can easily extends and enhance or if we want a show case of R3F easily understood. In the last case, I don't think ECS would be a good idea.
Following ECS means the project won't be as easy as it is today to understand, copy/paste, and so on. But I think we need to follow some design in order to be able to produce meaningful tests. As of today, I can't really imagine a way to test that the mini map won't be broken in the future because of the lack of architecture.

For example, based on what Coldi has written, I've tried this :

    <Entity name='foo' {...props}>
      <NPCModel />
      <Traits.Animated<CharacterStates, NPCActionName>>
        {({ Link }) => (
          <>
            <Link stateName='Idle' modelAnimationName='Idle' />
            <Link stateName='Walking' modelAnimationName='Walk' />
            <Link stateName='Running' modelAnimationName='Run' />
            <Link stateName='Dying' modelAnimationName='Death' />
            <Link stateName='Chatting' modelAnimationName='Chat' />
          </>
        )}
      </Traits.Animated>
      <Traits.FinaleStateMachine<CharacterStates> initialState='Idle' />
      <Traits.Interactable />
      <Traits.Talking   />
    </Entity>

It's just an example, and it explores different ways of doing the same thing but the main intention is there:

  • following ECS principles
  • being declarative
  • define common reusable components (Physics, Animation, Interaction, Sound, and so on, ...)

ECS seems especially good with Jotai by the way. It really makes sense with this library.

So what do you think? Is this a good idea for this project and its purpose?

i have no experience with ecs and i think there is no declarative abstraction that's good enough atm, only self cooked stuff. i've seen some generic ones around for r3f but imo a little confused and leaking performance. they didn't use hooks and had problems with instancing last time i checked.

as for jotai, imo it would not be a good fit because it's context based. that means it can't be used inside r3f without a workaround. and it's not been made for fast updates.

the typescript change was the biggest obstacle and i think it went pretty well. 🎉