この記事は本家つまみログの ツイートを拾ってニコニコ風に流すツールをJavaで作った を焼き直したものです。
こんにちは、つまみ(@TrpFrog)です。足が痛いです。
先日某電通大東方サークルの方に「ハッシュタグ拾って画面に流すツール作れない?」と頼まれ、面白そうだったのでそういうツールを作ってみることにしました。
車輪の再発明はしたくありません。とりあえずググってみてもヒットしなかったので作ることにしました。完成品はこちらになります!
背景を透明化できるので、Twitterのコメントを流しながら配信イベントを見たり
背景色を緑に設定してクロマキー合成で配信に使ったりできます。
完成後、記事を書くぞ!と編集画面を立ち上げて「ニコニコ風に流すツール」まで書いたところで嫌な予感がしました。
[https://twitter.com/TrpFrog/status/1330756282358263808:embed#ちなみに嫌な予感がしてさっき「Twitter ニコニコ風」で調べたら同じようなやつがゴロゴロ出てきて泣いてしまいました]
どうして1
それはさておき、せっかく作ったので開発までの過程を書いていきます。この記事ではコードの読みやすさを優先してimport文やgetter/setterを大幅に省略しています。完全なコードを見たい方はGitHubを覗いてみてください。あまり綺麗ではないので恥ずかしいですが!><
仕組み
Twitter APIのJava用wrapperというとても優秀なライブラリであるTwitter4Jを使います。仕組みは簡単で検索したい単語を指定して、ストリームを開始、流れてきたツイートを画面に流すだけです!簡単!
開発過程
コメントクラスをつくる
コメントに関する情報を保存しておくためにコメントクラスを作ります。
こんな感じで良いでしょう。たぶん。
x座標がdoubleになっているのはコメントの速度調整をゆるやかにしたいという目的があります。例えば0.1ずつ増やしたいと思ってもintだと floor(x + 0.1) = x ですので止まったままになってしまいます。ceil(x + 0.1) = x+1 でも速すぎてしまいます。ですので、doubleを使っています。
コメントを流す画面をつくる
ウィンドウ作り
まず、一番重要なコメントを流すウィンドウを作ります。記事の一番上で挙げたような画像のように背景色を透明にしたいと思います。JavaでGUIを作るときはSwingという標準APIを使います。まずJFrameというクラスを使ってウィンドウを作っていきます。
とりあえずこんなものでしょうか。
パネル作り
ウィンドウはできたので、ここからJPanelを使ってコメントを乗せるためのパネルを作ります。
だいたいこうです。(これしか言ってない)
おっと、JPanelで完全に透明化したいときsetBackground(new Color(r,g,b,0)) だと描画時に関係ないウィンドウを描画したり好き放題暴れてしまいます。そこで setBackgroundを次のようにOverrideしておきます。(やっていいのかは知りません……)
描画処理
CommentPanelに描画処理も乗っけましょう。あとで枠をつけたりとか自由度を上げたいのでpaintComponentを使います。JLabelを使っても良かったのですが。
リストを渡して全部出力してもらうようにします。
縁を描く
これだけでは寂しい、というか文字色と同じ色の背景のとき見づらいので縁を描きましょう。2
とりあえず paintComponent() の中に g.setColor() と paintFontBorder() をおきます。そして paintFontBorder() を次のように実装します。
縁取りの手っ取り早いライブラリやアルゴリズムが見つからなかったので自前のO(n^2) アルゴリズムで実装しました。くやし〜何かいい方法を知ってる方は教えてください。とはいえそんなにデカい縁を使う人が存在するとは思えませんが。
簡単にいうとx座標, y座標をそれぞれ -borderSize から borderSize までずらして描画を繰り返します。3
こうすることで文字を縁取りできるようになりました! 最後にこのパネルをCommentFrameにaddすれば完成です。
コメントの挿入位置を決定するやつをつくる
描画するだけではなくロジックっぽいことをするものを作りたいと思います。やりたいことはとりあえず
- 描画パネルを持っているCommentFrameのインスタンスを持っておく。
- コメントを保管するリストをつくる。
- まだ流していないコメントを保存しておくキューをつくる。
- 画面に入りきっていないコメントを保存する集合をつくる。
- コメントを挿入可能なY座標を保管する集合をつくる。
- コメントの高さを保管する。
- 10msおきにコメントを画面に送り出すタイマーをつくる。
の7つです。実装するとこうです。
ひい、結構な量になってしまいました。
コメントの追加処理
コメントの追加処理もやってしまいましょう。やりたいことは次の3つです。
- コメントの追加
- キューに入れる
- 挿入可能かのチェック
- 挿入可能Y座標の集合が空かどうかを見てO(1)で判断します
- 挿入位置の返却
- 挿入可能Y座標の集合の最小値をO(log n)で返します。
- これがしたいのでTreeSetを使っていました。
- ただ実際はそんなに挿入行は多くはならないので線形探索した方がいいかもしれません、自己満足(は?)
コメントを動かす
最後にコメントを動かすパートです。ウオオオ、一番肝心なパートです。
アクティブなコメントのリストを線形探索していき、1つ1つのX座標を動かしていきます。画面外に出たら捨てたいのでイテレータを使います。捨てる処理があるのでアクティブなコメントリストはLinkedListで実装しています。4
やりたいことは次の通りです。
- 移動距離 dx を計算
- コメントの全てにdxを足す
- コメントが画面内に入りきったらNEW_COMMENTからそれを削除
- さらに挿入可能なY座標の集合にそのコメントのY座標を追加
- 画面外に出たらリストから削除
- キューから新たなコメントを拾ってくる
これでaddCommentすれば自動でコメントが画面を流れる処理が完成しました!わーい
ここまでだいたい3時間ぐらいで実装できました。(もちろん後々見つかったバグの修正や仕様変更を除く)
ツイートを拾ってくる
さて、ツイートを拾っていきましょう。まずすることは次の3つです。
StatusListenerをimplementsしているので他のメソッドもオーバーライドします。今回使うのはonStatus(ツイートを拾ったときの動作)だけなのでそこだけ書きます。
- RTとリプライを消す。
- Statusというツイートのデータが入るクラスから文字列を生み出してaddCommentする。
ここでcommentFactoryはStatusというツイート用のクラスからコメント用に加工した文字列をつくる「工場」です。この工場を見ていきましょう。
ツイートからコメント用文字列を生み出す工場を建てる
単純にツイートを加工するだけなので上のクラス内に書いても良さそうですが、それにしてはちょっと処理が複雑なのでこうしたファクトリに分けました。ちょっとFactoryっていうのがかっこいいのでやってみたかった。
- ツイートからリンクやハッシュタグを削除して空白で連結する。
- IDと名前の表示設定から出力形式を変える。
をします。
StreamAPI7というのが便利で
- 空白と改行で切って(これはStreamAPIではないが)
- その配列をストリームにして
Arrays.stream(status.getText().split("[ \n]"))
- リンクを消して
.filter(e -> !removeLinks || !e.matches("^https://t\\.co/.*"))
- ハッシュタグも消して
.filter(e -> !removeHashtags || !e.matches("^#.+"))
- 全部を空白で繋げる
.collect(Collectors.joining(" "));
をたったの1文で、直感的な文法でこなすことができます。すき!
設定ウィンドウとかconfigとか
あとは定数で設定した部分(更新間隔、コメントが流れる速度、Factoryの設定など)を変数に置き換えて、configファイルから拾ってきた値を当てはめたり、設定ウィンドウに入れたものを適用させたりして完成!ここは本質的な部分ではないので省略します。
拡張性
ここまでの開発で気をつけたことがあります。それはコメントを流す処理にTwitterAPIを混ぜないということです。これによりコメントを流す画面のライブラリとして使うことができるという嬉しいことが起こります。つまりツイートを拾うだけでなく、別の配信サービスのコメントを拾うAPIを使って拾って流したりすることもできます。例えば次のように実装できます。
ということでJavaでコメント流すツールの話でした。Google検索の能力をこれからも磨いていきましょう!
おわり
Footnotes
-
なんでニコニコ風という単語に気がつかなかったんですかね?とはいえJava製のものは見つからなかったのでセーフ!(本当ですか?) ↩
-
今思ったのですが太さってもしかしてSizeじゃなくてWeightを使いますね?英語力の無さが露見してしまいました......。 ↩
-
線形リストなので削除がO(1)で終わる ↩
-
ツイートが流れてくる川と考えてください ↩
-
UserStreamingが死んだ件が話題になっていましたが、特定のワードに引っかかったものを引っ張ってくるストリーミングについてはまだ生きています。ちんちんリツイートbotなどもたぶんそれを使っています(例がひどすぎる) ↩
-
TwitterのStreaming APIとは別物です、ちょっと紛らわしいですが ↩

