2021年11月20日土曜日

ブシナビ2

以前ブシナビについて取り上げました。
あれから4ヶ月。当時はなかった不満が増えてきましたので、再び取り上げたいと思います。
なお、ナンバリングは前回から連番となっています。

不満8:新商品のプッシュ通知が来る
10/29(金)の昼、スマホにプッシュ通知が来ました。
この日はTD『御薬袋ミレイ -封焔の巫女-』の発売日で、売る側として宣伝したいというのは当然でしょう。
しかし、ブシナビでプッシュ通知を送る必要はあるのでしょうか?
ブシナビをインストールしている人の多く(というかほぼ全ユーザー)は大会にエントリーするためにインストールしているでしょう。
大会にエントリーするということは、デッキを作るために多かれ少なかれ商品を買っているはずです。
そして新商品の情報は事前に展開されているのですから、当日通知されなくても発売日は把握できているはずです。
「ああ、今日発売日だった。忘れてたわ」とショップに立ち寄るユーザーもいるでしょう。
しかし、俺から見ると不要な通知でしかありませんでした。
自分が買う商品の発売日は把握してToDoアプリに入れているので、わざわざ当日通知される必要などありません。

不満9:プッシュ通知をOFFにできない
不要な通知は無効化するに限ります。
ということでブシナビを開いてプッシュ通知をOFFにしようとしました。
しかし、ブシナビのメニューには下のスクショのように「設定」がありません。
嫌な予感がしますが、唯一通知の設定を変更できそうな「プロフィール」を開いてみます。
通知の設定などありませんね。
つまり、ブシナビをインストールしている場合、プッシュ通知をOFFにする方法は存在しないということになります。
上記の通り、俺にとっては商品の発売情報通知は不要です。
せっかくユーザーの情報を保持しているのですから、プッシュ通知の有無も選択できるべきではないのでしょうか?

不満10:プッシュ通知がひと目でブシナビのものだと分からない
プッシュ通知がある事自体不満なのですが、プッシュ通知の内容にも不満があります。
不満8のスクショを見てもらうと、アプリ名横のアイコンが水色の丸になっています。
ここはアプリのアイコンなど、プッシュ通知がどこから来たものであるかがひと目で分かるものであるべきです。
例えば、今日の俺のスマフォはこうなっていました。
SHOW BY ROCK!!はシアンとほわんのシルエット、FGOは「G」と「O」を組み合わせたロゴ、アニメイトはアプリアイコンのイラストです。
FGOとアニメイトは一発で分かりますね。
SB69はちょっと分かりにくいですが、独特な形をしているのでSB69からの通知であると判別できます。
もう一度ブシナビのプッシュ通知を見てみましょう。
単色の丸ではどのアプリの通知なのかさっぱり分かりません。
ブシナビのロゴが水色の丸だというなら類推できるかもしれませんが、実際には「ピンクと水色の平行四辺形」なので、やっぱりプッシュ通知のアイコンだけでは分かりません。
プッシュ通知は画面上部に少しの間現れてステータスバーに引っ込むという特性上、ひと目でメッセージを伝えることも考慮しなければなりません。
さらにステータスバー上ではアイコンしか表示されないのですから、アイコンを見てどのアプリの通知なのか分かって然るべきです。
それをないがしろにしたプッシュ通知は、読んでもらう気があるのか?と疑いたくなるくらいです。

不満11:たかがプッシュ通知と甘く考えている疑惑
10/29の『御薬袋ミレイ -封焔の巫女-』発売に続き、11/5にはタイトルブースター/TDの『SHAMAN KING』も発売されました。
このときも当然プッシュ通知が来て、当然水色丸アイコンでした。
本当にこれを続けていいと思っているのか?と運営に問いたいです。
プッシュ通知はテキストさえあればいい、というなら、アプリの開発なんかやめてもらったほうが世のためです。
昔、俺が仕事でAndroidアプリを作ったとき、アプリの機能上プッシュ通知を実装する必要がありました。
このときはアイコンは別の方に作ってもらい、アプリの実装は俺が担当しました。
プッシュ通知に然るべきアイコンを表示させるというのは、やろうと思えば1人2人でもできるのです。
企業が提供するアプリでそれができないというのは、開発者がそれ以外の対応で忙殺されているとか、仕様を決められないとか、どこかしらに重大な問題があると勘ぐってしまいます。
そうではなく、ただこの仕様で良いと考えているのであれば、ユーザーを軽視しすぎです。

不満12:対戦結果承認の通知が未読で残る
ブシナビの画面右上にはベルのマークがあります。
これをタップすると通知画面を開くことができます。
今の俺の画面はこうなっています。
ブシナビは対戦の勝者が結果報告を行い、敗者が結果を承認することで対戦結果を確定します。
で、敗者側がブシナビを開いている状態で勝者による結果報告が行われると、通知画面をすっ飛ばして、対戦結果を承認するための画面が表示されます。
この仕組み自体は大変便利だと思いますが、後処理が雑と言わざるを得ません。
通知を開かずに対戦結果を承認した場合、アプリ内の通知画面に、対戦結果を承認するための通知が未読で残ったままになります。
上のスクショでは全部既読になっていますが、これはスクショを撮る前に未読だった2件を既読にしてしまったからです。未読のバッジが表示されているのが邪魔だったもので・・・
ブシナビ上で対戦履歴の確認ができると喧伝している以上、対戦結果は当然保持しているでしょう。
また、アプリに表示するために通知の履歴も保持しているでしょう。
対戦履歴と通知履歴を突き合わせれば、「対戦結果が登録されているが、対戦結果の承認依頼通知が未読である通知」を抽出できるはずです。
抽出した通知は既読として上書きしてしまえば、見る必要のない通知を開く必要はなくなります。
見る必要のない通知を見る、というのはユーザーの時間を無駄に使わせることです。
ユーザーの時間を無駄に奪うアプリを使い続けたいのか、と問われると、NOと答えるほかありません。

不満13:見せたいものを見せるという意志
ブシナビは規約においてユーザーがクライアントを自作することを禁止しています。
利用規約の 第9条(禁止事項) (8) 本サービスでの当社が認めていない外部ツールの使用 です。
これを文面通り解釈するのであれば、ブシロードの許可を得ればクライアントを作って良い、となります。
しかし、最初から自前のクライアントがあるのにサードパーティクライアントを認めるでしょうか?
おまけにブシナビのAPIは公開されていません。
アクセス方法を解析しようとしたら、これも利用規約違反となるでしょう。
利用規約の 第9条(禁止事項) (14)本サービスに関わるソフトウェアおよびプログラム等の改変、修正、翻案、逆コンパイル、逆アセンブルおよびリバースエンジニアリング です。
これらは「ブシナビのサービスを使いたいのであれば、ブシナビの公式クライアントを使うべし」という無言のメッセージだと解釈しています。
提供元として、公式クライアントの使用を強制できると何が嬉しいのか?
それは「自分が見せたいものを見せることができる」です。

この代表格としてTwitterが挙げられます。
Twitterはかつてサードパーティクライアント大歓迎でしたが、今では自前クライアント最優先/サードパーティクライアントは蚊帳の外状態です。
そして自前クライアントはというと、「タイムライン」とは名ばかりのめちゃくちゃに並んだTweet群、挟み込まれる広告、興味もないトレンド・・・まあ、惨状です。
それでもサードパーティクライアントを完全に根絶やしにはできていません。
サービスの早いうちからAPIを公開している以上、完全に閉鎖した場合の影響が無視できないということだと思います。

