あかんわ

ブログで日誌でも書くようにすれば多少はやる気が出るかと思ったんです

恋するプログラムをSinatraでWebアプリにするPart.3[CHAPTER4 あこがれのGUI]②

記事の概要

『恋するプログラム』の[CHAPTER4 あこがれのGUI]で作成するGUI版チャットボットプログラムを参考に、Webアプリ版チャットボットのインターフェースを作成し、ブラウザに表示します。

目次

開発環境

参考記事
Rubyの開発環境構築は、こちらの記事を参考にさせていただきました。

ノビィに話しかける

テキストチャットとキャラクターのアニメーションをブラウザに表示し、ビデオチャットのようなインターフェースを作成します。

アプリケーションディレクトリの構成
~/programinlove
    |- proto.rb                 // CUIチャットボットのメインファイル
    |- unmo.rb                  // チャットボットオブジェクトのモデル
    |- responder.rb             // 応答オブジェクトのモデル
    |- app.rb                   // Webアプリのメインファイル
    |- /views                   // ビューのテンプレートを配置するディレクトリ
    |     |- index.erb          // チャットのインターフェースを表示するビュー
    |- /public                  // 静的ファイルを配置するディレクトリ
          |- styles.css         // チャットのインターフェースをレイアウトするcss
          |- /img               // 画像ファイルを格納するディレクトリ
               |- /normal       // 平常時の画像を格納するディレクトリ
               |    |-0000.png
               |- /blink        // 瞬きの画像を格納するディレクトリ
                    |-0000.png
                    |-0001.png
ソースコード

app.rbは、ほぼルーティングメソッドのみとなり、ユーザからのアクセスに応じてビューを表示した後は、入力された文字列を受け取った順番に配列に格納し、インスタンス変数@talk_logを生成してビューに表示します。

index.erbにはチャットのインターフェースの要素を設置し、styles.cssでレイアウトを整え、サンプルプログラムに似せた配置にしています。
<body>要素の最後の方に記述しているJavascriptでは、配列に格納された画像を順番に表示し、キャラクターが瞬きをするアニメーションを作成しています。
ただ、アニメーションを作成しているflipAnime関数はバグっていて((clearTimeoutがclerTimeoutになっている))、 エラーによって無理矢理ループが止まっている状態です。
記事を書いている最中に気付き、修正を試みましたが、誤字を修正するとループが止まらなくなったので、諦めてそのまま放置((直し方がわかったら直したいと思います))しています。
timeoutId = setTimeout(flipAnime, 100);を、clearTimeout(timeoutId);の前に記述したら直りました。

app.rb
require 'sinatra'
require 'sinatra/reloader'

# 会話ログを格納する配列
talk = []

# URL'/'にアクセス
get '/' do
  # nobyfomを表示
  erb :index
end

# URL'/'にPOSTメソッドでアクセス
post '/' do
  # ユーザの入力を会話ログに表示
  talk << "#{params['inputarea']}<br>"
  @talk_log = talk.join

  erb :index
end
views/index.erb
<html>
  <!-- publicフォルダのcssファイルを読み込む -->
  <link href="styles.css" rel="stylesheet">
  <body>
    <!-- チャットのインターフェースを囲う枠 -->
    <div id="nobyform">
      <!-- キャラクターの画像を表示 -->
      <div id="nobycanvas">
        <img src="img/normal/0000.png">
      </div>
      <!-- キャラクターからの応答メッセージを表示 -->
      <div id="responsearea">
        <p></p>
      </div>
      <!-- 会話やオプションの状態をPOSTするフォーム -->
      <div>
        <form action="/" method="post">
          <!-- ボタンを押すと会話を'/'にPOSTする -->
          <div>
            <input type="text" name="inputarea" placeholder="会話を入力">
            <button type="submit">話す</button>
          </div>
          <!-- ログにResponderを表示するオプション -->
          <div>
            <label><input type="checkbox" name="respoption" value="show">Responderを表示</label>
          </div>
        </form>
      </div>
    </div>
    <!-- 会話のログを表示 -->
    <div id="logarea">
      <%= @talk_log %>
    </div>
    <!-- キャラクターの画像をアニメーションさせるJavaScript -->
    <script>
      document.addEventListener('DOMContentLoaded', function(){
        var index = 0;    /* 配列の要素番号 */
        var cnt = 0;      /* 瞬きの回数 */

        /* 瞬きのアニメーション用の画像 */
        var blinkImg = ["img/normal/0000.png",
                        "img/blink/0000.png",
                        "img/blink/0001.png",
                        "img/normal/0000.png"
        ];

        /* 瞬きのアニメーションを作成     */
        /* 2回の瞬きでアニメーション終了 */
        function flipAnime(){
          timeoutId = setTimeout(flipAnime, 100);

          document.getElementById("nobycanvas").getElementsByTagName("img")[0].src = blinkImg[index];
          index++;
          if (index >= blinkImg.length){
            index = 0;
            cnt++;
          }
          if (cnt > 1){
            cnt = 0;
            clearTimeout(timeoutId);
          }
        };

        /* 瞬きのアニメーションを表示する */
        setInterval(flipAnime, 3000);
      });
    </script>
  </body>
</html>
public/styles.css
/*----------------------------
 * 全要素に対して直感的な
 * サイズ指定を可能にする
----------------------------*/
* {
  box-sizing: border-box;
}

