投稿記事

ブラウンチップ☆ 2020/01/11 17:13

ScalaでYoutubeLiveのコメントを取得してみる

はいどーもScalaできまScalaで有名なツミオです。
ScalaでYoutubeLiveのコメントを取得してみようと思い立ったので、本日は備忘録をば。

環境

SBTを使います。
build.sbtは以下の通りです。

name := "YoutubeLiveSample"

version := "0.1"

scalaVersion := "2.13.1"

libraryDependencies += "com.google.apis" % "google-api-services-youtube" % "v3-rev212-1.25.0"

scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked", "-Xlint")

YouTube Data APIを使用するために、依存関係を指定している以外はまあ普通かなと思います。
ちなみMaven Repositoryから探したやつです。
バージョンとかは適宜調整するとよい気がします。

APIキーの有効化

C#の記事ですが、APIキーの有効化には関係ないのでこちらの記事を参考にしました。
画像付きで非常にわかりやすかったです。

参考にするサイト

さてあとはコードを書くだけです。
が、Youtube Data APIを使ったこともなければ、Scalaも初心者ですので、今回の記事では「とりあえずコメントを取得できればなんでもOK」ということにしました。
基本は先に紹介したC#の記事を参考にしながら進めたのですが、Scalaではそのまま当てはめられないところも多々あったので、他に以下のサイトを参考にして作成しました。

基本は上記の2つですが、GitHubにもよさげなものが公開されています。

この中の特にListLiveChatMessagesは非常に参考になりそうな気配がしています(今回はこのリポジトリの存在に気がつけなかったので、次回以降利用してみます)。

LiveChatIDの取得

まずLiveChatIDを調べなければならないようです。
これはVideoIDからわかるようで、どうもハッシュ化かなにかされたもののようです。

https://www.youtube.com/watch?v=XXXXXX

これで言うとXXXXXXの部分がVideoIDですね。
VideoIDからLiveChatIDを調べるためのメソッドを定義します。

  def getLiveChatId(youtube:YouTube, videoId:String) : String = {
    val videoList = youtube.videos
      .list("LiveStreamingDetails")
      .setFields("items/liveStreamingDetails/activeLiveChatId")
      .setKey(apiKey)
      .setId(videoId)
    val response: VideoListResponse = videoList.execute

    val list = response.getItems.asScala
    list.filter(!_.isEmpty).map(_.getLiveStreamingDetails.getActiveLiveChatId).head
  }

apiKeyには先ほどAPIキーの有効化で作成したものを当てはめておきます。
YouTube型はYouTube Data APIリクエストの作成に使用するもので、こちらはあとで作成します。

このメソッドは以下のようにして使用できます。

val liveChatId = getLiveChatId(youtubeService, "HogeHoge")

チャットメセージの取得

次はシャットのメッセージを取得します。
まずはこんな感じ。

    val liveChatRequest = youtube.liveChatMessages.list(liveChatId, "snippet,authorDetails")
    liveChatRequest.setPageToken(nextPageToken)
    liveChatRequest.setKey(apiKey)
    val liveChatResponse = liveChatRequest.execute()

最初の行はsnippet,authorDetailsのみをリストから取ってくるという意味なのだと思いますが、snippetとauthorDetailsである理由はよくわかりませんw
とりあえずサンプルがこうなってたのでこうしただけです。
2行目と3行目はapiKeyやトークンをセットしています(nextPageTokenはリアルタイムにチャットメッセージを取得するときに使うもので、今回は「1度取得したら終わり」なところまでしか作らないのであまり関係なさそうです)。

リクエストを作り終えたら、あとはexecuteしてresponseを受けるだけですね。
このレスポンスを利用して実際にコメントをプリントするコードが以下の通りです。

    val list = liveChatResponse.getItems.asScala
    for(liveChat <- list) {
      if(liveChat.getSnippet.getSuperChatDetails != null) {
        println(s"スパチャ情報:${liveChat.getSnippet.getSuperChatDetails}")
      }
      println(s"${liveChat.getAuthorDetails.getDisplayName}:${liveChat.getSnippet.getDisplayMessage}")
    }

スパチャコメントがあればそれを表示して、スパチャが投げられていなければ普通に「書き込んだ人の名前」と「コメント内容」をプリントしています。
上記のコードをまとめて

getLiveChatMessages(liveChatId:String, youtube:YouTube, nextPageToken:String)

としておきます。
Scalaっぽくない非常に汚いコードなので、次回以降直していきます……。