話をブシナビに戻すと、こちらはTwitterのようにAPIを公開しなかったので、自前クライアント外からのアクセス=不正アクセスとみなすことができます。
もしもそのようなことがあれば利用規約違反として排除できます。
「提供元が見せたいものを見るか、サービスを使うのを諦めるか」という二者択一を迫ることができるということですね。

Google Play/App Storeの評価
前回もGoogle Play/App Storeでの評価を載せました。
現時点(2021/11/20 17:53)での評価は・・・
1枚目がGoogle Play、2枚目がApp Storeです。
Google Playの方は平均2.0から2.1とわずかに上昇しましたが、App Storeの方が平均2.9から2.3と大きく下降しています。
どちらも☆1が最多というのは変わっていませんね。

前回は改善案を俺なりに考えてみましたが、今回は考えるのも馬鹿らしいのでやりません。
正直なところ、プッシュ通知の見にくさについては「開発者/アプリ提供者としての矜持はないのか!」と怒鳴りたいくらいです。
前回7つ不満なポイントを挙げましたが、確認できない登録時の情報以外6つは何も変わっていないことが確認できました。
つまり、提供者は現状で問題ないと考えているということでしょう。
そのような状況でユーザーがなにかできますか?
俺はこれからも大会に参加したいのでブシナビを使いますが、これは「許容」や「歓迎」などではなく、「諦観」です。

今日の1曲:

2021年10月9日土曜日

COVID-19のワクチン接種

9/5と10/3にCOVID-19のワクチン接種を受けてきました。
職域接種でモデルナ製ワクチンでした。
「ブログのネタにしたろ」とメモを残していたので振り返ってみようと思います。

1回目

9/5
いつものように5:30に起床、接種が12:30なので11:45には昼食を摂りました。
接種そのものはブースに入って即終了、という感じ。
15分の経過観察中、12:40には早くも第一波の痛みがやってきました。
このときはすぐに収まったので12:45に会場を離れ、帰宅することにしました。
問題は会場から帰る手段で、運動は控えよとのことでバスで来たはいいものの、帰りの直近のバスが1時間後、ということに・・・
結局1時間ほど歩いて帰ってきました。
なお、途中でアイス食ってレッドブル飲んでます。余裕を通り越して油断してます。
16:30になって、左腕の痛みが持続するようになってきました。
18:00に夕食を摂っておとなしくしていたものの、19:00には弱いながらも吐き気が出てきました。
それでも熱はなく、37℃にぎりぎり届かないくらいが続いています。
この日は21:30に寝ました。

9/6
日付が変わってすぐ、左腕の痛みで目が覚めました。
耐えられなかったので解熱鎮痛剤を飲んで寝直したところ、5:30まで寝ることができました。
熱は相変わらず36℃台後半で、特に目立った体調不良もなかったため出勤することに。
途中何度か違和感を覚えつつも無事退勤。
念のため、早めの22:00に寝ました。

9/7
またしても日付が変わってすぐ、左腕の痛みで目を覚ますことに。
このときは薬は飲まずに寝直して、5:00に起床。
しかし左腕を動かしたときの痛みが前日より強く、まともに動かせなかったので休むことにしました。
起床した後に解熱鎮痛剤を飲み、それが切れるであろう時間帯になると、今度は右腕が痛くなってきました。
左腕をかばっていたら右腕に負担がかかった、とかでしょうか・・・?
左腕の痛みはこの日で消えました。


2回目

10/3
夜ふかしの影響か、6:15に起床。昼食は1回目と同じ時間帯に摂りました。
接種のブース配置が1回目と変わっていて、実施する側も試行錯誤なんだな、と実感させられました。
経過観察中も特に異常はなく、12:45に会場を離れました。
そして1回目と全く同じく、途中でアイス食ってレッドブル飲みながら歩いて帰ってきました。
16:00になると左腕の痛みがやってきました。痛みの強さは1回目と変わりません。
18:00に夕食を摂ったあたりまでは1回目と同じでしたが、ここから様相が一変します。
18:30には頭痛も出てきて、19:00には風邪のときのような感覚がするようになりました。
19:40になると熱も37.0℃に到達し、座っているのもきつい状態に。
20:00にベッドに入り、いつもと違って即消灯します。
しかし21:00、1時間も経たずに目が覚めると、夕食をすべて吐き出してしまいました。
このときに熱を測ると37.8℃で、本格的にまずいと覚悟しました。
その後も寝ては1時間ほどで起きる、というサイクルを繰り返しています。
22:15には38.3℃、23:15には37.7℃の記録が残っています。
どちらのときも水をコップ半分ほど飲んでいます。

10/4
0:15に目が覚めたときには吐き気がなかったため、はちみつレモンを一口飲んで寝直すことに。
1:00、吐き気がぶり返してトイレに直行、液体しか出てこない状態でした。
このときの体温は37.9℃で、下がる気配はありません。
2:20に目が覚め、さすがに限界なので解熱鎮痛剤を飲んで寝直し。
これが効いたのか、6:00まで寝ることができ、起きた時点で36.8℃まで下がっていました。
吐き気もなかったのでウィダーinゼリーを朝食にして、少し様子を見ることに。
あれだけ苦しんで数時間しか経っていないので、この日は仕事を休むことにしました。
8:00、熱は37.0℃まで上がり、寒気を感じるようになります。
8:45、熱は37.1℃ですが、吐き気が再度襲来。
9:45になると、熱は38.3℃まで上がっていました。
10:00になって解熱鎮痛剤を飲んだところ、楽になったのか、寝ていたようです。
14:00、起きたので熱を測ると36.9℃、薬が効いているようです。
その後は18:00まで1時間おきに熱を測っていますが、36℃台後半で推移しています。
18:00になっても上がってこないので、様子見を兼ねてインスタントの野菜スープを作って飲むことにしました。
スープ1杯完食するのに30分くらいかかったので、相当弱っていたんだろうと思います。
完食後も吐き気はなく、山場は越えたと感じました。
20:15には熱が37.0℃になっていたので、念のため解熱鎮痛剤を飲んでから寝ることにしました。

10/5
最初に目が覚めたのは2:30、熱を測ると36.4℃と平熱だったので、特に何もせずに寝直し。
6:20に起床し、この時点で36.7℃でした。
ベッドから起き上がることもできたのですが、ほぼ1日ダウンしていた直後だけに、歩いていると浮遊感があったりして、この日も仕事は休むことにしました。
7:00にはウィダーinゼリーと野菜スープで朝食、9:00にはカロリーメイトとポカリスエットも採っています。
このあたりから食欲が強くなり始めました。
12:00にはカップヌードル、14:00にはカロリーメイト、17:00には一本満足と、目についたものを手当り次第食べてる感じです。
その後は風呂に入って、18:30にいつもの夕食であるフルグラを完食しました。
19:00の時点で体温は36.7℃と、熱は沈静化しています。
風呂に入ってから、体のイメージと実体がずれるような感覚も弱まっていきました。
20:30にはワンタン麺まで食べています。

10/6~
記録は10/5で終わっています。
10/6には左腕に痛みが残る以外回復したので出勤。
しかしこの日でも痛みは抜けず、完全に痛みが抜けた、と自身を持って言えるのは今日(10/9)です。
2回目は1回目と違って右腕が痛くなったりはしませんでした。
おそらく右腕を使う余裕すらなかったということでしょう。


今振り返ってみて
まず第一に、ワクチン接種の1時間後にエナドリ飲むな、ということですね。
カフェインで酔う俺でも特に悪影響はなかったと思われるので、禁忌というわけではなさそうですが、やめたほうが良いでしょう。

