浏览器中的3D街机:我们如何在React + Redux上制作游戏

哈Ha!在那些不太遥远的年代,在我进入“程序员”学院的第一年,我喜欢问同学们一个问题:“你为什么还要去这里学习?” 当然,我没有对答案进行准确的统计,但是我确实记得:一半以上的人想做游戏。大多数以这种方式回答的人并没有为大量的不同类型的“数学家”和“物理学家”做好准备,在前两年的学习中我们不知所措。并非所有人都能幸存-到五个人满为患的第二年结束时,有三个残缺不全。


不久前,我们的前端团队有机会尝试扮演gamedev的角色。简而言之,任务是这样的:制作一个真实的3D游戏,以便您只需打开浏览器即可玩。甚至移动。即使在webview中。



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



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


VK / https://vk.com/codefest上CodeFest组的照片。 照片的来源在这里。
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, . - , «- » ( , , ).


那天有另一个团队成员。 照片来自Foto.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). :


图片


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