哈Ha!在那些不太遥远的年代,在我进入“程序员”学院的第一年,我喜欢问同学们一个问题:“你为什么还要去这里学习?” 当然,我没有对答案进行准确的统计,但是我确实记得:一半以上的人想做游戏。大多数以这种方式回答的人并没有为大量的不同类型的“数学家”和“物理学家”做好准备,在前两年的学习中我们不知所措。并非所有人都能幸存-到五个人满为患的第二年结束时,有三个残缺不全。
不久前,我们的前端团队有机会尝试扮演gamedev的角色。简而言之,任务是这样的:制作一个真实的3D游戏,以便您只需打开浏览器即可玩。甚至移动。即使在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 .