そして10/4PMですが、Twitterで見かけた「食欲が増す」という症状(?)そのものです。
家から出られない間の食料は用意していましたが、予想以上の速度で消えていきました。
仮に余ったとしてもいつか食うので、食料の備蓄はもっと余裕を持ったほうが良さそうです。

最後に副反応の吐き気について。
接種時の案内によると、吐き気が発現する確率は10~50%とのことです。
確率が低い方を引いたことになりますが、自分の免疫力がちゃんと働いていることの証拠だと思うことにしましょう。

今日の1曲:

2021年8月14日土曜日

コヤンスカヤの名前について考察

これまでブログでは取り上げてきませんでしたが、俺はFGOプレイヤーです。
始めたのが2018年4月で、2部2章の配信前後だったと記憶しています。

直接のきっかけは2部1章のCMでしたが、もう1つ大きなモチベーションとなっていたのがこちら。


こちらの動画のアップロードから3年4ヶ月、ついに先日「光のコヤンスカヤ」として実装されました。


ゲーム内での活かし方については攻略サイトとかYouTubeとかで多くの方が取り上げているので、ここでは扱いません。
代わりに「コヤンスカヤという名前」について取り上げてみたいと思います。
俺が行き着いた仮説が2つありまして、その2つを書いていきます。

仮説1:ロシア風の名前
ロシアの人物で○○スカヤという姓を目にすることがあります。
例えばジャーナリストのアンナ・ポリトコフスカヤなどですね。
○○スカヤは女性形で、男性形だと○○スキーになります。
ドストエフスキーとかチャイコフスキーがこれに該当しますね。
コヤンスカヤ自身も「TV(タマモヴィッチ)・コヤンスカヤ」とロシアっぽい響きのフルネーム(?)を名乗ったりもしたので、可能性は高そうです。

この場合、上述の姓がありますので、「コヤンスカヤ」は「コヤン」と「スカヤ」に分けて考えるべきでしょう。

まずは「スカヤ」から。
キリル文字で「ская」と綴り、ラテン文字に転写すると「skaya」になるとのこと。
調べてみると、ロシア語で形容詞を作るときの接尾辞であるということがわかります。
例えば「Россия」(ロシア)に「ский」(скаяの男性形)がくっついて「русский 」(ロシアの)になります。
ということは「コヤンスカヤ」は「コヤンの」という意味になります。

「スカヤ」の意味がわかったところで「コヤン」の意味を考えてみます。
海外のファンサイトなどではコヤンスカヤは「Koyanskaya」と綴られます。
「koyan」をキリル文字に転写すると「коян」あるいは「кояан」になるでしょう。
これをGoogle翻訳に入れると、言語の自動検出でキルギス語になりました。
そのまま日本語に翻訳すると「ウサギ」という意味だそうです。
光のコヤンスカヤの召喚時口上に「未来からやってきた万能美人秘書型バニー!」というセリフがありますし、なんだかそれっぽい気がします。
しかしこれには2つの弱点があります。
1. 逆翻訳かけると綴りが変わる
引き続きGoogle翻訳に「ウサギ」と入力して日本語→キルギス語に翻訳すると「коён」になります。
俺が「koyan」を「коян」「кояан」と転写したのが間違いだったのかもしれず、その場合はあまり反証にはならないかもしれません。
2. ロシア語とキルギス語の関係
キルギスは旧ソ連の構成国ですが、ロシア語とキルギス語は言語の系統/分類が違います。
ロシア語:インド・ヨーロッパ語族 > スラブ語派 > 東スラブ語群 > ロシア語
キルギス語:テュルク語族 > キプチャク語派 > キルギス語
キルギスの領土に当たる地域にロシアが影響力を及ぼすようになるのは1865年のロシア帝国によるコーカンド・ハン国併合前後からで、それまでは匈奴や突厥、モンゴルなどが統治してきた地域のようです。
語族まで違うのであれば、ロシア語の接尾辞と組み合わせるのは無理があるのかもしれません。
ただ、ツポレフTu-144に「コンコルドスキー」、Tu-4に「ボーイングスキー」という蔑称に近いニックネームがつけられるなどの例があるため、あまり神経質にならなくてもいいのかもしれないですね。

「コヤン」もロシア語であるという前提で調べましたが、1つ大事なことを忘れていますね。
日本のインターネットミームで、キツネの鳴き声を「こゃーん」と表現します。
コヤンスカヤが「タマモヴィッチ」と名乗ったこと、その耳と尻尾を見ると、キツネにつながるこちらなんじゃないかという気もしてきます。

仮説2:映画/曲のタイトル
ドイツのエピックメタルバンド、Equilibriumの5thアルバム「Armaggedon」に「Koyaaniskatsi」という曲が収録されています。
日本語盤では「コヤニスカッツィ」という邦題になっています。
また、1982年に「Koyaanisqatsi」という映画が制作されています。
こちらの邦題は「コヤニスカッツィ」あるいは「コヤニスカッツィ/平衡を失った世界」です。

これの意味を調べる前に、Equilibriumは俺が好きなバンドなので、「Koyaaniskatsi」を貼っておきましょう。


さて、どちらも英語とは思えない字面ですね。
まずは先に世に出た映画の方から見てみます。
これはアメリカ先住民の部族の1つであるホピの言葉で「常軌を逸し混乱した生活」「平衡を失った世界」という意味だそうです。
次に曲のほうですが、アルバムのリリースに伴って行われたインタビューにおいて、ギター兼キーボードのルネ・ベルティオムが「アメリカの先住民であるホピの言葉で平衡の逆、平衡がないことを意味する」と答えています。
ちなみにバンド名のEquilibriumは均衡や平衡という意味なので、バンド名と真逆の曲を作ったことになりますね。
どちらもホピの言葉で「平衡を失った」という意味であると考えてよいでしょう。

ゲーム内でのコヤンスカヤの行状を見てみると、こちらの意味がしっくり来る気がします。
更に言うならば、第1部の人里焼却、第2部の地球白紙化と、FGOの舞台そのものが「常軌を逸し混乱した生活」「平衡を失った世界」と言えるのではないでしょうか。

以上が俺の行き着いた2つの仮説です。
まとめると
1. キルギス語で「ウサギ」を意味する「коён」+ロシア語の接尾辞「ская」
2. キツネの鳴き声の「こゃーん」+「ская」
3. ホピの言葉で「平衡を失った世界」を意味する「Koyaanisqatsi」「Koyaaniskatsi」
の3つが候補となります。
個人的には2.なんじゃないかなあと思いますが、口上にかけている1.や、世界観にもつながる3.でも面白いと思います。
もちろん上記の中に正解がない可能性は十分ある、というかその可能性のほうが高いのでしょうが、あれやこれやと調べて見るのはとても楽しかったです。


今日の1曲:

2021年8月4日水曜日

モンスター スーパーフュエル

2021年8月3日、モンスターエナジーに新しいシリーズが増えました。
飲んだ後に悪酔いするので常用はしていませんが、新しいのが出たら試したくなるというものです。
ということでさっそく試してみました。

まず公式サイトはこちらです。
シリーズ名が「モンスター スーパーフュエル」で、フレーバーの名前が「ブルーストリーク」と「レッドドッグ」の2種類です。
「スポーツドリンク系エナジードリンク」とありますが、正直どっちだよという気がしますね。

