Halo, Habr! Pada tahun-tahun yang tidak terlalu jauh, pada tahun pertama saya di fakultas "programmer", saya suka bertanya kepada sesama siswa pertanyaan: "Mengapa Anda bahkan pergi ke sini untuk belajar?" Tentu saja, saya tidak menyimpan statistik jawaban yang akurat, tetapi saya ingat dengan pasti: lebih dari setengahnya ingin membuat game. Sebagian besar dari mereka yang menjawab dengan cara ini tidak siap untuk kelimpahan berbagai jenis "ahli matematika" dan "ahli fisika" dengan siapa kita kewalahan dalam dua tahun pertama studi. Tidak semua selamat - pada akhir tahun kedua dari lima kelompok yang penuh sesak ada tiga yang tidak lengkap.
Belum lama berselang, tim front-end kami diberi kesempatan untuk mencoba sendiri dalam peran gamedev. Secara singkat, tugasnya adalah ini: untuk membuat game 3D nyata, sehingga Anda dapat bermain hanya dengan membuka browser. Bahkan ponsel. Bahkan dalam tampilan web.

, , , — 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 .