動かしてみる

  val scopes = List("https://www.googleapis.com/auth/youtube").asJava
  val HTTP_TRANSPORT = new NetHttpTransport
  val JSON_FACTORY = new JacksonFactory
  val credential = new GoogleCredential().createScoped(scopes)
  val youtubeService: YouTube = new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(
    "youtube-cmdline-channelbulletin-sample").build()

  val liveId = getLiveChatId(youtubeService, "XXXXXXXXXXXX")
  println(s"LiveChatID:$liveId")
  getLiveChatMessages(liveId, youtubeService, "")

はいこんな感じです。
NetHttpTransportとJacksonFactoryが何やってるのかはよくわからないですが、サンプルでこうしてたのでそうしました。
credentialだけはどうも情報が古いのか、そのままやっても動かなかったのでGoogleCredentialを利用しています。

あとはBuilderで組み立てればOKです。
これでコメントが取得できるようになりました。

終わりに

本日は非常に駆け足な記事ですが、Scalaで実装する情報が皆無だったので色々と苦労しました。
たぶんC#だったら5分くらいで作れたと思うんですが(情報多いですし)、Scalaで作ったこれ実際には数時間かかってますw

ですがなんとなく仕組みがわかってきたので「僕専用のコメントビューア」をScalaの学習用としてボチボチ作ってみたいと思いますよ。

Scala使った仕事に就きたいですねえ。

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

ブラウンチップ☆ 2020/01/08 19:37

【ツクールMV】キャラクター間の最短距離を計算する

はいどーもツクラー界に始まってツクラー界に終わる無職です。
本日はキャラクター間の最短距離を返すプラグインを公開したので、そちらの制作の過程でも書こうかと思います。
「うるせープラグインさっさとよこせ!」という方はGitHubからどうぞ。

なんで作ろうと思ったか

キャラクター間の最短距離を計算するプラグインを作ろうと思ったきっかけは、ツクマテさんのこちらの記事です。
ご覧の通り、古い記事なのですが、レスが新しくついていたので僕も便乗した感じですね。

最短距離を返すには

RPGツクールMVの経路探索にはA*という有名なアルゴリズムが使われているそうです(どっかで読んだだけで、アルゴリズム自体は理解してないので本当に使われてるかどうかはわかりません。興味ある人はWkipediaをご覧ください)。
この経路探索アルゴリズムを流用すれば最短距離を計算できそうですね。

A*が使われてい箇所

A*アルゴリズムが使われているのはGame_CharacterクラスのfindDirectionToでしょう。
実装はめちゃんこ長いので省略しますが、以下のシグネチャを持ちます。

findDirectionTo(goalX:number, goalY:number):number 

目的地(X座標とY座標)を入力して使用すると、目的地に到達するためにどの方角へ移動すればいいか(このメソッドは向きをnumberとして返すだけで実際の移動は別メソッドに委譲しています)を教えてくれます。

この処理をうまいこと使えばなんだかいけそうですね。

findDirectionToが使われている箇所

次はfindDirectionToが使われている場所を探してみましょう。
するとGame_PlayerクラスのmoveByInputメソッドが該当することがわかります。
このメソッドは「キー入力」と「マウス入力」の両方を処理します(正確にはちょっと違いますが、そこは今はどうでもいいので無視します)。
で、マウス入力の際にfindDirectionToが使われています。
具体的にはマウスでクリックされたX座標とY座標を目的地とし、次に向かうべき方角を決定するという処理のために使われています。

実際の移動はexecuteMoveに委譲されていますが、実はこのメソッドはGame_Characterクラスには実装されておらず、Game_Playerクラスのみに実装されています。
すなわちイベントにはこのメソッドは使えません。

ただexecuteMoveはGame_CharacterクラスのmoveStraightに処理を委譲しているだけですので、経路探索するだけならmoveStraightを利用すればよいです。

探索終了の条件を考える

さて情報が出揃ってきました。
次に決めるべきなのは、探索終了の条件です。
というのもfindDirectionToはあくまでも次に進むべき向きを教えてくれるだけなので、探索を終了すべきかどうかはわかりません(0が返ってきたとき探索終了してもいいかも)。
ここでは以下の条件を満たすとき、探索を終了すべきとします(探索オブジェクトとは、目的地ではなく、目的地へめがけて進んでいるオブジェクトのことです)。

  1. 探索オブジェクトの周囲4方向に対象がいるとき
  2. 規定回数探索しても1の条件を満たせなかったとき