まずはブルーストリークのほうから。
大沼まで自転車で行ってきた帰り道に買ったので、自転車用のグローブを着用した手で持っています。
英語表記は「BLUE STREAK」ですね。streakにはいくつか意味がありますが、たぶん稲妻でしょう。
液色はフレーバー名の通り青です。
これまで飲んできたエナジードリンクで青いものはなかったと思います。
黄色~オレンジが多くて、ビーストアイの緑が異彩を放っていましたが、それに負けないくらいのインパクトです。
味は通常のモンスターエナジーほどの刺激や薬臭さはなくて、少しケミカルっぽい甘みと塩味、という感じでした。
公式サイトに「スポーツ時のエナジーチャージに」とあるので、強すぎる刺激は避けたのでしょうか。

続いてレッドドッグです。
ジョギング用のグローブを着用してますが、無駄に色が合いました。
こちらの英語表記は「RED DAWG」ですね。dawgとはdogのアメリカ南部発音で、それがスラング化したものだそうです。
こちらの液色もフレーバー名の通り赤です。
赤いエナジードリンクはバーンという前例がありますね。
味はブルーストリークに近いですが、フルーツのような感じがほのかにします。
しかしそれよりインパクトがあったのが匂いで、刺激臭といってもいいくらい強く感じました。
ブルーストリークで強い刺激を避けたものと思い込んでいただけに、これにはやられました。

2種類比べるのはここまでにして、共通するポイントをいくつか見てみます。
まずはボトルのラベルに描かれたアイコンやキャッチフレーズです。
写真はレッドドッグのものですが、ブルーストリークも色違いで同じ内容が載っています。
「電解質」「水分補給」など、スポーツドリンクを意識したものがてっぺんに来ています。
一方でエナジードリンクらしさもあり、「カフェイン」「スポーツ時のエナジーチャージを」とありますね。
カフェインは27mg/100ml含まれていて、1本だと148mgになり、通常のモンスターエナジーとほぼ同じくらいになります。
世界反ドーピング機関(WADA)による分類では、カフェインは興奮剤として監視プログラムの対象になっています。
これは検査対象であるものの、検出されてもドーピングとして扱わない物質です。
スポーツ時の摂取を謳う商品がドーピング検査対象の物質を含んでいていいのかな、という気はしますが、あくまでも一般消費者向けということでしょう。
また、通常は「Unleash The Beast」であるキャッチフレーズが「HYDRATE THE BEAST」になっています。「獣を解き放て」が「獣を潤せ」になっていて、スポーツドリンクらしさを重視しているようです。

次にボトルの形についてです。
上の写真の通り寸胴です。
内容量が550mlあるため、500mlボトルと同じスペースに収めるにはこうなるのも当然でしょう。
そのおかげで、自転車のボトルホルダーにも入ります。
自転車に限らず、クルマとかのボトルホルダーにもたぶん同じように入るでしょう。
形では飲み口の大きさも気になりますね。通常のペットボトルよりもかなり大きいです。
こんな感じの大きい飲み口が記憶にあるような気がしていたんですが、1日くらい経ってようやく思い出しました。学生のころに時々飲んでいたゲータレードがこれくらい大きい飲み口でした。
Twitterでも「ハワイで飲んだゲータレードっぽい」という感想があり、もしかすると全体的なお手本がゲータレードなのかもしれません。

ここでキャッチフレーズの「スポーツドリンク系エナジードリンク」を振り返ってみます。
スポーツドリンクの要素を取り入れつつも、あくまでも主軸はエナジードリンクであると考えるべきでしょう。
なので、スポーツドリンクの代わりに飲むとたぶん痛い目にあいます。

参考資料:
WADAの禁止物質リスト
※左側の「OTHER FORMATS」から「Prohibited List - Monitoring Program」をクリックすると監視プログラムリストのPDFを見ることができます

今日の1曲:

2021年7月17日土曜日

新しいメガネケース

2週間前になりますが、函館蔦屋書店でこんなの買ってきました。
タイトルの通りメガネケースです。
右には無料配布のペーパーも写ってます。

派ッッッ手な絵が入っていますが、これはオーストラリア出身のモルガというイラストレーター/アーティストの作品です。
公式ページでは「ユニークでヴィヴィッドなスタイル」とされています。
よくわからんけど、ユニークなのは間違いない。
現在蔦屋書店でモルガの特集コーナーが設置されていて、そこに売っていました。

なんで買ったのかというと、これまで使っていたメガネケースが限界に近いからです。
具体的には蓋を止めるマジックテープがボロボロになってきて、寝るときにメガネを入れて蓋を閉めても、朝には開いている状態でした。

で、買ってきた新しいメガネケース、売っている状態では1枚の板です。
裏返すと、組み立てたときに側面になるパーツが付いています。

組み立てると筒状になります。
蓋はマグネットで止めるようになっているので、長く使えそうです。
各面は指で曲げるように力を加えると変形するくらいの硬さですが、普通に使うぶんには十分硬いでしょう。マグネットが入っている面はもっと硬いです。
表面は小さな凹凸があるように見えますが、触るとつるつるしています。俺の手が凹凸を感じられていないだけかもしれませんけど。

買ったときは平らに戻して持ち運ぶこともあるかなと考えていましたが、今のところ家から持ち出していません。
ただ、持ち運ぶにしても組み立てた状態のままになると思います。
理由は平らにするとそこそこ面積を必要とすることと、最初のうちは平らな状態の癖がついていて、開けると元に戻ろうとする力が結構強かったので、その状態に戻ることを避けたいからです。

買うときは1500円という値段で結構迷いましたが、今のところ満足しています。
個人的には蓋を閉めたときの音と感触が好き。

今日の1曲:

2021年7月10日土曜日

ブシナビ

6/27(日)、函館蔦屋書店でのヴァンガードの非公認大会に出たとき、店員さんから参加者に以下の連絡がありました。
「来週以降の公認大会はブシナビを使うので、準備しておいてください」
で、少しだけ使ってみたのでこのブシナビを取り上げたいと思います。
「取り上げてみたいと思います」とは言いますが、正直不満が重なっていますので、マイナス意見が多めとなっています。

まずはブシナビの公式ページを見てみます。
少しスクロールするとAppleのApp StoreとGoogle Playへのリンクがあります。
大会の最中も結果の報告で使うとのことですから、スマホ前提なのでしょう。
ずっと下まで行くと「ブシナビWEBサイトはこちら」というリンクもあるので、WEBアプリとしても使えるようです。

さっそくアプリをインストールして見たところ、ログインを求められます。
アカウントを持っていなかったので登録しようとしたところ、いきなり不満点にぶち当たりました。

不満1:アカウント作成時に求められる情報が多すぎる
TCGの大会に参加するためのアプリですので、アカウントを作るにしても、メールアドレスと名前くらいあれば事足りると思います。
ところが、ブシナビでは以下を入力するように求められます。
・メールアドレス
・氏名とフリガナ
・HNとフリガナ
・住所
・電話番号
・性別
・生年月日
HNで参加したい人もいるでしょうから、本名とHNがあるのはいいでしょう。
性別と生年月日も、商品のユーザー層についての調査と考えると、あったほうが都合がいいのでしょう。
住所と郵便番号、必要ですか?
もしかすると公式大会などでエントリー書類やらを届ける宛先として使うのかもしれませんが、FAQにもこれらが必要な理由が説明されていません。
利用規約にはブシロードの個人情報保護方針に従って個人情報を取り扱うとあるので、そこまで疑わなくてもいいのではないかとも思うのですが、未だに納得していません。

「なんでこんなに必要やねん」と思いつつもアカウントを作って、大会にエントリーできるようになりました。
画面の左下に「イベント検索」というメニューがありますので、ここから検索して登録できそうです。
先程(2021/07/10 16時頃)のスクリーンショットがこちらです。
いくつかつっこみたいですね。