/*----------------------------
 * チャット用の要素を配置する
 * インターフェースの外枠
----------------------------*/
#nobyform {
  display: table-cell;
  vertical-align: middle;
}

/*----------------------------
 * 応答メッセージを表示する
 * 角丸の吹き出し
----------------------------*/
#responsearea {
  width: 290px;
  margin-top: 30px;
  margin-bottom: 30px;
  border: 1px solid #333;
  border-radius: 10px;
  background: #333;
}

/* 吹き出しの三角部分 */
#responsearea:before {
  content: '';
  position: relative;
  top: -40px;
  left: 30%;
  border-top: 20px solid transparent;
  border-right: 20px solid transparent;
  border-left: 20px solid transparent;
  border-bottom: 25px solid #333;
}

/* 吹き出しの中の文字 */
#responsearea p{
  height: 40px;
  margin-top: 5px;
  margin-bottom: 10px;
  color: #fff;
  text-align: center;
  font-size: 100%;
}

/*----------------------------
 * 会話のログを表示する
----------------------------*/
#logarea {
  width: 300px;
  height: 380px;
  display: table-cell;
  background-color: #f9f9f9;
  overflow: scroll;
}
実行結果

ターミナルでapp.rbを動かし、ブラウザにhttp://localhost:4567を入力します。

~/programinlove
$ruby app.rb
[2016-01-10 12:35:45] INFO  WEBrick 1.3.1
[2016-01-10 12:35:45] INFO  ruby 2.0.0 (2015-04-13) [x86_64-darwin12.6.0]
== Sinatra (v1.4.6) has taken the stage on 4567 for development with backup from WEBrick
[2016-01-10 12:35:45] INFO  WEBrick::HTTPServer#start: pid=8299 port=4567

f:id:b0npu:20160107142020p:plain

WebサーバのWEBrickが起動し、ターミナルにGETでアクセスしたログが表示されます。
CSSファイルや画像にもアクセスしているため、ログが多くなります。

::1 - - [10/Jan/2016:12:36:50 +0900] "GET / HTTP/1.1" 200 2329 0.0316
localhost - - [10/Jan/2016:12:36:50 JST] "GET / HTTP/1.1" 200 2329
- -> /
::1 - - [10/Jan/2016:12:36:50 +0900] "GET /styles.css HTTP/1.1" 304 - 0.0007
localhost - - [10/Jan/2016:12:36:50 JST] "GET /styles.css HTTP/1.1" 304 0
http://localhost:4567/ -> /styles.css
::1 - - [10/Jan/2016:12:36:50 +0900] "GET /img/normal/0000.png HTTP/1.1" 304 - 0.0007
localhost - - [10/Jan/2016:12:36:50 JST] "GET /img/normal/0000.png HTTP/1.1" 304 0
http://localhost:4567/ -> /img/normal/0000.png
::1 - - [10/Jan/2016:12:36:53 +0900] "GET /img/blink/0000.png HTTP/1.1" 304 - 0.0004
localhost - - [10/Jan/2016:12:36:53 JST] "GET /img/blink/0000.png HTTP/1.1" 304 0
http://localhost:4567/ -> /img/blink/0000.png
::1 - - [10/Jan/2016:12:36:53 +0900] "GET /img/blink/0001.png HTTP/1.1" 304 - 0.0004
localhost - - [10/Jan/2016:12:36:53 JST] "GET /img/blink/0001.png HTTP/1.1" 304 0
http://localhost:4567/ -> /img/blink/0001.png

レイアウトを整えたインターフェースを表示しているだけなので、瞬きをしているキャラクターに話しかけることは出来ますが応答はありません。

f:id:b0npu:20160111220818p:plain

参考書籍

参考記事

Sinatraについては、こちらの記事を参考にさせていただきました。

CSSのレイアウトについては、こちらの記事を参考にさせていただきました。

関連記事

- 恋するプログラムをSinatraでWebアプリにするPart.0[はじめに]
- 恋するプログラムをSinatraでWebアプリにするPart.1[CHAPTER3 ほんとに無能]
- 恋するプログラムをSinatraでWebアプリにするPart.2[CHAPTER4 あこがれのGUI]①
- 恋するプログラムをSinatraでWebアプリにするPart.4[CHAPTER4 あこがれのGUI]③
- 恋するプログラムをSinatraでWebアプリにするPart.5[CHAPTER5 辞書を片手に]
- 恋するプログラムをSinatraでWebアプリにするPart.6[CHAPTER6 感情コントロールの魔術師]①
- 恋するプログラムをSinatraでWebアプリにするPart.7[CHAPTER6 感情コントロールの魔術師]②
- 恋するプログラムをSinatraでWebアプリにするPart.8[CHAPTER7 学習のススメ]①
- 恋するプログラムをSinatraでWebアプリにするPart.9[CHAPTER7 学習のススメ]②
- 恋するプログラムをSinatraでWebアプリにするPart.10[CHAPTER7 学習のススメ]③
- 恋するプログラムをSinatraでWebアプリにするPart.11[CHAPTER8 文章を作り出す]
- 恋するプログラムをSinatraでWebアプリにするPart.12[CHAPTER9 ノビィ、ネットワークにつながる]
- 恋するプログラムをSinatraでWebアプリにするPart.13[おわりに]