How to write a game in React, TypeScript?

image

1. React advantage

  • Data driven, based on the change of state or props => view, the previous approach was to directly manipulate the DOM implementation and trigger an event to move the element code like this:

    =>
      this.moveRight = () => {
          this.left += 8;
          this.draw();
      }
    
      this.draw = () => {
          if(this.ele === null){
              this.ele = document.createElement('img');
              this.ele.src = this.url;
              this.ele.style.width = this.width + 'px';
              this.ele.style.height = this.height + 'px';
              this.ele.style.position = 'absolute';
              app.appendChild(this.ele);
          }
          this.ele.style.left = this.left + 'px';
          this.ele.style.top = this.top + 'px';
      };Copy the code

    It’s much more friendly now

    =>
      this.moveRight = () => {
          this.setState( preState => (
              {
                  left: preState.left + 8
              }
          ));
      }
    
      <ContraBG
          left={left}
          top={top}
          status={status}
          toward={toward}>
      </ContraBG>Copy the code
  • The structure is clearer, writing each component to be rendered one by one, so that people can see at a glance what components are loaded while the game is running. The old way to render an element in code style is like

    =>
      const plane = new ourplane();
      plane.draw();Copy the code

    If the render is too much and the structure is too complex, it becomes very difficult to read. The code style now allows you to see all the running components at a glance

    =>
      @observer
      class InGame extends React.PureComponent<InGameProps, {}> {
          render() {
              const { store } = this.props;
    
              return(<InGameBG // Wrap component renders background changes related store={store}> <Contra // Player-controlled character Component store={store}/> <BulletsMap // BulletsMap renders bullets Store ={store}/> <EnemiesMap // Render enemy characters store={store}/> </InGameBG>); }}Copy the code

2. How do you React

  • Flexibility Inheritance between classes is much more flexible, such as

    Aircraft inheritance to flying object => flying object inheritance to dynamic object => Dynamic object inheritance to a characteristic objectCopy the code

    Bullets can also be inherited to flying objects so that flying objects and other classes can be derived from more. Components in React can only be inherited from React.component. instead, you can use the Idea of HOC high order components to render a series of similar components. For example, there are many walls in super Mario Games, they have similar rendering logic, and some methods are needed, which can be generated by writing a high order component of static blocks to manage the code more efficiently.

    = >function WithStaticSquare<TOwnProps>(options: StaticSquareOption):ComponentDecorator<TOwnProps> {
          return Component =>
              class HocSquare extends React.Component<TOwnProps, HocSquareState> {
                  // xxx
                  render() {
                      const { styles, className } = this.state;
                      const passThroughProps: any = this.props;
                      const classNames = className ? `staticHocWrap ${className}` : "staticHocWrap"; const staticProps: WrappedStaticSquareUtils = { changeBackground: this.changeBackground, toTopAnimate: this.toTopAnimate }; // Provides some possible methods to change the background image and call up animation when hitreturn( <div className={classNames} style={styles}> <Component hoc={staticProps} {... passThroughProps}/> </div> ); }}}Copy the code

3. Performance problems

  • When React uses Mobx, Redux, etc., to control the whole game data, if you don’t optimize the rendering, when a property value changes in store, all props components will be rerendered.
  1. Some components need to be written this way with PureComponent

    =>
     class Square extends React.PureComponent<SquareProps, {}> {
         // xxx
     }Copy the code

    One of them is the PureComponent. React.PureComponent was released in React 15.3 on June 29, 2016.

    image


    PureComponent has changed the lifecycle method shouldComponentUpdate, and it automatically checks to see if the component needs to be rerendered. PureComponent will only call the render method if it detects a change in state or props, but this inspection is trivial and means that nested objects and arrays will not be compared
    For more information

  2. Use more components to render, and compare the two methods

    => // Method 1. <InGameBG // Wrapper component renders background changes related store={store}> <Contra // Player-controlled character component Store ={store}/> <BulletsMap // Renders bullets <EnemiesMap ={store}/> <EnemiesMap ={store}/> <InGameBG store={store}/> <div> { bulletMap.map((bullet, index) => {if ( bullet ) {
                         return (
                             <Bullet
                                 key={`Bullet-${index}`} {... bullet} index={index} store={store}/> ); }return null;
                 })
                 }
             </div>
             <EnemiesMap
                 store={store}/>
     </InGameBG>Copy the code

    The difference of the two methods is whether is to render a bullet through the component rendering or directly in the parent component rendering, including the performance of the method 2 there will be a big problem, when a bullet changes makes the largest container to rendering, which all subcomponents would to judge whether need to apply colours to a drawing, can appear the interface card. Method 1 only renders bullets with data changes.

4. What to watch out for

  • Remove listeners in time. When a component is uninstalled, remove event listeners and time functions of the component. Like the game start component

    =>
      class GameStart extends React.Component<GameStartProps, {}> {
          constructor(props) {
              super(props);
    
              this.onkeydownHandle = this.onkeydownHandle.bind(this);
          }
          componentDidMount() {
              this.onkeydown();
          }
          componentWillUnmount() {
              this.destroy();
          }
          destroy(): void {
              console.log("Game on! GameStart Component destroy ....");
              window.removeEventListener("keydown", this.onkeydownHandle);
          }
          onkeydownHandle(e: KeyboardEvent): void {
              const keyCode: KeyCodeType = e.keyCode;
              const {  store } = this.props;
              const { updateGameStatus } = store;
              switch ( keyCode ) {
                  case 72:
                      updateGameStatus(1);
                      break;
              }
          }
          onkeydown(): void {
              window.addEventListener("keydown", this.onkeydownHandle);
          }
          render() {
              return (
                  <div className="gameStartWrap"> </div> ); }}Copy the code

5. Recently wrote super Contra effects with GitHub

Super contra

Super contra

Github.com/xiaoxiaojx/…

Thank You