A React RPG Game!

Published Dec 09, 2017Last updated Jan 16, 2018
A React RPG Game!

Since I was a kid I'm a big fan of video game RPGs - specially JRPGs like the Final Fantasy series! So for my first article here on codementor I thought I could try something fun a build small versions of a RPG fighting system using my favorite framework, React.

Let's start by writing a React container component that looks like the menu from Final Fantasy as seen above. It's a really simply component, consisting of a div wrapper which is styled to look like the FF menu:

const MenuContainer = ({ children, style }) =>
  <div className="menu" style={style}>
    {children}
  </div>

You can checkout the styling in the CSS contained in the codepen below. Let's also create a Game component, which will render all whole game:

const Game = () => 
  <MenuContainer>
    Menu
  </MenuContainer>
  
 ReactDOM.render(<Game />, document.getElementById('game'))

Resulting in:

The next step is to build a Menu where the use can select an option. For that we create the MenuSelect component (full code in the next codepen):

class MenuSelect extends React.Component {
  
  (...)
  
  render() {
    return (
        <MenuContainer           
          style={{...this.props.style, paddingLeft: '44px'}}>
        {
          this.props.items.map( (item, index) => 
            <div 
              className="menu_item_wrapper" 
              style={this.props.itemStyle}              
              ref={e => this.itemRefs[index] = e}
              key={index}>
             { this.props.renderItem(item, index) }
            </div>
          )
        }
        {
          this.props.active ?
          <div className="menu_hand" style={{ top: this.state.handTop, left: this.state.handLeft }}>
            <img src="http://res.cloudinary.com/forte-3d/image/upload/v1512749704/hand_gkm8wr.png"/>
          </div>  : null
        }
        </MenuContainer>        
     )
  }
}

The basic Idea here is to have a list container which renders arbitrary components; the rendering of each component is done by the function renderItem supplied as a prop, which fed by the given items array, so that renderItem(item, index) renders the given item. The user selects the options by using the arrow keys in the keyboard, and presses enter or the space bar to actually select the item, and then the prop function onChoice is called. Now the Game component becomes:

const renderItem = name => <span>{name}</span>

const Game = () =>
  <div className="game-wrapper">
    <MenuSelect
      active={true}
      onChoice={i => {console.log("You have chosen", i)}}
      renderItem={renderItem}
      items={["Some character", "Other caracther", "Hyper Link", "Cid"]} />
  </div>

Well, now that the basic UI unit is working, we can start thinking about the game logic and structure! We will build a very simple model: one player fighting against one enemy. Both player and enemy are defined by their stats. In our simple model, an entity's stat is somethin like:

const entityStat= {
  hp: 100,
    time: 2000,
    attack: 50,
    defense: 32
}

Here, of course, hp is the entity's life value, time is the interval between turns, and attack and defense are... attack and defense.

As for the screens, there will be an start screen, with only on button that the users select to start the game, the game screen, where the player will select an action at each turn until either his HP or the enemy's HP run out, and the game ends, returning to the initial screen.

A quite minimalistic state for describing this game would be:

const game = {
  isRunning: false,
    playerStats: { hp: 100, time: 2000, attack: 50, defense: 32 },
    enemyStats: { hp: 50, time: 1200, attack: 20, defence: 15 },
    isPlayerTurn: false
}

this is the game state in the sense that it defines what will be rendered in the screen at any given time; there are other transient objects, e.g., timers to clock each turn that are necessary for the functioning of the game as well.

So let's try to structure this game state model around react components. Let's start with a small structure that simply renders a start screen:

const Start = ({ start }) =>
  <MenuSelect
    active={true}
    onChoice={start}
    items={["Start Game"]}
    renderItem={ item => <span>{item}</span> }
    >
  </MenuSelect>

class Game extends React.Component {
  constructor(props) {
    super(props)
    this.state = this.props.initialState ? {...this.props.initialState}{...initialState}
  }
  
  
  onStart() {
    this.setState(setIsRunning(true))
  }
  
  render() {    
    return (
      <div className="game-wrapper">        
        {
          this.state.isRunning ? 
            <span>game</span> :
          
            <Start start={this.onStart.bind(this)} />
        }
      </div>
    )    
  }  
}

So now we have a start screen, and when the user uses 'Start Game', he sees a screen with only the word 'game' on it. That should be the main game screen. Let's write it.

For the main screen we'll have to menu panels side by side, one for action selection, the other showing player stats. So we'll change the render function to:

render() {    
    if (!this.state.isRunning) {
      return (
        <div className="game-wrapper">                
          <Start start={this.onStart.bind(this)} />        
        </div>
      )
    }    
    return (
      <div className="game-wrapper">        
        <div className="main-game">          
          <MenuContainer className="menu main-game-panel">
            A
          </MenuContainer>
          <MenuContainer className="menu main-game-panel">
            B
          </MenuContainer>          
        </div>
      </div>
    )    
  }  
}

with:

.main-game {
  display: flex;
  flex-direction: row;
  flex: 1;
  height: 100%;
}

.main-game-panel {
  display: flex;
  flex: 1;
  align-items: stretch;
}

The final result is:

Discover and read more posts from Daniel Fortes
get started