最初の条件はいいですね。
もしも8方向探索する場合は、周囲8方向に対象がいる場合となります。

2の条件は、例えば目的地が絶対に到達不能の場所にいるときに探索を打ち切るために使用されます。
もし探索を打ち切らなければ、永遠に探索し続け、プログラムがフリーズしてしまいます。

実際の実装

目的地への最短距離を計算する歳、目的地を探索するキャラクターを動かすわけにはいきません(最短距離を計算したいがキャラクターは動かしたくない、というシチュエーションは存在すると考えたいため)。
ですがfindDirectionToは「現在の場所からどちらの方角を目指せばよいか」しか教えてくれません。
すなわちキャラクターを動かすことが必須となります。

これは困ったため、最短距離を計算する際、ダミーのキャラクター(画面上では見えない)を作成し、動かすことにしました。
そのダミーのキャラクターが1か2いずれかの条件を満たすまで探索を続けるわけですね。
また移動した距離はどこかで記録しておかなければなりません(1歩ずつ進ませるので最短距離がわからなくなる)。
この移動した距離を保存するためだけに一時変数を用いるのはブサイクだと感じたので再帰処理で書いてみました
ただ開発現場によっては「再起処理は遅いしわかりにくいので書くな」と言われることもあるくらいなので、この辺は好みでしょうね……。

終わりに

いかがでしたでしょーか。
前回公開したプラグインもそうでしたが、短いプラグインなので読みやすいかなと思います。
「プラグイン自分で書いて見たいなあ~!」という方の参考になればなあと思ってこんな記事にしてみました。
なにか参考になれば幸いです。

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

ブラウンチップ☆ 2020/01/06 10:47

【ツクールMV】飛行船の浮遊・着陸の速度を変えてみる

はいどーもこんにちは、ツクール界のニートです。
本日は久しぶりにプラグイン講座でもやろうかなと思いますよ。

お題

本日のお題はツクマテさんの「飛行船の浮遊・着陸時の速度を変更したい」という質問です。
こちらを見ながらステップバイステップでプラグインを作成していきましょう。
どうでもいいからプラグインが欲しいんじゃ! という方はGitHubをご覧ください。
ちなみに速度だけでなく、最高高度も設定できます。

確認すべき箇所

このお題で真っ先に確認するべき事項は

  • 浮遊・着陸をコントロールしているコード

でしょう。
RPGツクールMVはrpg_objects.jsというファイルに飛行船の動きをコントロールするコードが入っています。
というか飛行船に限らず大抵の「動きをコントロールするコード」はrpg_objects.jsに入っています。
これはツクールMVが以下の決まりごとに(おおまかには)従っているためです。

  • Game_で始まるクラスはモデルを担当する

このモデルという聞き慣れない言葉は、RPGツクールMVのみの話で言うならセーブデータに保存される値と、その値を使っておこなう処理をまとめた塊という認識で当たらずと言えども遠からずといったところでしょう。
ちなみにモデルはビューで利用されます。
ビューはrpg_sprites.jsに入っていまして、主に画面に表示されるもののことを言います。
RPGツクールMVの設計としては

  • モデルはビューに依存しない
  • ビューはモデルに依存する

という関係になっています(例外あるかも)。
すなわちビューはモデルを知っているが、モデルはビューを知らないという関係です。

ビューはモデルから得た値を用いて、画面に表示する位置やら向きやらなんやらを決めているわけなんですね(しつこいようですが例外はあります。例えばビューが直接ビューのアレコレを決めちゃってる場合も。セーブデータに保存したくないとかの意図があったのかも?)。

改変箇所

さてここまでは前置きです。
飛行船の上昇・下降速度を変更するには以下のコードを改変する必要があります。

Game_Vehicle.prototype.updateAirshipAltitude = function() {
    if (this._driving && !this.isHighest()) {
        this._altitude++;
    }
    if (!this._driving && !this.isLowest()) {
        this._altitude--;
    }
};

updateAirshipAltitudeとあるように、飛行船の高度の更新を担当する処理です。
_altitudeメンバをプラス1したりマイナス1したりしている箇所がまさに「高度を変更している」箇所ですね。
単純に考えるならば、以下のような改変で質問者さんの希望に添える改変が可能ということになります。

Game_Vehicle.prototype.updateAirshipAltitude = function() {
    const speed = 5;
    if (this._driving && !this.isHighest()) {
        this._altitude += speed;
    }
    if (!this._driving && !this.isLowest()) {
        this._altitude -= speed;
    }
};

