BCGゲームのAPIを利用して個人用ガチャを作って遊ぶ

# 概要
こんリリス！BCGゲーム「クリプトスペルズ」にドはまりしている**maepu**です。
（この挨拶はクリプトスペルズ公式キャラのリリスちゃんの挨拶を真似してみました）
挨拶はさておき、表題の件をお話しします。

BCGゲーム「[**クリプトスペルズ**](https://www.fisco.co.jp/media/crypto/cryptospells-about/)」とは、デジタルトレーディングカードゲームで、**レアカードがNFT(Non-Fungible Token)**になっています。
ブロックチェーンやNFTの説明をすると長くなるので、ここでは割愛します。

クリプトスペルズが気になるという方は[**クリプトスペルズ公式**](https://cryptospells.jp/)をご参照ください。

さて、クリプトスペルズには採掘という**ガチャ**があります。
はっきり言うとクリプトスペルズのガチャは無課金プレイをしているとまったく当たりません。ガチャの排出率アップの方法はありますが、かなり労力と課金が必要になってきます。

なので、少しでもレアカードを当てた気分を味わいたいと思いで、自分だけのガチャを作りました。（中毒者...）

作るにあたり、個人のお遊び程度なので、環境構築してプロジェクト管理や一般公開など考えないで作ることを考えました。
そこで、私の知っているお手軽プロトタピングサービス「**enebular**」を使って作る事にしました。
※こちらの記事はQiitaアドベントカレンダーに投稿しています。遅れてしまい申し訳ありません。

**本記事の対象者**
* enebularに興味があるor知っているor使ったことある方
* Node-REDに興味があるor知っているor使ったことある方

# 作ったもの
シンプルに「ガチャを引く」ボタンを押すと、ランダムにカードを決めて画像を表示します。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">素人ながら思い付きでクリスペガチャを作ってみました。技術足りないのでお粗末ですが、気分転換に遊べてます！リリースされているカードは連番だと思いましたが、歯抜けになっているようです。それはハズレとして猪表示します🐗<a href="https://twitter.com/hashtag/%E3%82%AF%E3%83%AA%E3%82%B9%E3%83%9A?src=hash&amp;ref_src=twsrc%5Etfw">#クリスペ</a> <a href="https://t.co/zede4RESzc">pic.twitter.com/zede4RESzc</a></p>&mdash; まえぷー (@kmaepu) <a href="https://twitter.com/kmaepu/status/1470280466226106370?ref_src=twsrc%5Etfw">December 13, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

せっかくなので、通常のガチャでは排出されないレジェンドカード（世界に19枚しかないカード）も当てられるようにしてみました！なかなか出ませんが。


# 技術的なところ

クリプトスペルズにはAPIが用意されています。
URL：https://cryptospells.gitbook.io/cryptospells/other/api

この中にカード情報を取得するAPIがあり、カードの画像が公開されてるURLを取得できます。これを利用して、ガチャに表示する画像をにしています。

システム構成はシンプルで、次の図のようにNode-REDのフローからクリプトスペルズAPIにリクエストし、画像のURLを取得します。UIはNode-REDのダッシュボードを利用しブラウザで表示しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183282/84847947-a9a5-6abf-cd0a-1bebc57620a1.png)

## Node-REDのフロー
　今回、enebularに用意されているNode-REDを利用し、サーバー側の処理を構築しました。
　Node-REDは「ノード」と呼ばれる処理ブロックをビジュアル的に接続してシステムを構築するツールです。詳しく知りたい方は[**Node-RED日本ユーザ会**](https://nodered.jp/)をご参照ください。

今回作成したフローを次の図に示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183282/f8f98ac2-7b44-5b30-26f1-d97a0a40c996.png)

このフローでは、主に次の処理を行っています。
（１）ダッシュボードでUI表示
（２）UIのボタンが押されたら、クリプトスペルズAPIで画像URLを取得
（３）UIの画像を更新

中でも躓いたのが、（３）の**UIの画像を更新**でした。
次の図に示す**ダッシュボードのTemplateノード**でUIを表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183282/2fe8cf50-2651-7aa1-231f-1b2f3743933e.png)

使い方は、この中にHTMLを書くか入力データに「**msg.template**」を与えるかです。
今回は次の図のように前段に**Templateノード**を用意しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183282/7957ebe6-8272-abc0-39e6-c9c7bf5d7651.png)

この中で画像URLを動的に設定するHTMLコードを書いています。
Templateノードのコードは次の通りです。

```html
<div>
    <img src="{{payload.image_url}}" width="100%" height="100%">
</div>
```

画像URLを「**mag.payload.image_url**」で渡すと、HTML内の画像が動的に変更できます。

Templateノードより前段は、クリプトスペルズAPIで取得したjsonデータから、画像URLを抜き出しています。

以上のフローは次のjsonをNode-REDに読込んで確認できます。

