砂上の考察

日々の考察を残し、できれば意見をもらい、また考察する。。。

Line BotのFlex Message制作~PythonでJSONを編集~

   この記事ではプログラミングほぼ初心者の筆者がLINE BotのMessage APIFlex Message(フレックスメッセージ)を作成・導入する方法を紹介しています。Pythonを少し勉強(classらへんまで)していればお好みでアレンジができると思います。以前書いたブログを改善させたものとなっているので、良ければ以下のブログも参照してみてください。

sajyounokousatu.hatenablog.com

 

1.Flex Messageとは

   Flex Messageとは文章あるいは写真、動画だけのメッセージではなく、それらを組み合わせた下図のような独自のレイアウトのメッセージです。

   以降ではレイアウトの作り方、コードに落とし込み・状況によって変更する方法、LINE Botで送信する方法を説明したいと思います。

 

2. Flex Messageのレイアウトの作り方

   Webデザインを少しやっとことがある人は、「小さくてもめんどくさいHTMLなどのコードを書かないといけないだろうな」と思ったかもしれませんが、実はLINE公式から簡単に作れるツールが提供されています。それが下のリンクのFlex Message Simulatorです。

Flex Message Simulator

   少し前まではLINE Bot Designerというツールが使われていたっぽく、少し古いブログではそのツールが紹介されているかもしれませんが、こちらは利用はできるようですが、2022年1月に開発が終了しており、Flex Messageの作成には先に紹介したFlex Message Simulatorが推奨されています。

Flex Message作成方法の簡単な説明

   Flex Messageにはおおまかに2種類あり、bubbleとそれを複数個横に並べたcarouselがあります。bubbleはあらかじめ上から順にheader、hero、body、footerの4つに分かれており、それぞれにboxを追加することでMessageに専用の枠ができます。boxの中にはboxにはimage、button、text、filler、separatorなどが追加でき、自由にアレンジできます。状況によってテキストなどを変えたい場合があると思いますが、それについては次の章で説明します。Flex Message SimulatorのShowcaseにいろいろなサンプルがあるので、いろんなサンプルの作り方を見ることで作りたいレイアウトが作れると思います。

 

3. PythonFlex Messageを送る方法

   先に必要なパッケージをインポートします。始めに紹介した以前書いたブログを参考にされた方はメインプログラムで、

from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)

のようにしていると思いますが、ここにFlexSendMessageを追加して、

from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage, FlexSendMessage
)

のようにします。他にも、FollowEvent、UnfollowEvent、PostbackEventなどのパッケージがあるので興味があれば調べて追加してみてください。筆者はボタンを押すとポストバックイベントが起きるようにしたので、PostbackEventもインポートしています。

   次に先ほど紹介したFlex Message Simulatorでレイアウトを作ったら、右上に「</>View as JSON」と書かれたボタンがあると思いますので、そのボタンを押すと長い文字列が出てきます。例として下の太字の文字列とボタンからなるFlex MessageのJSONデータを用いて説明します。

payload = {
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "vertical",
    "contents": [
      {
        "type": "text",
        "text": "Brown Cafe",
        "weight": "bold",
        "size": "xl"
      },
      {
        "type": "button",
        "action": {
          "type": "uri",
          "label": "button",
          "uri": "http://linecorp.com/"
        },
        "style": "primary"
      }
    ]
  }
}

扱いやすいように上記のようにpayloadという変数に代入します。ここで、レイアウトによってはエラーが出る場合があります。筆者が確認したものですと、valueがtrueやfalseとなっているところでエラーが表示されました。これは、Pythonでは真偽はTrue、Falseのように、先頭の文字を大文字で書く仕様となっているからなので、置換などを利用してすべて修正することでエラーがなくなります。先ほどインポートしたFlexSendMessageを用いて、

flex_message = FlexSendMessage(
  alt_text='this is alt_text',
  contents=payload
)

を実行し、送れる形式に変換します。ここで、alt_textとはFlex Messageをうまく表示できなかったときや、下の写真のように通知されたときに表示されるテキストです。

