Arcade 3D dans le navigateur: comment nous avons créé le jeu sur React + Redux

Bonjour, Habr! Dans ces années pas si lointaines, dans la première année de la faculté de "programmeur", j'ai aimé poser la question à mes camarades: "Pourquoi es-tu même allé étudier ici?" Bien sûr, je n'ai pas gardé de statistiques précises sur les réponses, mais je m'en souviens avec certitude: plus de la moitié voulait faire des jeux. La plupart de ceux qui ont répondu de cette façon n'étaient pas prêts pour l'abondance de différents types de «mathématiciens» et de «physiciens» avec lesquels nous avons été submergés au cours des deux premières années d'études. Tous n'ont pas survécu - à la fin de la deuxième année des cinq groupes surpeuplés, trois étaient incomplets.


Il n'y a pas si longtemps, notre équipe front-end a eu l'occasion de s'essayer dans le rôle de gamedev. Très brièvement, la tâche est la suivante: créer un vrai jeu en 3D, afin que vous puissiez jouer simplement en ouvrant le navigateur. Même mobile. Même en vue Web.



, , , — React + Redux, « », , , .



RnD , : « CodeFest — . — ». , , , . Gods in the sky.


Photo du groupe CodeFest sur VK / https://vk.com/codefest.  La source de la photo est ici.
CodeFest .


MMO TPS , , . , , , . CodeFest. , .



2 , , 2 . , , .


: -, — , . , N , — 1 . , — 15 . , , 16-. , , :


  1. 30 . CodeFest, — , «». , .
  2. «» . , :
    • 90 CPU;
    • 120 ;
    • 270 /c — , 675 /c — .
  3. 4,2 /c. , . — UDP WebRTC, . .
  4. . 2 , , , . , - Node.js , .

, , ( ?), , , , . .


, . , «». — .


:


, , , . , , 5 , , 40 .


-. , , — . React Redux. c 3D-, , Gods in the sky, three.js.


, .



:



, Node.js. :


  • , - ( , IE11);
  • access-;
  • html- .

SSR . kubernetes , . , . , , , , ( ). . Nginx.



. TS, AppState, :


interface AppState {
    gameState: GameState;
    gameObjects: GameObjects;
    data: Data;
    requests: RequestsState;
}

requests — , — . , — . .


data — , , , , «». .


GameState :


  1. stage: 'factoids' | 'citySelect' | 'flyShow' | 'game' | 'results'; — . :


    • ;
    • ;
    • ;
    • ;
    • .

  2. elapsedTime: number; — , , .



, factoids flyShow, , — elapsedTime. 0, . , . , , , . , . … - , .


, , :


interface GameObjects {
    user: UserInGame;
    gifts: { [id: string]: GiftInGame; };
    boosts: { [id: string]: Boost; };
    bombs: { [id: string]: Bomb; };
}

, : , , . — .


GameObjects — . , FPS . GPU 100 . Redmi Note 7 40 FPS ( ). MI 5S 30 FPS, 20 , .


, . .



- Redux, — . Redux , . ? Redux, .


, . , . «» ( . behavior — ). , .


. — tick, . , . tick . :


  • , , , . , , , .
  • ( Redux-), .
    , «» .
  • , gameObjects, . , «», .

25 ( ).


. ( ), React, . - , «- » ( , , ).


Un autre membre de l'équipe ce jour-là.  Photo prise à partir de pixabay.com
. pixabay.com


, FPS- 13. , , - MI 5S, FPS- — 5-6, . .


: performance — , . , , .


FPS=30 , :


  • Redux FPS * ~[ ] ~= 750 , , .
  • React’ FPS * ~2 * ~[ ] .
  • three.js , , WebGL . , .
  • (.. 150 ) 4-6 , .. .
  • CSS- .
  • , FPS 2-4 ( , «» ).
  • React.memo, - , .

React :


  • * ~2 , connect. connect — - HOC, , .
  • , React – (render pass), CPU, GPU.


: React.memo , , «», , DOM-, .


, React + Redux, .


Redux


Redux — . ~1 , . ~1, - — . -, , GameObjects , :


export function setNextGameObjects(payload: GameObjects) {
    return {
        type: 'SET_NEXT_GAME_OBJECTS' as const,
        payload,
    };
}