```
[{"id":"1ac7b607.ebe26a","type":"tab","label":"フロー 1","disabled":false,"info":""},{"id":"52e65947.89c228","type":"ui_template","z":"1ac7b607.ebe26a","group":"8de655c7.20ffc8","name":"","order":0,"width":"6","height":"7","format":"<div>\n    <img src=\"https://d3n9a7g7xguuos.cloudfront.net/uploads/card/image/500/3610.png\" width=\"50%\" height=\"50%\">\n</div>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":320,"y":340,"wires":[[]]},{"id":"f2fca.b46cb0368","type":"inject","z":"1ac7b607.ebe26a","name":"","topic":"","payload":"https://d3n9a7g7xguuos.cloudfront.net/uploads/card/image/500/3610.png","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":60,"wires":[["d92a1923.64b0f8"]]},{"id":"1f7c92ed.8b027d","type":"http request","z":"1ac7b607.ebe26a","name":"","method":"GET","ret":"txt","paytoqs":false,"url":"","tls":"","persist":false,"proxy":"","authType":"","x":450,"y":140,"wires":[["bcb334c0.c7e598"]]},{"id":"7e3a278d.daa248","type":"debug","z":"1ac7b607.ebe26a","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.image_url","targetType":"msg","x":630,"y":360,"wires":[]},{"id":"bcb334c0.c7e598","type":"json","z":"1ac7b607.ebe26a","name":"","property":"payload","action":"","pretty":false,"x":610,"y":140,"wires":[["3c2242bd.df463e"]]},{"id":"4510e11.d61a12","type":"ui_button","z":"1ac7b607.ebe26a","name":"","group":"8de655c7.20ffc8","order":1,"width":0,"height":0,"passthru":false,"label":"ガチャを引く！","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":120,"y":140,"wires":[["d92a1923.64b0f8"]]},{"id":"d92a1923.64b0f8","type":"function","z":"1ac7b607.ebe26a","name":"","func":"var min = 1 ;\nvar max = 3610 ;\n\nmsg.a = Math.floor( Math.random() * (max + 1 - min) ) + min ;\n\nmsg.url = \"https://cryptospells.jp/public_api/cards/\" + msg.a + \".json\"\n\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":140,"wires":[["1f7c92ed.8b027d"]]},{"id":"3c2242bd.df463e","type":"switch","z":"1ac7b607.ebe26a","name":"","property":"payload.status","propertyType":"msg","rules":[{"t":"eq","v":"404","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":310,"y":220,"wires":[["1663b2ae.091d0d"],["cf8be37a.9dc33"]]},{"id":"1663b2ae.091d0d","type":"change","z":"1ac7b607.ebe26a","name":"はずれ","rules":[{"t":"set","p":"payload.image_url","pt":"msg","to":"https://d3n9a7g7xguuos.cloudfront.net/uploads/card/image/562/396.png","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":220,"wires":[["2dd06f1d.bf2f9","7e3a278d.daa248"]]},{"id":"2dd06f1d.bf2f9","type":"template","z":"1ac7b607.ebe26a","name":"","field":"template","fieldType":"msg","format":"html","syntax":"mustache","template":"<div>\n    <img src=\"{{payload.image_url}}\" width=\"100%\" height=\"100%\">\n</div>","output":"str","x":660,"y":220,"wires":[["52e65947.89c228"]]},{"id":"cf8be37a.9dc33","type":"change","z":"1ac7b607.ebe26a","name":"日本語","rules":[{"t":"set","p":"payload.image_url","pt":"msg","to":"payload.image_url.ja","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":260,"wires":[["2dd06f1d.bf2f9","7e3a278d.daa248"]]},{"id":"da9bea27.9e9998","type":"debug","z":"1ac7b607.ebe26a","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"template","targetType":"msg","x":600,"y":400,"wires":[]},{"id":"31ee41f5.b7d80e","type":"debug","z":"1ac7b607.ebe26a","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":590,"y":440,"wires":[]},{"id":"8de655c7.20ffc8","type":"ui_group","z":"","name":"クリスペガチャ","tab":"4606de96.13ae5","order":1,"disp":true,"width":"6","collapse":false},{"id":"4606de96.13ae5","type":"ui_tab","z":"","name":"ホーム","icon":"dashboard","disabled":false,"hidden":false}]
```


# 感想
　Twitterで#クリスペで呟いたところ、CryptoGames 代表取締役（クリプトスペルズの運営本）の小澤さんからいいねを頂けました！
師走の忙しい時期ですが、苦労して作ってよかったなと感じます。

今回のプロトタイピングでクリプトスペルズAPIの使い方が分かりました。
このノウハウを活かして「レジェンドカードの保有者」を調べるものが作りたいと思ってます。
なぜかというと、レジェンドカードは勝ち負けをひっくり返すほど強力なので、所有者に注意してプレイが必要だからです。

しかし、クリプトスペルズAPIを使用して実現するには全ユーザの所有カードを調べてレジェンドカードとユーザ情報だけ抽出する手間があります。

これをNode-REDで自動化できたらなと考えています。

気になる方はQiitaで私のアカウントをフォローしていただけると、記事公開の通知があると思うので宜しくお願いします！