そして、応答メッセージにFlex Message送る場合は、

line_bot_api.reply_message(event.reply_token, flex_message)

とすることで送れます。

 

4. Flex Messageをコードで操作する方法

   先に例としてあげた変数payloadの型を下のコードで調べると、

type(payload)

「dict」と出力され、辞書型であることがわかります。辞書型は、keyとvalueの組み合わせのデータです。筆者はこれまで辞書型のデータを扱ったことがなかったので、あまり詳しくはないですが、Flex Messageを作るのに知っておくべきことは、追加した順になること、valueに変数を代入できること、そして辞書型の変数の結合方法です。valueに変数を代入できることは、

x = 1
dic = {"a":"0", "b":x}
print(dic)

を実行すると、代入されることが確認できると思います。また、

dic1 = {"1":"apple", "2":"orange"}
dic2 = {"3":"peach"}
dic3 = {**dic1, **dic2}
print(dic3)

を実行すると辞書型の変数が追加した順に結合されることがわかります。これらを利用してFlex Messageを変えていきます。

   先に紹介したFlex Messageの例のJSONの6~22行目の"content"のvalueを見ると、辞書型のデータを要素に持つリストになっています。このリストの要素を見ると、太字のテキストとボタンが入っていることがわかります。つまり、このcontentのvalueを変数にし、状況によって変わるようにコーディングすることで簡単に変えることができます。例として下のような関数を作ると、引数に取った文字列strが表示されたボタンが引数のn個並ぶFlex MessageのJSONデータが表示できます。

def make_flex(str, n):
  temp = [
      {
        "type": "text",
        "text": "Brown Cafe",
        "weight": "bold",
        "size": "xl"
      }
  ]
  for i in range(n):
    temp.append(
      {
        "type": "button",
        "action": {
          "type": "uri",
          "label": str,
          "uri": "http://linecorp.com/"
        },
        "style": "primary"
      }
    )
  payload = {
    "type": "bubble",
    "body": {
      "type": "box",
      "layout": "vertical",
      "contents": temp
    }
  }
  return payload

print(make_flex("test", 3))

改行がなく、わかりづらい方は3章で紹介した方法で実際に送信してみると、下図のようにうまくできていることが確認できると思います。

レイアウトが複雑にだったりして、変数が増えると、わかりづらい関数となることがあるので、header、hero、body、footerごとに関数を作って、最後に先に紹介した辞書型の結合を利用して結合するのが良いと思います。あらかじめレイアウトを作るときにboxでいい感じに分けておくと、JSONデータは長くなるが、わかりやすくなり、変更が簡単になったりするので、レイアウトを見直すことも重要だと思います。

 

5. 制作したFlex Message

  実際に作ったものを下図の右のようなFlex Messageで、以前はコマンドを入力する必要があり、SPカードもSPカードの番号を送信する必要があり、間違えやすく、めんどうであったが、ボタンにすることでコマンドを覚えなくても遊べるようにできました。少し縦長なのが気になりますが、どうしようもないかなと思ってます。。。

 

6. おまけ(postbackイベントとリッチメニュー)

postbackイベント

   ボタンのアクションには指定したサイトに飛ぶuri、日時を選択するdatetimepicker、指定したメッセージを送るmessage、そしてポストバックイベントを発生させるpostbackの4つが選択できます。postbackを選択すると、表示された文字列と異なるデータをサーバー側で受け取れます。先ほど紹介した筆者の制作物では、「Draw Best」などのSPカードのボタンを押すとpostbackイベントが発生します。始めに紹介した以前書いたブログとそこで紹介したコードを見てもらえるとわかると思いますが、SPカードの番号でいくつかの関数が動くようになっており、それを直すのがめんどうだったので、ユーザにはSPカードの名前が表示され、サーバには数字が届くというような要求をちょうど満たすpostbackをアクションとして選択しました。

   では、どのように実装するかは、ほぼ初心者の自分はすぐにはわからず、