GameObjects :


  1. , . SET_NEXT_GAME_OBJECTS.
  2. , , GameObjects. Redux , - . SET_NEXT_GAME_OBJECTS.

:


let state = store.getState().gameObjects;
for (const action of this.collectedActions) {
    state = gameObjectsFakeStateReducer(state, action);
}
store.dispatch(setNextGameObjects(state));

collectedActions — , . — , .


— ~25 10 : ~2 . , gameObjectsFakeStateReducer . 25 . , .


? , perf-. 3 :


  1. ES2018, ~50 < ES2018 (, Firefox — ). , spread.
  2. Object.assign 300 .
  3. 500 , « » .

, , , — ES2018, . Firefox.


№3 , … . .


Object.assign. gameObjectsFakeStateReducer gameObjectsFastStateReducer, - :


switch (action.type) {
    case 'PARTIAL_GIFT_STATE_PATCH':
        if (!state.gifts[action.payload.id]) {
            return state;
        }
        Object.assign(state.gifts[action.payload.id], action.payload);
        break;
    // case, default ...
}

? , . , . 25 1 2-4 ( CPU). :


image


«» , , Redux 10 . , - , , connect gameObjects. , .


React


React . — DOM. , , — reselect, React.memo/React.PureComponent, shouldComponentUpdate . .


, , ~2, — 5. , React , .. DOM .


batch React + Redux. , — . , ?


, , connect . , .


. , <ScoreBoard />,


  1. ;
  2. , .

React.PureComponent, connect mapStateToProps. elapsedTime score. Score , elapsedTime — . elapsedTime - render. , FPS, shouldComponentUpdate. HOC’a connect — shouldComponentUpdate .


, «» , :


  1. shouldComponentUpdate, elapsedTime ( ). , HOC’a connect.
  2. mapStateToProps . , , . connect’a.
  3. useSelector. , .. . , , , .
  4. — connect, — . , . ScoreBoard .

— ! ScoreBoard’a connect, , . mapStateToProps , .


, connect. , — - - canvas-, three.js (, , .). render , :


return <div id="map" className={s.map} ref={mapRef} />;


return <canvas id="scene" className={s.scene} ref={sceneRef} />;

, DOM — . , React . , componentDidUpdate, . , componentDidUpdate :


public componentDidUpdate() {
    const { geoPos, orientedRotationQuanternion } = this.props;
    const { map } = this.state;
    map.setQuat(orientedRotationQuanternion);
    map.setCenter([geoPos[0], geoPos[1]], { animate: false });
    map.setZoom(getMapZoomFromHeight(geoPos[2]), { animate: false });
}

, componentDidUpdate . :


  1. connect. , mapStateToProps;
  2. public shouldComponentUpdate() { return false; } — «» «»;
  3. Redux ( ) useStore ;
  4. (store.subscribe(() => { /* … */}));
  5. ;
  6. !

:


store.subscribe(() => {
    const state = store.getState();
    const { map } = this.state;
    const { geoPos, orientedRotationQuanternion } = state.gameObjects.user;

    map.setQuat(orientedRotationQuanternion);
    map.setCenter([geoPos[0], geoPos[1]], { animate: false });
    map.setZoom(getMapZoomFromHeight(geoPos[2]), { animate: false });
}); 

, , .


, - , .



. — React Tree Reconcilation + Commit



React Tree Reconcilation + Commit



React + Redux , :


  • , , , «» React , ;
  • ;
  • « », , ;
  • , .

, . , . , . ? .


:


  1. , , , , . , .
  2. , React’ 30 . : .
  3. , .
  4. . . , .
  5. .
  6. Performance — . CPU — , Intel i9.
  7. - : 12–20 – . , .
  8. , . , , . , .
  9. , , . IE11 , . 70- Edge 18- ( ).
  10. WebGL . - , , -, Firefox linux, . . — . -, . , , « », .

, :


  1. Firefox. . , WebGL, — spread. FPS Firefox , Chrome. , , .
  2. IPhone. Safari IE6 220 . , , . , , . . — - .
  3. . . . , , (, , ). .
  4. . , , . , , , «» .
  5. ( ), – , . .

, , . iOS, Android Web .


All Articles