ポケモンのクイズをつくりたい
去年、ポケモンのダイヤモンド・パールがリメイクされるということで10年ぶりくらいにポケモンのゲームをプレイして、ポケモンへの熱量が高まっている。そして、Node-REDとLINE Botを学んだ今、みんなで遊べるような楽しいポケモンクイズができるbotを作りたい!
どんなLINE Bot?
- ポケモンのクイズを出して、そのポケモンの名前を当てる。
- わからなかったらヒントも出してくれる。(種類、高さ、重さ、タイプ)
- 正解をしたらポケモンを捕まえらた旨とそのポケモンの画像を返信する。
環境
- Node-RED
- JavaScript
- PokeAPI
- LINE Bot
記事内で使用したフロー&JSON
完成品
[{"id":"47952a78.358a6c","type":"ReplyMessage","z":"a12d3705.a81c58","name":"","replyMessage":"","x":1160,"y":320,"wires":[]},{"id":"cc08d5f1.7f2c3","type":"function","z":"a12d3705.a81c58","name":"整数生成","func":"//現在の時間を取得\nlet time = new Date();\n//月、時、曜日 0始まり\nlet date = time.getDate();\nlet hour = time.getHours();\nlet day = time.getHours();\n\n//ランダムな整数を生成(1~151)\nlet min = 1 ;\nlet max = 151 ;\nlet num = Math.floor( Math.random() * (max + 1 - min) ) + min ;\n\n//時間×ランダムな数値\nmsg.id = date + hour * day ;\nmsg.query = encodeURI(msg.id);\n//msg.id = num;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":260,"y":60,"wires":[["f47d961a.10347"]]},{"id":"4706d615.789b","type":"function","z":"a12d3705.a81c58","name":"get_json01","func":"// http requestの結果を取得する\nconst result = msg.payload;\n\n//ポケモンの名前とタイプ\nmsg.en_name = result.name;\nmsg.en_type = result.types[0].type.name;\n\n//体重と高さを変換\nmsg.weight = parseFloat(result.weight)/10;\nmsg.height = parseFloat(result.height)/10;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":490,"y":200,"wires":[["6dec7d3a.d7715c"]]},{"id":"82ba342b.9e44a8","type":"http request","z":"a12d3705.a81c58","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://pokeapi.co/api/v2/pokemon/{{{id}}}","tls":"","persist":false,"proxy":"","authType":"","x":330,"y":200,"wires":[["4706d615.789b"]]},{"id":"6dec7d3a.d7715c","type":"http request","z":"a12d3705.a81c58","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://pokeapi.co/api/v2/pokemon-species/{{{id}}}","tls":"","persist":false,"proxy":"","authType":"","x":330,"y":320,"wires":[["4494da9e.bc898c"]]},{"id":"4494da9e.bc898c","type":"function","z":"a12d3705.a81c58","name":"get_json02","func":"// http requestの結果を取得する\nconst result = msg.payload;\n\n//ポケモンの名前\nmsg.ja_name = result.names[0].name;\nmsg.ja_genera = result.genera[0].genus;\nmsg.en_genera = result.genera[7].genus;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":490,"y":320,"wires":[["9eaf5252.c31ad8"]]},{"id":"f47d961a.10347","type":"function","z":"a12d3705.a81c58","name":"メッセージ格納","func":"// msg.payloadにメッセージ本体を入れる\nmsg.input_line = msg.payload;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":420,"y":60,"wires":[["82ba342b.9e44a8"]]},{"id":"d4a00c3d.fe07d","type":"function","z":"a12d3705.a81c58","name":"②ヒント","func":"// LINEサーバーからの内容を復元する\nmsg.payload = msg.line;\n\n//ランダムな整数を生成(1~4)\nlet min = 1 ;\nlet max = 4 ;\nlet num = Math.floor( Math.random() * (max + 1 - min) ) + min ;\n\nlet text01 = num;\n\nif(num === 1){\n text01 = \"名前:\" + msg.en_name;\n}else if(num === 2){\n text01 = \"重さ:\" + msg.weight +\"kg\";\n}else if(num === 3){\n text01 = \"高さ:\" + msg.height +\"m\";\n}else if(num === 4){\n text01 = \"タイプ:\" + msg.en_type;\n}\n\nmsg.payload = text01;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":820,"y":260,"wires":[["47952a78.358a6c"]]},{"id":"9eaf5252.c31ad8","type":"switch","z":"a12d3705.a81c58","name":"","property":"input_line","propertyType":"msg","rules":[{"t":"eq","v":"ポケモンクイズ","vt":"str"},{"t":"eq","v":"ヒント","vt":"str"},{"t":"eq","v":"答え","vt":"str"},{"t":"eq","v":"ja_name","vt":"msg"},{"t":"else"}],"checkall":"true","repair":false,"outputs":5,"x":630,"y":320,"wires":[["44f9c357.02555c"],["d4a00c3d.fe07d"],["15eca841.4a87c"],["eed13ce7.c0255"],["b842e653.b1eab"]]},{"id":"44f9c357.02555c","type":"function","z":"a12d3705.a81c58","name":"①クイズ","func":"// LINEサーバーからの内容を復元する\nmsg.payload = msg.line;\n\n// 返信メッセージをhttp requestの結果にする\nlet text01 = \"ID:\"+ msg.id + \"\\n種類:\" + msg.en_genera ;\n\nmsg.payload = text01;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":820,"y":200,"wires":[["47952a78.358a6c"]]},{"id":"15eca841.4a87c","type":"function","z":"a12d3705.a81c58","name":"③答え","func":"// LINEサーバーからの内容を復元する\nmsg.payload = msg.line;\n\n// 返信メッセージ\nlet text01 = \"名前:\" + msg.en_name + \"\\n種類:\" + msg.en_genera;\nlet text02 = \"名前:\" + msg.ja_name + \"\\n種類:\" + msg.ja_genera;\nlet text03 = \"重さ:\" + msg.weight +\"kg\"+\"\\n高さ:\" + msg.height +\"m\";\n\nmsg.payload = \"<英語>\\n\"+text01 +\"\\n\\n<日本語>\\n\"+text02+\"\\n\"+text03;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":810,"y":320,"wires":[["47952a78.358a6c"]]},{"id":"b842e653.b1eab","type":"function","z":"a12d3705.a81c58","name":"⑤凡例","func":"// LINEサーバーからの内容を復元する\nmsg.payload = msg.line;\n// 返信メッセージをhttp requestの結果にする\nlet text01 = \"『ポケモンクイズ』\\n\\tポケモンのクイズが始まるよ\\n\";\nlet text02 = \"『ヒント』\\n\\t重さ・高さ・種類の中から\\n\\tランダムでヒントを出すよ\\n\";\nlet text03 = \"『答え』\\n\\t答えを表示するよ\";\n\nmsg.payload = text01+text02+text03;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":810,"y":440,"wires":[["47952a78.358a6c"]]},{"id":"35afd36b.91664c","type":"Webhook","z":"a12d3705.a81c58","name":"","url":"/webhook","x":100,"y":60,"wires":[["cc08d5f1.7f2c3"]]},{"id":"eed13ce7.c0255","type":"function","z":"a12d3705.a81c58","name":"④正解","func":"// LINEサーバーからの内容を復元する\nmsg.payload = msg.line;\n\n// 返信メッセージ\nlet text01 = \"やった~!\\n\" + msg.ja_name + \"をつかまえた\";\n\nmsg.payload = text01;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":810,"y":380,"wires":[["47952a78.358a6c"]]}]
画像送信
[{"id":"de594584.2845b8","type":"Webhook","z":"768ce1bb.a65b4","name":"","url":"/webhook","x":160,"y":220,"wires":[["f00ac848.af456"]]},{"id":"f00ac848.af456","type":"change","z":"768ce1bb.a65b4","name":"","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload.type","pt":"msg","to":"image","tot":"str"},{"t":"set","p":"payload.originalContentUrl","pt":"msg","to":"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/25.png","tot":"str"},{"t":"set","p":"payload.previewImageUrl","pt":"msg","to":"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/25.png","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":220,"wires":[["623f799f.380128"]]},{"id":"623f799f.380128","type":"ReplyMessage","z":"768ce1bb.a65b4","name":"","replyMessage":"","x":540,"y":220,"wires":[]}]
ポケモンの画像を送信するものとなっており、単体ではうまく画像が送信されるのだが、完成品と併せてテキストと画像を一緒に送信しようとするとうまくいかなかった。。
完成品
実際に作ったものはこちら
『ポケモンクイズ』と入力をするとポケモンのクイズが始まる。
ポケモンの種類が英語で送られてくるのでどのポケモンなのかを当てるというクイズゲームとなっている。
難しかったら『ヒント』と送信をするとポケモンの名前(英語名)、高さ、重さ、タイプの中からランダムで一つ教えてくれる。たまにポケモンの名前(英語名)がもろにポケモン名で出るのはご愛嬌。。。どのポケモンかわかったらポケモンの名前を送信して正解をすれば、そのポケモンをゲットすることができき、わからなかったら『答え』と送信をするとどのポケモンだったのか情報を教えてくれる。クイズのポケモンは1時間毎に変化をするので18時は『ヒトカゲ』、19時は『ピカチュウ』のように捕まえられるポケモンが変わってくるので楽しんでほしい。
手順
今回は PokeAPI を利用してポケモンの名前やタイプ、重さ等の情報を取得していこうと思う。
1.クイズのための図鑑NOを生成する。
最初にポケモンの図鑑NO.をランダムで指定をして、ポケモンの情報を取得するようなコードを書いていこうと思う。今回は以下のコードのように現在の時刻を取得して 日+時間×曜日 で計算をしている。最初は、1〜151のランダムな整数を生成してそれを図鑑NOとしてJSONを取得しようと思っていたのだが、これだと メッセージを送信するたびに図鑑NOが変わってしまい クイズとして成り立たなくなってしまうのでこのように1時間毎に変化をする計算方法を用いた。
//現在の時間を取得
let time = new Date();
//月、時、曜日
//例)日曜日だったら”0”、火曜日だったら”2”となる
let date = time.getDate();
let hour = time.getHours();
let day = time.getHours();
//ランダムな整数を生成(1~151)
let min = 1 ;
let max = 151 ;
let num = Math.floor( Math.random() * (max + 1 - min) ) + min ;
//日+時間×曜日
msg.id = date + hour * day ;
//メッセージを送信するたびにIDが変わってしまうためボツ
//msg.id = num;
return msg;
生成された id でJSONを取得する。urlの最後の数字を図鑑NOにすることで該当する番号のポケモンの情報を取得することができる。
https://pokeapi.co/api/v2/pokemon/25
↑だったら図鑑NO25のピカチュウの情報を取得することができ、ポケモンの名前(英語)、タイプ(英語)、体重、高さが格納されている。
https://pokeapi.co/api/v2/pokemon/{{{id}}}
2.PokeAPIを使って図鑑NOに対応したポケモンの情報を取得する。
ポケモンの情報はJSON方式で返されるので function ノードを使って欲しい情報を取り出す。
- ポケモンの名前(日本語、英語)
- タイプ(英語)
- 種類(日本語、英語)
- 体重
- 高さ
体重と高さは文字列から数字へ変換をして、単位をそれぞれ変換をしている。
// http requestの結果を取得する
const result = msg.payload;
//ポケモンの名前とタイプ
msg.en_name = result.name;
msg.en_type = result.types[0].type.name;
//体重と高さを変換
msg.weight = parseFloat(result.weight)/10;
msg.height = parseFloat(result.height)/10;
return msg;
今回、 図鑑NOのポケモンの名前とメッセージで送信したポケモンの名前が一致 した時に正解としたいので、ポケモンの名前(日本語)を取得したいのだがhttps://pokeapi.co/api/v2/pokemon では見つからなかった。
調べたところによるとポケモンの名前(日本語)や種類(日本語、英語)https://pokeapi.co/api/v2/pokemon-species で取得できることがわかったので、先ほどと同様の手法でポケモンの名前(日本語)や種類(日本語、英語)を取得する。
3.入力されたメッセージによって返信を変える。
メッセージまで取得をすることができたので次にユーザーから入力されたメッセージ毎に返信メッセージを作成するようなフローを作成する。
<入力されたメッセージ>
① 『ポケモンクイズ』が送信されたとき
// LINEサーバーからの内容を復元する
msg.payload = msg.line;
// 返信メッセージをhttp requestの結果にする
let text01 = "ID:"+ msg.id + "\n種類:" + msg.en_genera ;
msg.payload.events[0].message.text = text01;
return msg;
② 『ヒント』が送信されたとき
// LINEサーバーからの内容を復元する
msg.payload = msg.line;
//ランダムな整数を生成(1~4)
let min = 1 ;
let max = 4 ;
let num = Math.floor( Math.random() * (max + 1 - min) ) + min ;
let text01 = num;
//ランダムに生成された整数によってヒントを変えている
if(num === 1){
text01 = "名前:" + msg.en_name;
}else if(num === 2){
text01 = "重さ:" + msg.weight +"kg";
}else if(num === 3){
text01 = "高さ:" + msg.height +"m";
}else if(num === 4){
text01 = "タイプ:" + msg.en_type;
}
msg.payload.events[0].message.text = text01;
return msg;
③ 『答え』が送信されたとき
// LINEサーバーからの内容を復元する
msg.payload = msg.line;
// 返信メッセージ
let text01 = "名前:" + msg.en_name + "\n種類:" + msg.en_genera;
let text02 = "名前:" + msg.ja_name + "\n種類:" + msg.ja_genera;
let text03 = "重さ:" + msg.weight +"kg"+"\n高さ:" + msg.height +"m";
msg.payload.events[0].message.text = "<英語>\n"+text01 +"\n\n<日本語>\n"+text02+"\n"+text03;
return msg;
④ クイズに正解したとき
// LINEサーバーからの内容を復元する
msg.payload = msg.line;
// 返信メッセージ
let text01 = "やった~!\n" + msg.ja_name + "をつかまえた";
msg.payload.events[0].message.text = text01;
return msg;
⑤ ①〜④以外が送信されたとき
// LINEサーバーからの内容を復元する
msg.payload = msg.line;
// 返信メッセージ
let text01 = "『ポケモンクイズ』\n\tポケモンのクイズが始まるよ\n";
let text02 = "『ヒント』\n\t重さ・高さ・種類の中から\n\tランダムでヒントを出すよ\n";
let text03 = "『答え』\n\t答えを表示するよ";
msg.payload.events[0].message.text = text01+text02+text03;
return msg;
まとめ
今回はPokeAPIを使用してはポケモンクイズを作成したのだが、本当はポケモンを捕まえた際に画像も一緒に表示するような仕様にしたかったのだがなかなかうまくいかず、テキストのみとなってしまった。今後、原因が判明したらテキストと併せて画像も送信できるように改良をしたい。
参考