オンライン対戦フェアリーチェスウェブアプリをつくる
コマンドラインから起動するフェアリーチェスアプリを前に作ったのですが、ブラウザで通信対戦ができるようにしたいので作りたいなという気持ち…
フェアリーチェスができるサービスって実はあまりないような気もする。
もとのコードも結構大きくてぐちゃぐちゃなので、この機会に画像ファイルとかゲームや駒のデータを分離してきれいに書き直したいという動機もある。
とりあえず
画像ファイル(駒・動き説明): Storage
駒・ゲームのデータ : Cloud Firestore
通信 : Realtime Database
で考えています。
Alice chess online も似たようなことをしているんですが、あれは React とかを使っていないのと Canvas API を使っている。
結局状態管理とかが欲しくなったりして大変ではあったので今度は React 使いたい。Canvas は Konva よさそう。
Alice Chess のほうはゲームが一種しかないけど、今回はゲームがたくさんあるので、SPA ではなくなりそう。
Next, Chakra UI, Storybook, ESLint, Jest + React Testing Library になりました。ちゃんと環境整えようとするとセットアップが割と大変ですね…
前作ったアプリ(このチェスアプリも含む)を改修するときに大変な想いをしたので、今回は可読性・保守性を高くしようと欲張りセットです。
駒やゲームのデータは更新することがほとんどなくだいたい参照するだけだから Firestore
対戦中のデータは頻繁に更新されるから Realtime Database
というふうに分けていたけど、データ量多くないし両方 Realtime Database に入れても無料枠に収まりそう…とか思い始めた。
いや、そもそも駒とゲームのデータはリアルタイム性不要だし自分しか書き込みしないし、CMS で管理してもいいよなあ
いちおう Storage に JSON 入れる方法もあるらしいけど、管理がつらそうなので CMS でいきたいと思います。
画像ファイルも入れられるので Storage も使わずに
駒・ゲームのデータ・画像を CMS で
i18n 対応も翻訳データを分離したいけど、locize は有料だし、CMS に置くのもあまりやりやすくないし、量自体そんなに増えないし、ローカルに置こうか…
next-i18next というのがあって割と簡単にできました。
調べたら locize 以外にもいろいろあるらしい…
こういうサービスを使って API でリソースを取得して表示をしようとしていろいろ試行錯誤したのですが、結局うまく行かず断念しました。
CLI でローカルにダウンロードして、それを next-i18next から使うというのはできるので、
ローカルで JSON 書かなくてよくなるだけでもいいやと思い i18nexus を使いました。
さて、駒の情報を CMS に登録します。
駒の名前とか価値はいいものの、駒の動きをどう扱えばいいのか…
現行だと Python のコードで関数を作って使っているんだけど、関数は登録できないのでちょっと考えないといけないですね。
関数には駒の位置を渡していたりします。平行移動してあげればいいので、駒の位置を (0, 0)
として、駒が原点にあるときの移動可能な動きの情報を JSON で管理することにしました。
Pawn の例
{
"moves": [
{
"move": {
"type": "rider",
"direction": [0, 1],
"length": 2,
"option": "move-only",
},
"when": "piece.moveCount === 0",
},
{
"move": {
"type": "leaper",
"direction": [0, 1],
"option": "move-only",
},
},
{
"move": {
"type": "leaper",
"direction": [1, 1],
"option": "capture-only",
},
},
{
"move": {
"type": "leaper",
"direction": [-1, 1],
"option": "capture-only",
},
},
],
}
CMS で JSON をフィールドとして持たせてもバリデーションができなかったりするのでそこのつらさはある🥺
人力で JSON をいちいち指定するのは本当につらいので、できるだけ楽したい気持ちがある。
なのできれいにすぐに使えるデータを入れるのではなく、クライアント側で parse してしまおうかという。
{
"id": "N",
"color": "W"
}
とかではなくて WN
としてしまってもいいよなあ。
とか考えながら CMS にコンテンツを作成していく。
GraphCMS からデータを取得するのは比較的楽にできました!
graphql
と graphql-request
を入れてからこのように書いて
import { GraphQLClient } from 'graphql-request';
const graphcms = new GraphQLClient(process.env.GRAPHCMS_URL ?? '');
export const fetchGql = async <T>(query: string) => graphcms.request<T>(query);
getStaticProps
でこんな感じに書けばデータを取得できます。
export const getStaticProps: GetStaticProps = async () => {
const data = await fetchGql<GamesResponse>(`
{
games {
id
name
description
}
}
`);
return {
props: {
games: data.games,
},
};
};
権限とか Publish 状態とかは気を付けないとデータが取得できないので注意しましょう😇