不満2:なぜいきなり検索結果が出る
「イベント検索」というくらいですから、タップしたら検索条件を入力するのが当然では?
検索条件を入力するには、右上の虫眼鏡マークをタップしてから進まねばなりません。
もしかしたら、俺はすでに「タイトル:ヴァンガード」で検索したことがあるからこのようになっているのかもしれませんが、それでもこの動作は納得できません。
「1回検索したら、次も同じ条件で検索するだろう」という考えで作ったのかもしれませんが、この考えは正しいのでしょうか?
「前はヴァンガードの大会を検索したけど、今回はヴァイスシュヴァルツの大会を検索したい」とか、検索条件を変更するのはそんなにレアな操作ですか?
たかが1タップでも手間は手間です。多くの大会に出るヘビーユーザーに不便な仕様というのは、営利企業のアプリとしてどうなのかね、と思います。

不満3:絞り込みが甘い
大会の情報はどこかのDBに格納されていて、検索条件に合致するものを表示していることと思います。
先程のスクショには7/10の10:00開始、つまり閲覧時点でもう開始されている(そしておそらくもう終了している)大会まで表示されています。
その他の大会日程を見ると毎週土曜日10:00から大会を行っているようですので、7/3の大会は検索条件に合致しないとして表示していないのでしょう。
以上から、表示条件のうち日程は年月日までしかチェックしていないと推測します。
開始時刻までチェックして、開始時刻を過ぎたイベントは表示しないほうがいいのではないでしょうか?
イベントを検索するということは、エントリーしようと考えて検索しているパターンが多いでしょう。
エントリーした人が結果報告のために大会のページを開くというのはありますが、その場合は「マイイベント」から開けるので、検索結果に表示されなくても問題ないはずです。
エントリーするために検索したのにエントリーできないイベントが表示されるのはノイズであり、利便性を下げているのではないかと思います。

目的の大会にたどり着いたのでエントリーを済ませます。
「マイイベント」からエントリーした大会を開いてみます。
無事にエントリーできているのはいいですが、利用ガイドにないものが表示されています。

不満4:アンケート入力って何
FAQを見ても「アンケート入力が押せません」というのはあるものの、アンケートが何なのかは記載されていません。
アンケートに答えないと参加できないのかと思ったら、7/4の大会ではアンケートなしで参加できました。
スマホの画面も大型化しているとはいえ、パソコンのディスプレイと比べれば圧倒的に小さいです。
その貴重なスペースを意味不明な項目で占めるのは不便です。意味のないものであれば撤去するべきではないでしょうか。

エントリーを確認できたのでアプリを終了しましょう。
では戻るボタンを押して・・・

不満5:戻るボタンの挙動がWEBページそのまま
戻るボタンを押すと、これまでアクセスしてきたページの履歴を遡っていきます。まるでブラウザの戻るボタンを押したかのようです。
そういえば、ブラウザで公式ページを開くと、「ブシナビWEBサイトはこちら」というリンクもありましたね。ちょっと見比べてみましょう。
1枚目がアプリ版、2枚目がWEB版のものです。アプリ版はログイン状態、WEB版は非ログイン状態という違いはありますが、さほど変わらないように見えます。
つまり、アプリ版も結局WEB版にアクセスしているのではないかと思います。
AndroidにはWebViewというクラスが用意されていて、これを使うとアプリ内でWEBページを表示できます。
メニューの表示の仕方が違うので、WEB版をそのまま表示しているわけではなさそうですが、HTMLを解析して必要なところを書き換えているとか、サーバー側でユーザーエージェントを見て返すHTMLを切り替えているとか、そういう細工を入れているのかもしれません。
使う側からすると内部の実装は大した問題ではないのですが、じゃあネイティブアプリ版は必要なの?と疑問が浮かんできます。

なんとかメインらしい画面に帰ってきました。
では戻るボタンを押して終了しましょう。もしかしたら終了確認のダイアログが出るかもしれないですね。

不満6:戻るボタンでアプリを終了できない
何も起きません。
最初に触ったときには思わず「は???」と声が出ました。
アプリのメイン画面で戻るボタンを押してアプリを終了する。Androidユーザーには当たり前の操作ではないでしょうか?
それができないとはどういうこと?
そもそも、普通にアプリを作ったらメイン画面で戻るボタンを押したら終了します。
俺が昔作ったAndroidアプリでも、特に設定を変えるとか処理を書くとかなしで、メイン画面で戻るボタンを押したらアプリが終了しました。
ということは、わざわざ戻るボタンのイベント処理を拾って握りつぶしているのでしょう。
一体何のために?
「誤ってアプリを終了しないように」ということなのかもしれませんが、それなら再起動すればいいだけですよね?
iOSにはOS単位での戻るボタンがないので大した問題にはならなさそうですが、Androidでは他アプリとの決定的な操作の違いになっています。
アプリを終了するというのは、アプリの使用に必ずついて回る最も基礎的な操作です。
そこは他のアプリを足並み合わせても良かった、というか合わせてほしかったところです。

では大会に出かけましょう。
結果の報告または承認のためにアプリの操作が必要なので、一応起動しておきます。

不満7:謎のスプラッシュ画面
今の俺がブシナビを立ち上げると以下の画面になります。
「START」を押すと不満5で貼ったスクショの画面に遷移します。
で、この「START」って何?
規約により、ブシナビのアカウントは1人で1つしか持つことができませんし、複数人で1つのアカウントを共有することもできません。
したがって、ログインアカウントを切り替えるという機能はこのサービスにおいて不要であると言えます。
だったらスプラッシュ画面を起動して、バックグラウンドでログインを済ませ、ログインとページ取得が完了したらメイン画面に遷移する、というのがスマートではないでしょうか?
今の実装ではアプリを起動するたびに意味なくボタンを1回押させられていることになります。
ユーザーはモンキーテストのための人員ではないのです。
不満2でも書きましたが、たかが1タップでもユーザーの負担を減らそうという設計思想はないのでしょうか?

ここまでずらずらと不満をぶちまけてきましたが、これを改善するためになにかできないか、と考えてみます。

改善案1:WEBアプリに一本化する
俺の不満は多いですが、特に強いのは戻るボタンなどの操作感です。
ネイティブアプリの感覚で触ってWEBアプリのように動くから違和感があるのです。
じゃあWEBアプリに一本化すればいいんじゃないか、ということです。
これならAndroid版/iOS版を作る必要がなくなりますから、WEB版の改善に集中することができます。
また、公式ページからのリンクもGoogle Play/App Storeが不要になり、スペースを少しだけ節約できます。

改善案2:もっとネイティブアプリに寄せる
ある意味案1と真逆です。
まず、必要な画面は決まっているのでテンプレートは用意できるはずです。
テンプレートに入れる情報は(たぶん)今と同じくWEB版にアクセスして、HTML解析でもすればよいでしょう。
検索やエントリーなどで情報を送信する必要が出てきますが、これもWEB版と同じ内容を同じ宛先に送ればいいはずです。
HTML解析も、HTTPリクエスト/レスポンスの生成も、探せばライブラリが転がっていることでしょうから、それを使えば実装の手間も少し減るでしょう。
iOS版も同じようにネイティブアプリに寄せると考えると、それでもリソースはかかってしまいます。
それでも今よりはマシになる、と思います。

