React Native ゲーム開発 Whack-A-Mole(もぐらたたき) 第二回

## Whack-A-Mole(もぐらたたき) 第二回

フロントエンジニアの、YAMATAKUです。
[前回](https://qiita.com/team-lot/items/a0130c168ec38d12de82)、ベースUIの実装まで行いました。今回は、ゲームの核である「もぐら」の実装をしていきます。

## もぐらクラス作成

```jsx:Mole.js

import React, { Component } from 'react';
import { 
  View,
  StyleSheet,
  Button,
  Image,
  TouchableWithoutFeedback
} from 'react-native';
import Images from './assets/Images';
import SpriteSheet from 'rn-sprite-sheet';
import Constants from './Constants';

export default class Mole extends Component {
  constructor(props) {
    super(props);

    this.mole = null;
  }

  pop = () => {
    this.isPopping = true;
    this.mole.play({
      type: "appear",
      fps: 24,
      onFinish: () => {
        this.actionTimeout = setTimeout(() => {
          this.mole.play({
            type: "hide",
            fps: 24,
            onFinish: () => {
              this.isPopping= false;
              this.props.onFinishPopping(this.props.index);
            }
          })
        }, 1000);
      }
    })
  }

  whack = () => {

  }

  render() {
    return (
      <View style={{flex: 1}}>
        <SpriteSheet
          ref={ref => (this.mole = ref)}
          source={Images.sprites}
          columns={6}
          rows={8}
          width={100}
          animations={{
            idle: [0],
            appear: [1, 2, 3, 4],
            hide: [4, 3, 2, 1, 0],
            dizzy: [36, 37, 38],
            faint: [42, 43, 44, 0],
            attack: [11, 12, 13, 14, 15, 16],
            heal: [24, 25, 26, 27, 28, 29, 30, 31, 32, 33],å
          }}
        />
        <TouchableWithoutFeedback onPress={this.whack} style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0}}>
          <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0}} />
        </TouchableWithoutFeedback>
      </View>
    )
  }
} 
```

## App.js修正

```jsx:App.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

import React, { Component } from "react";

// 省略 //

import Mole from './Mole';

const DEFAULT_TIME = 20;
const DEFAULT_STATE = {
  // 省略 //
}

export default class MainScreen extends Component {
  constructor(props){
    super(props);
    this.state = DEFAULT_STATE;
    this.moles = [];
    this.molesPopping = 0;
    this.interval = null;
    this.timeInterval = null;
  }

  componentDidMount = () => {
    //setStateで状態更新、第2引数はcallback関数
    this.setState(DEFAULT_STATE, this.setupTicks);  
  }

  setupTicks = () => {
    //もぐらの更新スピード決定
    let speed = 750 - (this.state.level * 50);
    if (speed < 350) {
      speed = 350;
    }

    //もぐら更新スタート
    this.interval = setInterval(this.popRanndomMole, speed);

    //時間カウントスタート
    this.timeInterval = setInterval(this.timerTick, 1000);
  }

   //もぐら位置抽選
  randomBetween = (min, max) => {
    return Math.floor(Math.random() * (max - min + 1) + min);
  }

  //もぐらアクション終了で表示もぐらカウントをマイナス
  onFinishPopping = (index) => {
    this.molesPopping -= 1;
  }

  //表示するもぐら抽選して表示
  popRanndomMole = () => {
    //12匹の準備ができているかチェック
    if (this.moles.length != 12) {
      return;
    }
    
    let randomIndex = this.randomBetween(0, 11);  //表示もぐら抽選
    //当選したもぐら位置が非表示かつ、表示中のもぐらが3匹以下なら表示する
    if (!this.moles[randomIndex].isPopping && this.molesPopping < 3) {
      this.molesPopping += 1;
      this.moles[randomIndex].pop();
    }
  }

  //時間更新
  timerTick = () => {
    if (this.state.time === 0) {
      clearInterval(this.interval);
      clearInterval(this.timeInterval);
      this.setState({
        cleared: true
      })
    }
    else {
      this.setState({
        time: this.state.time - 1
      })
    }
     
  }

  render() {
    let healthBarWidth = (Constants.MAX_WIDTH - Constants.XR * 100 - Constants.XR * 60 - Constants.XR * 6) * this.state.health / 100;
    return (
      <View style={styles.container}>
        <Image style={styles.backgroundImage} resizeMode="stretch" source={Images.background} />
        <View style={styles.topPanel}>
          <SafeAreaView >
            // 省略 // 
          </SafeAreaView>
        </View>
        <View style={styles.playArea}>
          
          {//縦に4段
          Array.apply(null, Array(4)).map((el, rowIdx) => {
            return (
              <View style={styles.playRow} key={rowIdx}>
                {//横に3列
                Array.apply(null, Array(3)).map((el, colIdx) => {
                  let moleIdx = (rowIdx * 3) + colIdx;

                  return (
                    <View style={styles.playCell} key={colIdx}>
                      <Mole
                        index={moleIdx}
                        ref={(ref) => { this.moles[moleIdx] = ref }}
                        onFinishPopping={this.onFinishPopping}
                      />
                    </View>
                  )
                })}
              </View>
            )
          })}
        </View>
      </View> 
    )
  }
 }

const styles = StyleSheet.create({
  
// 省略 //

  playArea: {
    width: Constants.MAX_WIDTH,
    marginTop: Constants.YR * 250,
    height: Constants.MAX_HEIGHT - Constants.YR * 250 - Constants.YR * 112,
    flexDirection: 'column',
  },
  playRow: {
    height: (Constants.MAX_HEIGHT - Constants.YR * 250 - Constants.YR * 112) / 4,
    width: Constants.MAX_WIDTH,
    flexDirection: 'row',
  },
  playCell: {
    width: Constants.MAX_WIDTH / 3,
    height: (Constants.MAX_HEIGHT - Constants.YR * 250 - Constants.YR * 112) / 4,
    alignItems: 'center'
  }
})

```

## 確認

```
$ react-native run-android
```

![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/616010/75a7fe30-0119-204e-eb74-3a91ebe0f824.gif)

もぐらがアニメーションして現れます。
次回の記事で、もぐらを叩く機能を実装してゲーム完成させようと思います。


## おまけ
今回、TEAM用に新しくGitHubアカウントを開設しましたが、自分のMacで作成した鍵が、別のGitHubと衝突したため、以下の様な設定を行いましたので、メモとして残します。

以下、Macの場合

```
1. 鍵作成(hogeは適宜置き換え)
$ ssh-keygen -f ~/.ssh/hoge_key

2. Gitにhoge_keyを登録

3. 設定ファイル修正
$ vi  ~/.ssh/config 

host github.hogehoge.com
 HostName github.com
 IdentityFile ~/.ssh/hoge_key
 User git
 TCPKeepAlive yes
 IdentitiesOnly yes

//保存
:wq + Enter

//適用
$ source ~/.ssh/config 

[設定完了]

・remote addの場合
$ git remote add origin git@github.hogehoge.com:******/******.git

・cloneの場合
$ git clone git@github.hogehoge.com:******/******.git

これで、複数のGitHubの設定が有効になり、リモート操作も可能になります

```
すいません、Windowの場合も基本は同じだと思われます。