line-bot-sdk-python/README.rst at master · line/line-bot-sdk-python · GitHub

このGitHub上のドキュメントを眺めていると、WebhookHandlerの項目を見ると新たにhandlerをたす必要があることがわかり、3章で述べたようにPostbackEventをインポートし、

@handler.add(PostbackEvent)
def on_postback(event):

    reply_token = event.reply_token
    user_id = event.source.user_id
    postback_msg = event.postback.data
    message_to_user = []
    
    if postback_msg == '012':
        message_to_user.append(TextSendMessage(text='hello'))
  
    
    # ユーザーに送信するメッセージが存在しない場合
    if len(message_to_user) == 0:
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text='invalid message'),
        )
    # メッセージが存在する場合は送信
    else:
        line_bot_api.reply_message(
            event.reply_token,
            message_to_user,
        )

をメインより前に追加することで、下図のFlex Message Simulatorのdataに指定した文字列がサーバに送られ、届いたpostback_msgによって異なる挙動をするようにプログラムできます。他のWebhookイベントについても同様にインポートしhandlerを追加することで扱えます。

リッチメニュー

   リッチメニューとは何か、どうつくるのかについては公式のマニュアルにわかりやすく書いてあるので、興味ある方は下記のリンクを見てみてください。

LINE公式アカウント (LINE Official Account Manager) リッチメニューを作成するマニュアル|LINE for Business

   一応実際に作ったものを紹介します。下図が制作したもので、写真を用意していないのとデザインのセンスがないので、久しぶりに開いても困らないように必要なコマンドが押すと送信される単純なリッチメニューを作りました。

 

7. 今後について

   LINE Botについては満足いくものができたので、これで完成にしたいと思っています。他にも機械学習系やVR系のことも勉強しているので、面白い発見などがあればブログにしたいと思います。特にVRに関してはUnityのCardboardを利用して簡単に作れることはわかったが、作りたいものがなかったため中断したので、なにか案があればぜひコメントしていただきたいです。

LINE Botを使った対戦型ゲーム制作~バイオハザード7の「21」を対人ゲームに~

   この記事ではプログラミングほぼ初心者の筆者がLINE Botを利用した簡単なゲーム開発法を紹介しています。Pythonを少し勉強(classらへんまで)していればお好みでアレンジができると思います。バイオ7の「21」を友人と遊びたい人は後半に記載したコードをコピペして遊べると思います。

1.経緯

・LINE Botを用いた理由

   ゲームの実装方法はいろいろあります。例えばスマートフォン向けにApp StoreGoogle Playなどにリリースする方法や、ブラウザアプリを開発し公開する方法などがあります。前者はリリースに審査があったり、お金がかかったりし、筆者のような初心者が少し試したり、友人と少し遊ぶ程度に用いるには大変で、後者はレンタルサーバーなどわからないことが多く、ハードルが高かったため、他の方法を模索した結果、友人を含め、多くの人が利用しているLINEのBotで簡単に実装できたため、それを紹介したいと思います。

バイオハザード7の「21」を再現した理由LINE Bot

   カプコンの人気シリーズの1つであるバイオハザードシリーズの作品の一つのバイオハザード7のDLCの1つである「Banned Footage Vol.2」には「21」というブラックジャックのようなゲームが収録されています。このゲームの詳細は下に書きますが、結論を先に言うと、対人戦がないのがもったいないと感じたからです。考えたら勝率をかなり向上できる点が一番おもしろい点で、対人戦なら割と心理戦ができそうなゲームなので、対人戦があったら面白いだろうなとずっと思っていました。

   「21」は相手とライフをかけて勝負するゲームで、先にライフがなくなったら負けとなります。山札からカードを引き、手札の合計がゴール値(通常は21)以下で大きいほうが勝ちで、一方がゴール値を超えバーストしていたらバーストしていない方の勝ち、両者バーストした場合、合計が小さいほうが勝ちとなり、負けた方は賭けていたライフ分ライフが減り、合計が同値の場合引き分けとなり、どちらのライフも減りません。山札は1~11の11枚しかなく、始めに2枚ずつ配られ、自分のターンのとき、山札からカードを引くか、引かないでチェックするか以外にスペシャルカード(SPカード)を使用するかの3択があります。スペシャルカードは何種類もあり、賭けるライフの数を増やしたり、ゴール値を21から15や25に変えるなどがあります。お互いの1枚目のカード以外は相手にも見えるようになっています。両者チェックしたら合計が比較され、勝敗が判定されます。

