3D-Arcade im Browser: Wie wir das Spiel auf React + Redux gemacht haben

Hallo Habr! In diesen nicht allzu fernen Jahren, im ersten Jahr der "Programmierer" -Fakultät, stellte ich meinen Kommilitonen gern die Frage: "Warum bist du überhaupt hierher gegangen, um zu studieren?" Natürlich habe ich keine genauen Statistiken über die Antworten geführt, aber ich erinnere mich mit Sicherheit: Mehr als die Hälfte wollte Spiele machen. Die meisten derjenigen, die auf diese Weise antworteten, waren nicht bereit für die Fülle verschiedener Arten von „Mathematikern“ und „Physikern“, mit denen wir in den ersten zwei Studienjahren überfordert waren. Nicht alle überlebten - am Ende des zweiten Jahres der fünf überfüllten Gruppen waren drei unvollständig.


Vor nicht allzu langer Zeit hatte unser Front-End-Team die Gelegenheit, sich in der Rolle des Gamedev zu versuchen. Ganz kurz lautet die Aufgabe: ein echtes 3D-Spiel zu erstellen, damit Sie einfach durch Öffnen des Browsers spielen können. Sogar mobil. Auch in der Webansicht.



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



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


Foto von der CodeFest-Gruppe auf VK / https://vk.com/codefest.  Die Quelle des Fotos ist hier.
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, . - , «- » ( , , ).


Ein weiteres Teammitglied an diesem Tag.  Foto von pixabay.com genommen
. 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). :


Bild


«» , , 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