実際に上記の改変をおこなうと、飛行船の上昇・下降速度が変わっていることがわかるかと思います。

問題点

先ほど紹介した方法にはいくつかの問題点があります。
具体的には以下の通り。

  1. rpg_objects.jsを直接改変した場合、改変箇所の管理が大変
    • コアスクリプトのバージョンがまだあるのかないのかは不明ですが、仮にあったとしたらそれでも問題に
  2. speedが最大高度を超えている場合、最大高度を超えた位置に船が固定されてしまう

1番目の問題点については有名ですね。
プラグインとして上記のコードを導入することで解決できます。

2点目については解説が必要かもしれません。
飛行船の高度を調整する際には最大高度の値を参照します。
この最大高度の値よりも現在の高度が高い(あるいは同じ)場合に上昇が止まります。
この最大高度はデフォルトでは48という値が与えられています。
ところが上記のコードにおけるspeedに48より大きい値を与えた場合、飛行船は最大高度を超えた位置で固定されてしまうのです!

試しに上記のコードにおけるspeedを100としてみてください。
飛行船の位置が明らかにおかしくなります。

フォロワー以上限定無料

特典はほとんどありませんが、入ってくださる方が増えれば増えるほど僕が喜ぶプランです(笑)

無料

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

ブラウンチップ☆ 2020/01/05 13:20

『Adaptive Code C#実践開発手法 第2版の感想

はいどーもこんばんは、無職オブ無職です。
実は記事は書いてたのですが、年末年始いろいろと出かけていて、(IDとPW忘れて)Ci-enにログインできず記事が更新できませんでした。
今日から更新ぼちぼち再開していこうと思いますよ。

ちゅーわけで本日は久しぶりに読書感想文でも書いてみようと思います(昔ブログ時代によく書いてた)。
書籍タイトルは『Adaptive Code ~ C#実践開発手法 第2版』です。
ちなみに再読なので初読ではないです。

どんな本なのか

タイトルの通り、C#を用いて実践的な開発手法を紹介している本です。
ただ、内容としてはC#以外にも当てはまる話が多いかなと思います(実際原著では2版のタイトルからvia C#という言葉が取り除かれているらしい)。
最初の章なんてコードが全く出てこず、開発手法の一つであるスクラムについてずっと語っていますw
僕はスクラム開発をしたことがないのですが、同じくアジャイルナニソレという方でも「こういうものもあるのだなー」という感じで読み進められるかと思います。
2章からは馴染みのある言葉がポンポン出てきます。
個人的には第3章が最も面白く読めたかなと思います。

対象読者

タイトルに「実践」とついていることからもわかるように、プログラミング初心者を対象とした本ではありません。
一方で、他言語経験者でC#初心者、ということなら全く問題なく読み進められるかと思います。
もちろんC#特有の記述方法が使われたコードも出てきますが、他言語の経験があれば多くのコードの意味は何となくわかるのではないでしょうか。
この本で大事なのは「開発における考え方」であって「C#における記述方法」ではないと僕は感じているので、C#未経験の方にもオススメできるのではないかなーと思いますよ。

インターフェイスの重要性

本書で繰り返し説かれているのはインターフェイスを使用することの重要性です。
あるクラスがあるクラスに直接依存するよりも、インターフェイスに依存しましょうねと、そういうよく聞く話です。
もちろんその理由についても繰り返し詳述されているので、本書を読み終えたあとではインターフェイスなしのプログラミングは考えられなくなるでしょう(ダックタイピングを前提とした言語だとまた話は別ですが・・・)。
今までインターフェイスを全く使ったことがない、という方はおそらくデザインパターンについてもほとんど知らないと思われるので、GoFのデザインパターンくらいは先にざっと抑えておくと本書を読み進めやすいかもしれません。

依存しない設計

あるクラスを利用すると、他の実装が問答無用で一緒についてくることはコードの臭いとなります。
例えば「HogeClass」に依存するコードを書いたとします。
このとき、HogeClassがデフォルトコンストラクタを利用する方法以外でインスタンスを生成できない場合、普通は「HogeClass」以外になにか別のクラスに依存しているとは考えません(ビルトインのクラスとかは別として)。
ところがHogeClassが実は巨大なパッケージに依存していて、そのパッケージがさらに別のパッケージに依存していて・・・という連鎖が続くとします。
こうなると、単にHogeClassに依存したつもりのはずが、もはや簡単には理解不能な一連の巨大な依存関係ができあがってしまいますよね。
これはよくないですよね、という話が出てきます。
初読のときはあまりピンと来なかった記憶があるのですが、今読むとなるほどなあと得心できたので少しは成長したのかなーと思いましたw
このことに関連して、EntourageアンチパターンとStairwayパターンが紹介されています。
仮にインターフェイスを用いた設計をしていても、Entourageアンチパターンになっていてはダメだよと、そういう感じのお話です。