2.参考資料

   実装方法の根幹は下のサイトのほとんど丸パクリになるので、ここでは簡単に説明だけします。実装したい人はこのサイトを見て挑戦してみてください。筆者のコードも4章で記載していますが、違う部分がいくつかあります。

 

LINE Botで友だちと遊べるリバーシ開発(1) 手順書

LINE Botで友だちと遊べるリバーシ開発(2) 手順書

 

   詳しくは上の参考サイトにわかりやすく記載されているので簡単に流れを説明すると、GoogleのアカウントやLINE API、ngrokのアカウントがない人は作成し、LINE APIのChannel SecretとChannel Access Token、ngrokのAuthtokenを取得し、Google Colabのコードにコピペし、Google Colabのコードをいくつか実行し、WebhookのURLを取得し、LINE APIにそのURL(のhttpをhttpsに変更したもの)を所定の箇所にコピペし、残りのプログラムを実行すると実装できます。

   浅い知識で仕組みのイメージを書くと、ngrokによってGoogle ColabがあるURLでネットに公開され、LINE Botにメッセージを送るなどのイベントが発生したときにLINE APIがそのURL当てにメッセージを送りそのことを伝え、来たメッセージにGoogle Colab上で処理が行われ、結果を返すといった感じです。
(間違っていたらすみません。。。)

   他にも公式のドキュメントに基本的には全て書かれているので、調べて応用してみてください。筆者は特定の相手にメッセージを送るコードを公式ドキュメントを参考に導入しました。

Messaging APIリファレンス | LINE Developers

3.作成したゲーム

   作成したゲームを紹介します。正直PCでプレイしないと改行が多くて見づらすぎる(改善法模索中。。。)ので、その画面を見せたいと思います。プログラミングしたことがある多くの人が全角が混ざって止まるといったことを経験したことがあると思いますが、それが面倒なのでルールやヘルプは英語でつたない書いています。コード中にある(おそらくすべての)日本語は先に紹介したサイトの方が書いたものです。

   一応ルールは下図のようにruleと送ると出てきます。(つたない英語なうえにわかりにくい説明かもしれません。。。)

   また、指定されている有効なメッセージはhelpと送信することで下図のように表示されます。SPカードの説明はここにしか書かれていないです。

   指定されていないメッセージや場合によっては有効でないメッセージには"invalid message"と返し、自分のターンでないときに、helpやrule、showなどのいつでも有効な特定のメッセージ以外のメッセージには"Not Your Turn"と返すようになっています。

遊び方

1. 部屋を作る / 部屋に入る

   下図のようにrandom、make room、または部屋番号を入力することでゲームがスタートします。

   下図の左のようにrandomを送った場合randomを送った人から順に2人ずつがペアとなりゲームが始まります。そのため、奇数人がrandomと送った場合、最後の人は誰かがrandomと送るまで待つことになります。

   下図の真ん中のようにmake roomと送ると部屋番号が送られ、その番号を送信した最初の人とマッチングするようになっています。部屋番号はある範囲内でランダムな数字となっています。

   対戦相手が部屋を作った場合は、下図の右のように番号を送信することで部屋に入れ、対戦がスタートします。

2. 自分のターンにdraw、check、またはSPカードの番号を送信する

   下図のように自分のターンのときは、カードを引く場合はdraw、引かない場合checkを送ると、相手のターンになります。また、SPカードの番号を送信すると、そのSPカードを持っている場合はカードが消費され、そのカードの効果が発揮されます。相手にはカードを使用したことが通知されます。SPカードを使ってもターンは終了しません。