ちなみに、現時点(2021/07/10 17:53)でのGoogle Play/App Storeの評価を見てみると・・・
1枚目がGoogle Play、2枚目がApp Storeです。
どっちも平均2点台、そして☆1が最多です。
Google Playでは「大会でこれを強制されるくらいならブシロードのゲーム全部引退する」というレビューもあります。
本気なのかはさておき、ここまでボロクソ言われているということは、使いにくいと感じているユーザーが多いことの表れではないかと思います。

ブシロードによると、本格的な利用開始は2021年8月を予定しているとのことです。
ブシナビのアプリリリースが3月ですので、テスト期間として5ヶ月用意している、ということですね。
しかし、緊急事態宣言などで大会開催を見送った店舗も多いことでしょう。
それで本当に必要なテストは実施できたのか?と疑いたくなります。

現在のアプリバージョンはGoogle Playで1.2.0、App Storeで1.2.1となっています。
メジャーバージョンが上がっていないということは、ブシロードとしてはこのアプリに重大な欠点はないと考えているとみなしてよいのでしょう。更新履歴も「軽微な不具合の修正」だけですからね。

俺個人としては、ビジネスの視点を度外視して言えば、改善案で出したように抜本的な見直しをしたほうがいいと思います。
もちろんすべての決定権はブシロードにあるわけですが、「こんなはずじゃなかった」と後で泣き言言うのだけは避けていただきたいです。

今日の1曲:

2021年7月3日土曜日

レジェンズ Blu-ray BOX

6/25(金)夜、以下のTweetがいろいろな方のRTから俺のタイムラインに流れ込んできました。

本放送が2004年4月~2005年3月なので、16~17年の時を経てのBD化ですね。
即予約、とまではいかないものの、翌日まで考えた末に予約しました。

実家には放送時に録画したものが残っているのですが、年代が年代だけにVHSなんです。
録画したものなので、災害情報とかニュース速報のテロップが入っていたりもします。
普通はDVDを買うところですが、当時DVDプレイヤーを持っていなかった上、付近に取り扱う店もなかったので、DVDは買いませんでした。
当時は高校生だったし、中古CD漁りに時間とお金をつぎ込んで余裕もありませんでしたからね・・・
そんな状況でのBD-BOX化は、全く予想していない朗報でした。
しかも描き下ろしイラストのボックス+ブックレット付きとのこと。
届くのが今から楽しみです。

当時の思い出とかを書こうとすると、何度書いて推敲しても「あれもこれもある、まだまだある」が尽きないと思うので、それは置いておきましょう。
代わりに「BD-BOX化を期にこんな事があるといいな」をいくつか挙げてみようと思います。

・小説版の続編刊行
当時高校に持っていって仲の良いクラスメイトに布教しようとしていたこともあって、小説版にも思い入れがあるんです。
本来小説版はアニメ版の前日譚となるはずだったのですが、その後アニメ版は大幅な路線変更(では済まないレベルだった様子)があり、小説版は全く孤立したストーリーとなってしまいました。
小説としての続きを刊行して、小説版の完結を見たいのです。

・「リボーンだよ! シロンくん」単行本刊行
レジェンズの漫画版は月刊ジャンプで連載されていたのですが、それと並行してアニメ版に基づくギャグ時空の4コマ漫画「リボーンだよ! シロンくん」も連載されていました。
月ジャン連載の全14話+月ジャン増刊号の1話があったのですが、連載当時から今日に至るまで単行本化されたことがありません。
数えてみたところ120ページほどだったので、ジャンプコミックスで単行本とするにはページが足りなかったのかもしれません。
ですが、A5判の4コマ漫画単行本とかだと150ページに満たないものもあるので、その枠で刊行してもらえないでしょうか・・・?
雑誌のスクラップでしか読めないというのはあまりにも寂しいです。

・「龍Q伝説」の収録
これはBD-BOXについての願望です。
首都圏外郭放水路の施設である龍Q館でだけ見ることができる(できた?)「甦る龍Q伝説」、見たいんです。
すでに内容、仕様、特典が明記されている以上可能性はほぼゼロですが、実現したら嬉しいです。

結構前から思っていた願望を3つ挙げてみました。
同じ願望をお持ちの方、違った願望をお持ちの方、いろいろいると思います。
1つでも現実になってくれると嬉しいですよねぇ。

いつもなら何かの曲を貼って〆るのですが、今日はレジェンズBD-BOXのCM(「風のレジェンズ」ver.)で〆たいと思います。

2021年6月19日土曜日

FAC製作記 その3 JsonSerializer.SerializeAsync()で何も出力されない

本題の前に少しだけ、これまでの反省といいますか、ちょっとした注意書きを。
前回が前提となる事柄に分量や気力などを吸い取られまくったので、これからはそういう事柄は適当にカットします。

ここから本題。
これまでの2回はダイアログでしたが、今回はJSONの処理についてです。
FACでは設定した内容をsettings.jsonというファイルにJSON形式で保存しています。
起動したときにファイルから設定を読み込んで、画面の「設定を保存」ボタンをクリックするとファイルに設定を書き込むようになっています。 (画面についてはFACについての最初の投稿にスクショを貼っています)

【FACでのJSONの扱い方】
.NET 5対応の前後でJSONのシリアライズ/デシリアライズに使っているものが異なっています。
.NET 5対応前:Jil
.NET 5対応後:JsonSerializer
Jilは個人が作っているライブラリ、JsonSerializerは.NET Core 3.0/C# 8.0から追加された標準機能です。

今回の課題と解決策は以下の通りです。

【課題】
JsonSerializerの非同期シリアライズ&書き込みを使うと何も出力されない。

【解決策】
JsonSerializerはJSONのシリアライズ/デシリアライズにだけ使う。
ファイルの読み書きはStreamReader/StreamWriterに任せる。

今回は.NET 5対応のときに作ったテスト用アプリのソースコードを見ていただいたほうが早いと思います。

まずは読み込みから。


読み込みのメソッド全部でこれ。
コメントアウトしているDeserializeAsync()がストリームを使った非同期デシリアライズ&読み込みですが、こちらはうまくいきました。

一方の書き込みが以下になります。


try-catchのtry部分しか収まりませんでした。
コメントアウトしたコードにあるSerializeAsync()が非同期のシリアライズ&書き込みメソッドです。
例外は発生しなかったのですが、「課題」に書いたとおり、書き込み完了後にファイルを開いても何も出力されていない、という現象が発生しました。

【試行錯誤】
まずはFileStreamを作るときに使ったメソッドが良くないのではないか?と疑いました。
スクショの77~79行目で、Open()を使ったり、オプション指定するコンストラクターを使ったりと試しています。
これらはいずれも効果なく、ファイルには何も出力されない状態が続きました。

次にそもそもシリアライズは成功しているのか?ということで、Serialize()でシリアライズした結果をデバッグ出力させてみることにしました。
スクショの82と83行目です。
こちらは成功して、JSON文字列が出力されました。

以上の2つから、JsonSerializerはシリアライズだけ担当してもらい、ファイルへの書き込みは別にすればうまくいくのでは?という発想が出てきます。
それを試したのが90~92行目です。
コメントにある通りで、ファイルにはJSON文字列が出力されていました。成功です。

さて、これで読み込みはすべてJsonSerializerで、書き込みはJsonSerializer+StreamWriterで処理している状態になりました。
これでもちろん動くのですが、書き込みがJsonSerializer+StreamWriterなら、読み込みはJsonSerializer+StreamReaderにしたほうが、対照というか見栄えというか、きれいに見えませんか?

ということで今度は読み込み側に手を加えてみます。
ファイルからの読み込みをStreamReaderに受け持ってもらい、JsonSerializerはデシリアライズだけ担当してもらいます。
読み込みの52と53行目ですね。
実行した結果は変更前と変わらず、こちらは特に引っかかりなく書き換えに成功しました。