SOLIDコード

SOLIDコードを扱っている第4章は馴染みのある方も多いのではないでしょうか。
僕も主にプログラミングの作業要員として、このあたりのことは意識してコードを書いています。
とはいえリスコフの置換原則はまだ難しく、正直ジェネリック型のinとoutは未うまく使えませんw
なんとなくこうかなーという感じでやっているので、なんかおかしいときも多々あるかと思われます。
まあそれでもSOLIDコードを意識して書くだけでもコードがだいぶんよくなるんじゃないでしょーか。
SOLIDコードとは少し離れますが「コマンド/クエリ分離の原則」や「副作用のない関数」あたりのことも頭に入れてコードを書くと読みやすくなるのかなあ、なんてちょっと思っています。

依存性の注入

依存性の注入と言えば、Unity使いにとってはZenjectでしょうか。
ぶっちゃけ僕はあまりうまく使えていませんw
Poor man's dependency injectionはまあ・・・という感じなのですが、そこから先のIoTコンテナ等々については正直ウーンという感じです。
概念はわかるのですが、うまく使えるビジョンが見えてきません。
この辺はまだまだ勉強と実践が足りないかなーという感じです。

一通り読んでみて

再読してみて以前より理解できる箇所が格段に増えていたのはよかったですね。
最近停滞期に入っていた感があるので、自分の成長を久しぶりに実感できて読書が楽しかったです。

仕事も辞めてこれからどうしようかなと悩んでいるところですが、今後もまったりゆっくりボチボチ開発は続けていきたいと思いますよ。
ほなそんな感じでまた。

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

ブラウンチップ☆ 2019/12/25 07:27

コルーチンの仕組みを勉強してみる

はいどーもこんばんは、天才に生まれたかった無職です。
本日はDotFeatherのコルーチンを追ってみようと思いますよ。

コルーチンってなんやねん

コルーチンってみなさんご存知でしょうか?
おそらくUnity使いの人にはおなじみなのですが、ツクラーさんだと使ったことがないかもしれません。
Unity公式マニュアルの説明がわかりやすいので、コルーチンのことが全くわからないという方はそちらを読んでもらうといいかなと思います。
非同期処理によく使われるのかなーと思いますが、いわゆるマルチスレッドではない(その用途だとUnityなら最近Job Systemが出てきました)点だけ注意ですね。

DotFeatherにもコルーチンがある

DotFeatherのマニュアルを見てみますと、こちらも同じくコルーチンがあります。
WaitForSecondsやWaitUntilなどもあるので、Unityのノリで使えちゃいます。
で、本日はコルーチンが内部で何をしているのか? を追って行きたいのでDOtFeatherのコードを見ていこうと、そういうわけなんですね。
ソースコードはGitHubで公開されていますので、こちらを見ていきましょう。

コルーチンの基本的な流れ

コルーチンは以下の静的メソッドで開始することができます。

CoroutineRunner.Start

また、GameBase内からだとStartCoroutineだけで呼ぶことも可能です。
これも結局は処理を

CoroutineRunner.Start

に委譲しているだけですね。

で、StartはIEnumerator型の引数を受け取ります。
この引数をもとにCoroutine型のインスタンスが作成され、Startが呼ばれます(CoroutineRunnerクラスのStartとは別モノです)。

ただこの処理の前にKeyをCoroutine型のインスタンスとして、nullをValueとして渡しています。
これがどうなるのかはまたおいおい見ていきましょう。

StartしたあとはCoroutineRunner.Startを呼んだクラスがその後のコルーチンを管理できるようコルーチンをreturnしてあげています。

ここまでの流れをまとめるとこんな感じですね。

CoroutineRunner.Start(IEnumerator coroutine);
↓
var c = new Coroutine(coroutine);
↓
c.Start();
↓
return c;

フォロワー以上限定無料

特典はほとんどありませんが、入ってくださる方が増えれば増えるほど僕が喜ぶプランです(笑)

無料

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

« 1 2 3 4 5

記事のタグから探す

月別アーカイブ

記事を検索