3. 両者checkのみを送ったターンが連続したら勝敗が判定される

   どちらもSPカードを使用しないで、checkのみをした場合、ゲームの勝敗が判定され、一方のライフがなくなった場合は、下図の右のようにマッチの勝敗が決定され、どちらもライフがある場合は下図の左のように次のゲームが始まります。

 

   byeと送ることでrandomと送ったときの待機状態から抜けられ、対戦中に送ると降参になります。また、30分間どちらからも何も送られなかった場合、マッチが消えるようになっています。

4.ソースコード

   下のリンクからコードを見ることができます。LINE APIのChannel SecretとChannel Access Token、ngrokのAuthtokenを取得し、Google Colabのコードの各種設定の各''内にコピペして使えます。「21」をプレイしたことがないとわかりづらい上に、あまり説明がうまくかけていないと思うので参考程度にして、お好みでアレンジしてみてください。

Google Colab
※新しいタブで開き、自分のドライブに保存してください。

   コードの解説はいろいろ実装し、複雑に絡んでいて難しいので、先に紹介した参考サイトと大きく異なる点を紹介します。

  • グループに追加しない
  • いくつかの操作後に相手にも特定のメッセージが届く

   ゲームの性質上、自分の手札を隠す必要があるため、紹介したサイトと異なり、グループラインに追加するわけではなく、Botとの個チャで対戦する形式にしています。そのため、メインプログラムの570行目(Google ColabならCtrl+m+lで操作中のセルの行数を表示できます)の変数idに送信者固有の番号を格納し、479行目らへんであらかじめ定義した関数を用いて、その送信者がすでにゲームに参加しているかしていないか識別し、参加している場合には参加しているゲームの情報を得るといったようなアプローチをしています。いくつものペアが同時にゲームすることも想定してこのようなプログラミングをしています。

   いくつかの操作では相手にもメッセージが送信されるようにしています。例えば、自分のターンが終了しシステム上相手のターンになった際に相手にメッセージが送信されないと、相手はいつターンが来るのかわからず、直接伝えるなどのめんどうが発生して困ったことになります。なので自分のターン終了時に自動的に相手にメッセージが届く必要があります。やり方としては、公式のドキュメントの「プッシュメッセージを送る」を参考に、先に書いた送信者固有の番号をあらかじめ格納しておき、

 line_bot_api.push_message(送信者固有の番号, TextSendMessage(text='送るメッセージ'))

ターン終了時に上のコードで相手にもメッセージが送信されるようにするといった手法です。紹介した筆者のコードではリストに固有の番号を保存しておき、プレイヤーにシステム上番号(0または1)を割り当てておき、"1-プレイヤー番号"で相手のプレイヤー番号になることを利用してコードを簡単にしています。

5.今後挑戦したいこと

   3章で紹介したようにとても見づらいゲームで現状わかりにくいです。この点を改善する方法として、Flex MessageやLINE Front-end Framework(LIFF)を用いるなどの手法があり、いろいろ勉強してわかりやすくしたいと思っています。さらにイベントなしでメッセージを送れるのかも気になっているのでここらへんも調べたいと思っています。これができればタイマーが実装でき、シンキングタイムを決められます。わかりやすいサイトややってみてわかったことなどがあればまたブログにまとめたいと思ってます。

   他にも機械学習系やVR系のことも勉強しているので、面白い発見などがあればブログにしたいと思います。特にVRに関してはUnityのCardboardを利用して簡単に作れることはわかったが、作りたいものがなかったため中断したので、なにか案があればぜひコメントしていただきたいです。

   この記事は筆者にとって最初の記事なので、色や図なども活用してうまく記事にする方法なども勉強して、筆者のようなほぼ初心者のプログラマーが軽くプログラミングして作れるものや役立つ知識などを共有出来たらなと思っています。

 

追記(2022-09-21)

   改善したものを下記のリンクから見れます。こちらもぜひ読んでみてください。 

 

sajyounokousatu.hatenablog.com