なので、今回はJsonSerializerを使う範囲をJSONとオブジェクトの変換に限定し、ファイルの読み書きはStreamReader/StreamWriterを使うことで解決としました。

【考察】
最初はファイルへの書き込みが終わる前にファイルを開いてしまったのが原因かもしれないと思ったのですが、それなら書き込めずに例外が発生するはずですので、この可能性はないでしょう。
次にGitHubでJsonSerializerのソースコードを見てみたのですが、わかりませんでした。
処理をたどっていくと、どうやらストリームに直接書き込まずにバッファリングしているようですが、バッファリングしようがしまいが最終的に出力先に書き込むのは変わらないはずなので、これのせいだとも思えません。
なんだったんでしょうね・・・?

【参考資料】

今日の1曲:


2021年6月6日日曜日

FAC製作記 その2 ダイアログの表示をどう実装するか

前回に続きダイアログがテーマです。
前回はそもそもダイアログを表示できないところからスタートし、それを解決して表示できるようになりました。
今回はダイアログを表示できるところからスタートしますが、どこでどのように表示するかがポイントになります。
「いや、必要なタイミングで表示させるだけでしょ?」となりそうですが、その表示させる「だけ」にも考えなければならないことはあるわけです。

今回の課題と解法は以下のとおりです。

【課題】
MVVMの役割分担を壊さずにダイアログを表示したい。
.NET Framework時代はMVVMフレームワークの機能を使って実現していたが、.NET移行でライブラリを乗り換えたので、別の方法を探さなければいけない。

【解法】
ヘルパークラスを使う。
ヘルパークラスはM/V/VMいずれでもない名前空間に作る。

本題の前にMVVMについて。
MVVMは「Model」「View」「ViewModel」の頭文字を取ったもので、この3つでアプリケーションを構成します。
それぞれの役割は以下の通り。
  • Model:アプリケーションの本体、UIに関わらない内部の処理
  • View:ユーザーに見える部分、画面とか
  • ViewModel:ModelとViewの橋渡し
以下はWikipediaの図ですが、MとVM、VとVMがやり取りしてアプリの機能を実現します。
(作者:Ugaya40, CC BY-SA 3.0)

例えばFACの「ファイルを分類する」という機能の場合は以下になります。
1. ユーザーがVの実行ボタンをクリックする
2. VからVMに実行ボタンが押されたことが伝わる
3. VMはMのファイル分類を実行する
4. MからVMにファイル分類の結果が返される
5. VMからVにファイルの分類が完了したことが伝わる
6. Vがユーザーにファイルの分類が完了したことを見せる

MVVMは必ず従わなければいけない規則というわけではありません。
しかし、1箇所に全部まとめてしまうとメンテナンスも大変なので、FACでもMVVMパターンを採用しています。

ではMVVM的にダイアログはどう扱うべきでしょうか?
ダイアログはユーザーに見える部分ですから、Vが扱うべきである、と考えることができます。
しかしVは表示とユーザーからの入力を受け付けることが役割ですから、「いつ」「何を」表示するのか決めるのは越権行為です。
「いつ」「何を」表示するか決めるのはM、またはMとVMの役目と言えます。
ではMとVMで扱うのが正しいのかというと、今度はMやVMが直接画面を触るという越権行為が発生します。

もう1点、MVVMの解説図に「Data Binding」というのがあります。
日本語でもそのまま「データバインディング」と言われます。
Vでイベント(例:ボタンのクリック)が発生したときにVMのどの処理を実行するか、Vにどんなものを表示するのか、など、VとVMを紐付ける仕組みです。
MVVMパターンを使う上で重要なポイントではあるのですが、.NETや.NET Framework標準ではこの機能が用意されていません。
ですので、MVVMパターンを使うときは、データバインディングを含めてフレームワークのライブラリを使うことが一般的です。

ではFACでは.NET Framework時代にどうしていたのか、.NETではどうするのか、1つずつ書いていきます。

【.NET Framework時代】
MVVMフレームワーク:MVVM Light Toolkit
MVVM Light Toolkitはその名の通り薄い軽いフレームワークでした。
シンプルですが、データバインディングを始め必要なものは用意されています。

この時代はダイアログの表示をMVVM + Messengerで実現していました。
Messengerはその名の通り、VMからのメッセージをVに渡す役割をします。
VはMessengerに「xxというメッセージが来たらこの処理を呼んでくれ」と登録します。
VMは必要なときにMessengerにxxというメッセージを送ります。
これでVM→(メッセージ)→Messenger→(登録された処理の呼び出し)→Vという流れができます。
なお、本来はMessengerも自分で作らなければならないのですが、MVVM Light ToolkitにはMessengerも用意されています。

今回の場合、VがMessengerに登録する処理でダイアログを表示しています。
こうすることでVMから直接Vを触ることなく、つまりMVVMの役割分担を壊さずにダイアログを表示することができます。

【.NET時代】
MVVMフレームワーク:Prism
Prismはある意味MVVM Light Toolkitとは逆で、盛り沢山なフレームワークです。
機能的にはMVVM Light Toolkitで十分なのですが、最新版のリリースが2018年9月と古く、.NETに対応する様子もないため、.NETへの移行を期にPrismに乗り換えることにしました。

PrismにもMessengerに使えるEventAggregatorが用意されていますが、調べてみるとMVVM Light ToolkitのMessengerと全く同じというわけにはいかないようです。
人によっては「EventAggregatorはVM間の通信を実現する」と言い切っているくらいで、VとVM間の通信に使うのはちょっと怖くなってきました。

ではどうするのか。ここで先に書いた解法です。
FACのフォルダ/名前空間構成は以下のようになっています。
「Models」「ViewModels」「Views」にMVVMのそれぞれにあたるクラスが入ります。
そして、選択して色が変わっている「Helpers」というフォルダがあります。
ここにダイアログを表示するDialogHelperというクラスを入れてあります。(IDialogHelperはDialogHelperで実装する内容を定めたインターフェースです)

ダイアログを表示するときはVMがDialogHelperを呼び出します。
VM→(処理の呼び出し)→Helperという流れですね。
この方式でも、VMから直接Vを触ることなくダイアログを表示することができます。

【残った課題】
Helperクラスを使うことで、「VMが直接ダイアログを表示したりVを触ったりしない」という条件はクリアできました。
しかし「表示はVが受け持つ」という条件はクリアできていません。
.NETに移行するときにはライブラリの乗り換えなど大きな問題が積み重なっていたため、「VMに表示処理が入っていなければヨシ!」と現場猫化しました。
ということで、.NET Framework時代と同じく、ダイアログの表示はVに戻せないか、後日再チャレンジして見るかもしれません。

【参考資料】

2021年5月30日日曜日

FAC製作記 その1 ダイアログを表示するときに例外発生

先日の通り、FACを作るときに遭遇したあれやこれやを早速書いていきます。

本日のお題はダイアログです。
GUIアプリケーションであれば、ダイアログを使わないもののほうが少ないのではないでしょうか。
FACでも結果の通知とフォルダーの選択にダイアログを使っています。

今回の現象と解決策だけ書くと以下のとおりです。

【何があったのか】
.NET Framework 4.7.2から.NET 5に移行するとき、ダイアログの表示で例外が発生した。

【どうしたのか】
ダイアログのライブラリを乗り換えた。

それぞれについて詳しく書く前に、.NET Framework 4.7.2時代のダイアログについて説明します。

