Hello, Habr! In those not so distant years, in the first year of the "programmer" faculty, I liked to ask fellow students the question: "Why did you even go here to study?" Of course, I did not keep accurate statistics of answers, but I remember for certain: more than half wanted to make games. Most of those who answered this way were not ready for the abundance of different types of “mathematicians” and “physicist” with whom we were overwhelmed in the first two years of study. Not all survived - by the end of the second year of the five overcrowded groups there were three incomplete.
Not so long ago, our front-end team had the opportunity to try themselves in the role of gamedev. Very briefly, the task is this: to make a real 3D game, so that you can play just by opening the browser. Even mobile. Even in webview.

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

CodeFest .
MMO TPS , , . , , , . CodeFest. , .
2 , , 2 . , , .
: -, — , . , N , — 1 . , — 15 . , , 16-. , , :
- 30 . CodeFest, — , «». , .
- «» . , :
- 90 CPU;
- 120 ;
- 270 /c — , 675 /c — .
- 4,2 /c. , . — UDP WebRTC, . .
- . 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
:
stage: 'factoids' | 'citySelect' | 'flyShow' | 'game' | 'results';
— . :
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, . - , «- » ( , , ).

. 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 :
- , .
SET_NEXT_GAME_OBJECTS
. - , , 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 :
- ES2018, ~50 < ES2018 (, Firefox — ). , spread.
- Object.assign 300 .
- 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;
}
? , . , . 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 />
,
- ;
- , .

React.PureComponent, connect mapStateToProps. elapsedTime
score
. Score
, elapsedTime
— . elapsedTime
- render. , FPS, shouldComponentUpdate. HOC’a connect — shouldComponentUpdate .
, «» , :
- shouldComponentUpdate,
elapsedTime
( ). , HOC’a connect. - mapStateToProps . , , . connect’a.
- useSelector. , .. . , , , .
- — 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 . :
- connect. , mapStateToProps;
public shouldComponentUpdate() { return false; }
— «» «»;- Redux ( ) useStore ;
- (
store.subscribe(() => { /* … */})
); - ;
- !
:
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 , ;
- ;
- « », , ;
- , .
, . , . , . ? .
:
- , , , , . , .
- , React’ 30 . : .
- , .
- . . , .
- .
- Performance — . CPU — , Intel i9.
- - : 12–20 – . , .
- , . , , . , .
- , , . IE11 , . 70- Edge 18- ( ).
- WebGL . - , , -, Firefox linux, . . — . -, . , , « », .
, :
- Firefox. . , WebGL, — spread. FPS Firefox , Chrome. , , .
- IPhone. Safari IE6 220 . , , . , , . . — - .
- . . . , , (, , ). .
- . , , . , , , «» .
- ( ), – , . .
, , . iOS, Android Web .