180名が参加するeスポーツ大会(ApexLegends)の集計処理を爆速で処理する話

# 180名が参加するeスポーツ大会(ApexLegends)の集計処理を爆速で処理する話
## 導入
この記事は社員の9割がゲーマーの会社エイプリルナイツAdventCalendar 11日目の記事です。

https://adventar.org/calendars/8003

- 最初に好きなクリスマスソングを記載するルールなんですが2本目なので被らなそうなやつにします。
   - 「クリスマスなんて大嫌い!! なんちゃって♡／クレイジーケンバンド。」
     - J-PHONEのCMソングでした。当時J-PHONEユーザーだったのでCMがきっかけでCKBを聞くようになった気がします。

<iframe width="560" height="315" src="https://www.youtube.com/embed/EM_-n5xP6Y8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

## GCD事業とcogme cupの紹介
- 弊社には[ゲーミングカルチャーデザイン事業部](https://april-knights.com/gaming_culture/)という他では絶対に耳にしない名前の部署があります。
- ミッションは **「ゲームという共通言語でコミュニケーションを活性化する」** という文化の普及です。
### cogme cupの概要
- [cogme cup](https://cogme.jp/cup)はその取組の1つであり、企業eスポーツ部を成立させるための成果発表の場です。
- ApexLegendsの大会で、1チーム3名20チームの合計60名で試合が行われます。
- 前々回のcogmecup＃4では予選が3ブロックあったので60チーム180名が同時進行で試合する大規模な大会となりました。

https://twitter.com/cogme_official/status/1526859798171779072?s=20&t=QCuEIPJp13e82oyMCWLVdQ

- 今後も継続してApexLegendsの大会やscrimを開催していきますので、今まで知らなかった方は同僚をお誘いの上、ぜひ企業eスポーツ部登録をして大会Discordサーバーへお越しください。

https://media.cogme.jp/12042/

## 爆速じゃない頃の集計の話
- 私がGCD事業部に参画してすぐにcogmecup#1が開催され、少しだけ手伝っていました。
- そのころは20チーム60名の参加でしたが、集計はゲーム画面に表示されたスコアを読み上げ、Googleスプレッドシートに手入力していました。
  - 大会は全部で3試合行われるので、その作業を3回繰り返す必要がありました。
- その時の様子がアーカイブとして残っていましたので大会運営の裏側に興味のある方はどうぞ

https://youtu.be/x26zVHf0tt0?t=1340

- 大会はリアルタイムで配信していましたが、配信内で各試合ごとの集計結果を発表できていませんでした。
- この方法で3ブロック60チーム同時進行の規模を開催するのはとても無理！

## 爆速集計を実現するアーキテクチャ
- cogmecup#2の開催が迫る中、本格的に手伝うことになり、まずあの集計作業をなんとかしたい（見てられない）と思ったので、色々資料を漁っていると試合後の結果をAPIで取得できるらしいことがEAから提供される資料に書いてありました。
- 既存の集計に使っているスプレッドシートまわりも非効率な関数が多かったので大幅に書き直し、今の集計システムができあがりました！
:::note warn
現在、ApexLegendsのカスタムマッチ（大会専用モード）は一般開放しておらず、エレクトロニック・アーツ（EA）から許諾を受けたユーザーのみ利用できます。APIについては一般公開されていないことから詳細について記載するのは控えます。
:::

### 概要図
#### 全体
- 構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262707/3ab24ea6-f0dd-8fa1-d7b4-76ba9a444633.png)

- シーケンス図
   - 集計担当者はまず試合が終わったと同時にApexAPIからデータ取得をします。
   - 集計結果を確認したらNodeCGでもデータの更新をするためのボタンを押下して最新状態を取得します。
   - オペレーションはこの2つ。ボタンを押して内容の確認をするだけです。あとは全自動。
```mermaid
sequenceDiagram
  actor U as 集計担当
  participant GAS
  participant sh as Sheets
  participant api as ApexAPI
  participant node as NodeCG
  U->>+GAS: データ取得リクエスト
  GAS->>api: fetch
  api-->>GAS: 試合結果データ
  GAS-->>-sh: 試合結果データ
  sh->>sh: 集計処理
  sh-->>U: 集計結果シート
  U->>node: データ取得リクエスト
  node->>+sh: fetch(SheetsAPI)
  sh-->>-node: 集計結果
  node->>node: 画面描画
  node-->>U: 集計画面HTML
```
#### 扱っている技術
- Googleスプレッドシート
    - エントリー管理
    - 集計表
- GoogleAppsScript
    - ApexAPIの取得
    - エントリー管理
    - Slackへの更新通知
- NodeCG
    - 配信画面の作成
- OBSStudio
    - NodeCGで作ったHTMLの表示
- vMix
    - 大会配信画面へNodeCGで作った集計画面の切り替え

#### スプシの構成

![無題クリップ_121022_044425_PM.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262707/5a3fcd1b-fa6d-e759-fa2e-b732b5f40371.jpeg)


### いかに手作業をなくすかについて
- ApexAPIから取得するようになるだけでデータ入力が減って大幅に省力化できます。
- 取得したデータだけではeスポーツ大会の配信に使えないのでさらなる加工が必要です。

<details><summary>APIから取得できるデータ</summary>

- ゲーム内の英語表記のチーム名
- ゲーム内のプレイヤーID
- 取得できるデータは1試合ごと
</details>

<details><summary>データ加工としてやらなければいけない作業</summary>

- チーム名、プレイヤー名を日本語表記に変換
- 企業eスポーツ部大会なので企業ロゴも載せたい
- 3試合ごとに集計して、ランキング作成
- 個人ごとの集計、ランキング作成
- Top3チームは個人ごとの成績を別途用意する

</details>

### ポイントに絞ってざっくり解説
#### GoogleAppsScript(GAS)
- ApexAPIからデータ取得
  - fetchしてシートに転記するだけ
- 関数ではやりづらい情報整理
  - フォームで受け取る情報は1チーム1行
  - フォーム情報をプレイヤーごとに正規化
  - フォームが送信されて情報が更新するたびにGASが実行され、常に最新のプレイヤー情報が参照できるようにしています。

<details><summary>フォーム情報の正規化イメージ</summary>

フォームの回答
|  チーム名  |  プレイヤー1の名前  | プレイヤー1のランク | プレイヤー2の名前 | プレイヤー2のランク|
| ---- | ---- | ---- | ---- | ---- |
|  team1  |  AAA  | ゴールド | BBB | シルバー |
|  team2  |  CCC  | プラチナ | DDD | ゴールド |

プレイヤーごとの情報
|  チーム名  |  プレイヤー名  | ランク |
| ---- | ---- | ---- |
|  team1  |  AAA  |  ゴールド  |
|  team1  |  BBB  |  シルバー  |
|  team2  |  CCC  |  プラチナ  |
|  team2  |  DDD  |  ゴールド  |

</details>


#### Query関数超便利
- Query関数はSQLと同様にSelect,Where,OrderByに加えて、SumやGroupByも使えます。
- 集計に必要な要素はほぼ揃っているのでQuery関数やvlookup関数を組み合わせることでほぼ完結します。
![スクリーンショット_121022_041737_PM.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262707/c76360ed-e34e-f4c9-9fb5-a3c06556faf6.jpeg)

#### importrange関数
- 他のファイルを開かずとも情報を確認するため
- 上記Query関数の画像右側の日本語チーム名やロゴファイル情報はvlookup関数で取得してますが、その情報はGoogleフォームで回収したエントリー情報からimportrange関数を使って参照しています。
![スクリーンショット_121022_042257_PM.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262707/d449a108-31ad-2b4d-c872-c2291de9c16f.jpeg)

#### NodeCGの話
- NodeCGを何に使っているか
    - 集計データ取得
        - SheetsAPI
    - HTML,CSS,Javascriptで配信画面を描画
    - NodeCGならではの部分
        - データ取得し直しはダッシュボードからボタンクリックで
        - 静的な情報の更新
          - ラウンド数など

- NodeCGによって削減できた作業
    - 配信で使うアセットの生成にかかる時間
        - AfterEffectsへのデータ入力
        - 動画レンダリングの時間
NodeCGのダッシュボードでボタンをクリックすると、最新データの取得と配信画面の描画が数秒で実行されます。
弊社のWebデザイナーがCSSでアニメーション職人と化して作ってくれました。
![nodecg-animation (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262707/c741d57d-35de-3589-7be4-bc213a2a9272.gif)


- NodeCGはRTAinJapan界隈でよく使われている技術みたいです。以下の記事には大変お世話になりました。

https://qiita.com/Hoishin/items/36dcea6818b0aa9bf1cd

https://zenn.dev/cma2819/articles/start-nodecg-01

https://zenn.dev/cma2819/articles/nodecg-implements-replicant

https://qiita.com/pasta04/items/d676d9c2fb716176f665

## まとめ
- どのぐらい爆速になったか
  - ゲーム内で**XXX is THE CHAMPION**と出た瞬間にAPIからデータ取得ができ、数分でランキング画面の描画ができるようになりました。

- 弊社はeスポーツ大会運営がメインの会社ではありません。大会の規模が大きくなったからといってすぐに人を増やすことができず、こうした技術を使って効率化することでなんとか乗り切っている状況です。eスポーツ大会の配信制作でITエンジニアががっつり関わることがあまりないみたいなので、この分野にもどんどんITエンジニアが参画してきてソリューションが生まれるとみんな幸せになれると思います！
- 明日は大会用Discordサーバー運営をラクにするDiscordBOTについて書きますのでそちらもお楽しみに！