【コトが起きる前】
.NET Framework 4.7.2時代、FACはダイアログとしてWindows API Code Packを使っていました。
これはもともとMicrosoftが提供していたもののようですが、俺が知ったときにはすでにMicrosoft直々の公開は終了していて、いろいろなユーザーがかつて公開されていたものをそのままor独自のモディファイを加えて公開している状況でした。
上記のリンクはそれらのうち俺が使っていたものです。

たかだかダイアログでなぜライブラリを使っているのかというと、標準では足りなかったからです。
FACはGUIフレームワークとしてWPFを使っています。
WPFにもダイアログは用意されているのですが、問題があります。
用意されているのは通常の通知用ダイアログのみで、フォルダーを選択するダイアログはないのです。(FACでは使っていませんが、ファイル選択ダイアログもない)

WPFでフォルダー選択ダイアログを使いたい場合、Windows API Code Packは定番と言っていいでしょう。
それについて解説しているブログもすぐ見つかるくらいなので、王道と言ってもいいかもしれません。
Windows API Code Packには通常のダイアログ(※)も用意されているので、ダイアログはすべてWindows API Code Packを使っている、というのが.NET Framework時代のFACでした。
※正確には機能が強化されたTaskDialogなのですが、通知用であれば同じ感覚で使えます。

【起きたこと】
FACを.NET 5に移行するため、事前検証としてテスト用アプリを作って、色々と動作を確認していたとき、事件は起こりました。
Windows API Code PackのGitHubのreadmeにも「.NET 5をサポートしてるよ!」と書いてあるので、同じようにコード書けば動くだろー、と楽観視していました。

まずは通常のダイアログの動作を確認します。


例外が発生しました。
サポートしてるんじゃなかったんかい。

次にフォルダー選択ダイアログの動作を確認します。


こちらは.NET Framework 4.7.2のときと同じように表示されます。
なんでやねん。

【例外のメッセージから調べる】
気を取り直して、例外のメッセージを確認してみます。

System.NotSupportedException: 'TaskDialog feature needs to load version 6 of comctl32.dll but a different version is current loaded in memory.'
EntryPointNotFoundException: Unable to find an entry point named 'TaskDialogIndirect' in DLL 'Comctl32.dll'.

「TaskDialogはcomctl32.dllのバージョン6以上が必要だが、そうでないバージョンがロードされている。」「comctl32.dllに'TaskDialogIndirect'というエントリーポイントがない。」とあります。
調べてみると、dllファイルのバージョンをマニフェストファイルで指定すれば良い、とのこと。

しかし、ここで俺は以下のように思いました。
「.NET Frameworkではマニフェストファイルなんか不要だった。.NETになって必要になるのは納得いかない」
今思い返すと逆ギレですが、「じゃあ別のダイアログを探そう」という発想に行き着きました。

【乗り換え先のダイアログを探す】
「.NET 5対応」「NuGetでインストール可能」を条件に探してみると、Ookii.Dialogsというものが見つかりました。
余談ですが、こちらの作者の方は東大に留学経験があり、サイトのトップにも「Ookii -発音は/oːkiː/(だいたい"oh-key")- は日本語でbigという意味で、私のラストネームと同じ意味です」とあるので、「大きい」を意識した命名です。
さっそくインストールして、Windows API Code Packのダイアログ類を使っていたところをOokii.Dialogsに差し替えてみます。

まずは通常のダイアログから。


問題なく表示されますね。
続いてファイル選択ダイアログも。


こちらも問題なく表示されました。

ということで、この件はWindows API Code PackからOokii.Dialogsへの乗り換えという形で解決しました。

【原因と解決策の考察】
精査したわけではないので、俺の想像であることをご承知おきください。
すでに書いたように、Windows API Code PackはもともとMicrosoft謹製です。
名前から想像するに、WindowsのAPIをごく薄くラップしたライブラリ類なのでしょう。
したがって、.NET Frameworkと .NET 5の差分(標準で参照するcomctl32.dllのバージョンが違う?)を隠蔽しきれなかったのではないかと思います。

Ookii.DialogsはTaskDialogクラスを独自に実装しています。
そのためにcomctl32.dllを参照する必要がなく、Windows API Code Packで地雷になった箇所の影響を受けなかったのではないでしょうか。

【参考資料】

今日の1曲:


2021年5月22日土曜日

作ってるもの

ブログの記事としてはお久しぶりです。
前回の記事が2016年の10月ですので、約4年半ぶりです。

この間特に何かあったとかではないのですが、
「1週間空いてしまった、来週は書こう」
「そうこうしてるうちに1ヶ月空いてしまった、来月こそ書こう」
「1年空いたし、もうよくね?」
という感じで、ずるずると4年半以上何も更新がないという事態になっていました。
これからは不定期でも年に1回くらいは更新したいな、と思っています。

今回のタイトルの「作っているもの」ですが、パソコン上で動かす簡単なツールを作っています。
名前を「Fur Affinity Classifier」としました。以下では頭文字取って「FAC」と記載します。
ソースコードや実行ファイルはGitHubにおいてあります。
READMEは英語で書いてありますが、今回は日本語での説明兼宣伝をさせてください。

【何これ?】
Fur Affinity(以下FA)からダウンロードしたファイルを、ユーザーのIDごとにフォルダーに分類するツールです。
FAについてはご存じの方が多いと思うので、詳しい説明は省略しますが、ケモノに特化したpixivと考えていただいてよいと思います。
classify (分類) に -er を付けて「分類機」という意味合いで名付けました。

【なんで作ったの?】
俺はFur Affinityで気に入ったイラストなどがあると自分のパソコンに保存するのですが、ファイルの分類が手間でした。
ファイルを分類するためには
1. ファイル名を見て
2. ユーザーIDを確認して
3. ユーザーIDに応じたフォルダーを開いて
4. ファイルを移動する
という手順が必要でした。
これがファイル2~3個とかならまだなんとかなりますが、5個10個となると面倒、20を超えると分類に割く時間がもったいないくらいになります。
じゃあ自分の勉強も兼ねて、自動でやってくれるツールを作ってしまえ、というのがスタートラインになっています。

【何ができるの?】
「何これ?」の通り、FAからダウンロードしたファイルを、フォルダーに分類することができます。
FAからダウンロードしたファイルは所定の形式になっていますので、後述する対象のフォルダー内のファイルを走査して、合致するファイルを移動します。
また、「このユーザーはIDと違うフォルダーに振り分ける」という設定もできます。
日本のような非ラテン文字圏だとこういう事態は起きやすいのではないかと思います。

【どうすれば使えるの?】
FACの実行には.NET 5が必要になります。
また、FAC内でも設定が必要です。
実際に俺が使っているときのスクリーンショットを以下に貼ります。
フォルダー名は黒で潰してありますがご了承ください。


スクショのうち「フォルダー設定」、つまり「ダウンロードしたファイルが入っているフォルダー」「IDごとのフォルダーが入っているフォルダー」の設定が必須になります。
IDと違うフォルダーに振り分けたい場合は、スクショで「IDと異なるフォルダーに分類する」となっている一覧に入力することで、「ID」列のIDは「フォルダー」列のフォルダーに振り分けるようになります。

【注意することとかある?】
あくまでも「俺が使うためのツール」を公開したものであるので、要望をいただいた場合は検討しますが、対応をお約束することはできません。
ライセンス(MIT)のとおり、保証とかは一切ありません。

今後はFACを作る上で困ったこととその解決法とか、そういうことも書いていきたいなと思っています。
「え、こんな簡単なモンでもそんなんあるの?」と思われるかもしれませんが、あるんだなこれが。

今日の1曲: