PsyTouSan’s LAB

アプリ開発に関することから、くだらないことまで。

白霧島と黒霧島を飲み比べてみる

今日は黒霧島白霧島を飲み比べます。最近は、少しだけ芋焼酎にはまっているので、そのノリで書いていきます。
霧島シリーズはこれら二つだけでなく、赤霧島とか茜霧島とか様々なシリーズがありますが、まだ飲んでいないので、人生の楽しみの一つとして温めておきます。

f:id:PsyTouSan:20220317173442j:plain:w500
黒霧島と白霧島のワンカップ

ワンカップサイズを買いました。黒霧島のワンカップはそこらじゅうで見かけますが、白霧島のワンカップはあまり見かけません。偶然見かけたコンビニでゲットしました。

動機

黒霧島と白霧島はどちらも一回飲んだことがあります。しかし、両者の違いが分かりにくいなと感じたのが正直なところでした。一回目に飲んだ時は、最初に黒霧島を頂いた後に、しばらくの期間を空けて白霧島を頂きました。なので、今回は黒霧島を飲んだ後に、間髪入れずに白霧島を飲んで両者の違いを探ってみたいと思います。

黒霧島

まずは、一番オーソドックスな黒霧島から飲んでみたいと思います。キャッチコピーはトロっとキリっと黒霧島です。
記事を書いている途中で気づきましたが黒麹で造られているそうです。サツマイモは黄金千貫だそうです。
トロっとは甘み、キリっとはキレの良さを表しているみたいです。なお、アルコール度数は25度です。

f:id:PsyTouSan:20220317173510j:plainf:id:PsyTouSan:20220317173501j:plain

黒霧島と肴です。黒霧島に氷の入ったグラスが凄く似合っています。なにも味のない焼酎とは違って、オンザロックやお湯割りでも、しっかりと味があって美味しいです。

白霧島

続いて白霧島を飲んでみます。こちらのキャッチコピーはどしっとほわんと白霧島です。こちらは、白麹で造られているそうです。しかし、使用しているサツマイモの品種は黒霧島と変わりありません。度数も25度になっています。

f:id:PsyTouSan:20220317212530j:plain:w500
お湯割り

こちらも肴を用意しました。霧島なので、白っぽい肴を用意しました。オンザロックとお湯割りです。
こうして両者を飲み比べてみると、思ったより違いがあります黒霧島よりも、ガツンと来ます。さらに、白霧島の方が口の中に残りやすいです。まさにほわんとしています。

感じた違い

期間を空けて飲んだ場合と、間を空けずに飲み比べた場合では、全然インパクトが違いました。黒霧島の方が、白霧島よりも明らかにキリっとしています。要するに、飲み込んだ後にお酒の風味が潔く去っていく感じです。対して、白霧島は飲み込んでもしばらくの間はお酒の風味が口の中に残留します。味は、白霧島の方が爽快です。ただ、香りに関しては両者ともあまり違いはありませんでした。

黒霧島(「キリっと」は分かりやすい)

キャッチコピーにあるキリっとに関しては、しっかりと感じることができます。白霧島に比べても明らかに、キレがいいです。しかし、トロっとに関しては分かりにくかったです。白霧島との比較をすると、こちらの方が少しだけトロっとしているような気もします。

白霧島(概ねキャッチコピー通り)

こちらのキャッチコピーにはほわんとと入っていますが、これは分かりやすいです。口に入れた時に、口の中で何かが膨張するような感じです。しかも、前述したとおり黒霧島よりもキレが少ないので、余計にほわんと感じると思います。さらに、飲んだ時にガツンと来ます。これが恐らくどしっとということでしょう。

個人的な感想

飲み比べてみると、意外と違いがあることがわかりました。個人的には、黒霧島の方が好みです。白霧島はインパクトと爽快感があり、これはこれで美味しいです。ただ、やっぱ黒霧島です。霧島シリーズの中で、トップクラスの人気を誇るだけあり、万人受けしやすい芋焼酎になっていると思います。
ここからは完全に余談ですが、焼酎とお菓子が想像以上に相性が良くてびっくりしました。焼酎のロックでは、度数も高い上に割とガツンと来る(それこそ白霧島とか)ので、そのインパクトをチョコレートやビスケットなどでそれなりに緩和できるので、料理を食べ終えた後の晩酌には最高かもしれません。

日本酒にアイスを組み合わせるとどうなるか。

この記事は、どうにかしてアイスと日本酒を組み合わせて、美味しく頂く方法はないのかと考えた者の奮闘記です。最後までご覧いただけると幸いです。

動機(合うのか、合わないのか)

これに関しては完全になんとなくです。一般的には、日本酒は天ぷら、刺身、焼き鳥や鍋料理と共に頂くものです。ただ、お菓子とかデザートと組み合わせも悪くないのでは?そう思ったので、今回試してみました。あと、バニラアイスに燗の日本酒をぶっかけると、結構見栄えが良くなりそうなので「合うのか、合わないのか」という点でも気になります。

調べてみると気になるサイトが…

しかし、自分が実践する前に、アイスに酒という組み合わせに先人がいるのではないかと思ったので、検索ワードに「日本酒 アイス」と入れて検索してみました。すると、割と上位の方に以下の記事が出てきました。

magazine.asahi-shuzo.co.jp

日本酒の銘柄として非常に有名な「久保田」を醸造している、朝日酒造さんのサイトです。このサイトを少し覗いてみると、以下のようなことが書かれていました。

  • 芳醇でコクのある日本酒には、「アイスクリーム」や「アイスミルク」と表記されているような、ミルクの風味豊かな濃厚タイプのバニラアイスが良く合います。(引用)

芳醇でコクのある日本酒が、バニラアイスと合いやすいらしいので、これを参考にします。そこで今回使用する日本酒は、芳醇なお酒を中心に試していきたいと思います。ただ、それだけでなく価格が安いお酒や、淡麗辛口のお酒など、いろいろ試してみようと思います。

使用するアイス

というわけで、バニラアイスを用意します。アイスにも多少こだわって頂きたいと思いますので、少し奮発してハーゲンダッツを使用します。ただ私自身、ハーゲンダッツを食べたことがないので日本酒と一緒に食べる前に、そのままの状態のハーゲンダッツを食べてみました。やはり、やや高級なアイスということでかなり濃厚なバニラ味でした。というか、ねっとり感がほかのバニラアイスよりも強かったです。普通に美味しいアイスです。

試した日本酒

アイスと組み合わせる日本酒を紹介します。

  1. 八海山特別本醸造
  2. 魚沼 淡麗辛口
  3. 月桂冠 山田錦純米パック
  4. 久保田 碧寿
  5. 一ノ蔵 円融

この5つで試してみます。個人的には、八海山特別本醸造が2番か3番目に好きなお酒です。安すぎず高すぎず、ちょうどいい価格です。そして、その価格以上に美味しいです。ただ最近は、月桂冠の純米紙パックのお酒を飲んでいます。安価なお酒ですが美味しいです。しかし、この記事で一番注目すべきは、久保田碧寿。先ほど紹介した朝日酒造さんの記事でも紹介されていた日本酒です。このお酒は純米大吟醸としては珍しく、でも美味しく頂くことができる銘柄なので、アイスと組み合わせて頂く事はもちろん、食中酒として頂く事も楽しみにしています。また、バニラアイスに相性がいいとされている芳醇なお酒は、山廃仕込みのものに多いらしいので、山廃仕込みは「久保田 碧寿」と「一ノ蔵 円融」の2つを用意しました。

八海山特別本醸造

今回試す5つの日本酒の中では、唯一の非純米酒(特別本醸造)です。好きなお酒なので、アイスと一緒に食べる前に、少しだけ味わいました。
少しだけ味わった後に、いよいよアイスにかけて頂きます。

f:id:PsyTouSan:20220316210522j:plainf:id:PsyTouSan:20220316210459j:plain
八海山特別本醸造
感想

アイスとの組み合わせは初めてですが、意外と美味しいです。アルコール感がだいぶ緩和されているので、極めて飲みやすく感じました。ただ八海山本来の風味は、衰えてしまいます。雰囲気としては、焼酎のコーラ割に近いような気がしま。度数の高いお酒を、甘い何かで割って飲みやすくする的な感じです。でも、全然悪くないです。

月桂冠 山田錦純米パック

比較的安価な日本酒の中でも、個人的に美味しいと感じているやつです。なんだかんだ気に入っています。アイスと日本酒のツーショットを撮ってみましたが、和洋折衷で意外にも似合っていますね。ブルーベリーがいい感じのアクセントになっていて綺麗ですが、ミントとか乗せてみるとさらに綺麗に映るかもしれません。

f:id:PsyTouSan:20220301190754j:plainf:id:PsyTouSan:20220301190724j:plain

右の写真は、上燗(45度)にした酒をぶっかけた様子です。酒を温めると、アイスにぶっかけた時に少し溶けるので、アイスと酒が混ざり易くなります。

感想

八海山特別本醸造より相性は良いと思います。癖がなくシンプルです。お酒自体は、比較的芳醇なものなのでバニラアイスとの組み合わせは良い方です。

魚沼 淡麗辛口

白瀧酒造さんが醸しているお酒です。白瀧酒造といえば、上善如水でも有名な酒蔵です。
お酒にかけるとこんな感じです。試しに一口だけ飲んでみましたが、普通の美味しいお酒です。少し八海山に近いような気がしますが、魚沼の方が甘くない(辛口)です。

f:id:PsyTouSan:20220316210457j:plainf:id:PsyTouSan:20220316210456j:plain
魚沼

さっきから特に代り映えありません。

感想

少し、八海山特別本醸造と似ている気がしますが、こちらの場合は八海山の売りの一つである麹香があまり感じられませんでした。しかし、肴がアイスの場合むしろ麹の香りが立たない方が良いかもしれません。なので、八海山特別本醸造と比較した場合は、魚沼の方が優勢かと思われます。

久保田 碧寿

主人公です。碧寿は山廃仕込みのお酒となっています。初めて頂くお酒なので、まずは一杯頂きました。もちろんぬる燗で頂きます。ぬる燗なので香りは結構立ちます。しかし、癖が無くてスッキリしており、酸味も強めなのでちょっと贅沢な食中酒として楽しめます。とはいっても、今回はハーゲンダッツの食中酒です。

f:id:PsyTouSan:20220316210455j:plainf:id:PsyTouSan:20220316210439j:plain
久保田 碧寿
感想

これまでの組み合わせの中では、群を抜いて美味しいです。麹香や淡麗によるさっぱり感などがないため、アイスとの組み合わせは非常に良いと思います。しかし、碧寿の場合は、そういった複雑さがなくてシンプルに味わうことができました。これまでの組み合わせだと、「月桂冠 純米パック」が一番近いと思われます。

一ノ蔵 円融

こちらも、碧寿同様に山廃仕込みのお酒となります。碧寿とどのくらい違いが出るのか楽しみです。これも初めて頂くお酒なので、まずは一杯。
思ったよりも酸味が強かったです。酸味が強いということはつまり芳醇である証なので、アイスに会う可能性が高いということです。

f:id:PsyTouSan:20220316210436j:plainf:id:PsyTouSan:20220316210434j:plain
一ノ蔵 円融
感想

これも非常に美味です。もしかすると、碧寿超えの可能性もあるくらい相性がいいです。お酒単体では、酸味が強いので、味の濃い料理との相性が良いと思います。ですが、酸味の強いお酒に、バニラと合わせて頂いてみると、お酒の酸味が良い感じに緩和されるので、癖のない味わいになって飲みやすくなります。

結論(普通に美味しい)

アイスとの組み合わせは全然ありだと思います。しかし個人的におすすめしたいのは、久保田 碧寿一ノ蔵 円融です。ただ、この2種類のみならず、山廃仕込みのお酒であれば美味しく頂けると思います。もちろん、山廃仕込みのお酒は、この記事で紹介した2種類だけではないので、今後も機会があれば記事にしてまとめてみたいと思います。

卒業旅行に広島へ…(獺祭本社、厳島神社、しまなみ海道etc...)

高専の卒業旅行として広島へ行ってまいりました。その内容を記事にざっくりとまとめます。
旅行は3泊4日で、広島県を中心に観光しました。

高崎駅から羽田空港

群馬県高崎駅から羽田空港第2ターミナル駅までは、9時29分発の上野東京ラインに乗って、赤羽駅まで到着した後、京浜東北線で在来線やモノレールをうまいこと乗り継いで到着しました。ただ、羽田空港からは飛行機となります。

f:id:PsyTouSan:20220310173757j:plainf:id:PsyTouSan:20220310173755j:plain
赤羽駅羽田空港第2ターミナル

私が小さい頃に乗ったことがあるみたいですが、そんな小さい頃の記憶など残っていないので、実質飛行機となります。
羽田空港内は、めちゃめちゃ広かったです。あと、高専に通っていた私からしたら、道行く女子は全員可愛いと思いました。

搭乗手続き(シンプルに不安)

自分で飛行機に乗ったことがない人間なので、搭乗手続きに手こずるか多少心配でしたが、直感でうまくいきました。航空券はネット予約で済ませたので、あらかじめ取得してあった予約番号を、券売機みたいなやつに入力すると、QRコードが描かれた搭乗券がウィーンって出てきます。

保安検査場(面倒)

保安検査場を見た途端に、面倒な雰囲気が漂っているのが感じ取れました。金属探知機ゲートやら、X線付きコンベヤーやら、モニターを眺める監視員の姿など、テレビや動画などのメディアでもよく見る光景です。財布、イヤホン、鍵やパソコン、さらにはベルトといった、金属含んでいる物を全部かごに入れる必要があるので割と面倒でした。しかし、面倒でも仕方ありません。機内に爆弾でも持ち込まれたり、機内で燃料を撒かれたり、銃器を見せてハイジャックされたら、たまったもんじゃありません。旅行が楽しくなくなるどころか、生きて帰れなくなります
ちなみに、金属探知機は3往復もしました。割とうるさめの音量でピッピピッピ鳴ります。

離陸(楽しい)

滑走路に入ってしばらくすると、エンジンがウォーミングアップし始めます。そして、前進を開始して数秒後に物凄い加速をして、十分な速度に到達すると離陸します。また、離陸後しばらくの間は機内が多少上下しますが、これもスリルがあって楽しいです。
飛行機は地上から10km以上の高度を、時速600km/hくらいの速度で飛び続けます。

着陸

羽田空港から広島空港までは、大体1時間15分で到着します。割とすぐに到着しました。関東地方から、1時間ちょっとで中国地方へ飛んでいくので、ちょっと不思議な感覚ですね。宿泊予定のホテルは広島駅近くにあるので、広島駅まではバスで移動します。バスは、広島空港と広島駅の往復券を購入しました。2700円くらいだった気がします。往復券は、一週間以内に使わないと、期限切れになってしまいます。

一日目

到着した時間が午後4時半前くらいだったので、あまり大きな移動をすることができません。なので、近場で観光できる場所に行きたいと思います。

広島駅(カープファンが大勢)

駅に到着する也、試合観戦が終わった大勢のカープファンがぞろぞろと駅構内に入ってまいりました。皆、すがすがしい顔をしていたし、ほぼ同じタイミングで「広島カープ サヨナラ勝ち」みたいなニュースが流れてきたので、凱旋ですね!
さすが広島だなと言った感じです。また、駅構内はかなり広く、商業施設も充実しており土産物を買うにも都合が良かったです。

広島城

最近まで存在そのものを知りませんでしたが、駅からだと歩いて20分位に位置しています。あまり大きな移動ができないので、友人と2人でとりあえず行ってみることに。

f:id:PsyTouSan:20220311084123j:plainf:id:PsyTouSan:20220311084125j:plain
広島城

近くで見るとこんな感じです。

f:id:PsyTouSan:20220311084119j:plain
広島城天守閣跡

敷地がかなり広かったです。敷地内には、建物が存在したであろう跡がちらほら見えました。「歴史的建造物なのに、なんで取り壊したんだろう」と思いましたが、ここは原爆ドームが位置する広島県です。天守となっているのも頷けます。

さらに、近くに池田勇人氏の像が建っていました。

f:id:PsyTouSan:20220313202928j:plainf:id:PsyTouSan:20220313202929j:plain
内閣総理大臣 池田勇人氏の像

頭にカラスが乗っているのはたまたまです。

一日目はこれで終了です。

続きを読む

【Android Studio】メモアプリの作成 Firebaseを用いてログイン機能を実装

メール・パスワードによるアカウント認証機能を持つメモアプリを作ります。また、作成しメモはクラウドに保存されるため、他の端末からログインすれば同じメモを見ることができますし、編集もできます。メモアプリにわざわざ認証機能を追加する必要があるのかと考えればそれまでですが、勉強の一環として実践しようと思います。

デモンストレーション

こんな感じのやつを作ります。

f:id:PsyTouSan:20220228204345g:plainf:id:PsyTouSan:20220228204134g:plain
左:ログインとログアウト 右:メモの追加と削除

こんな感じのメモアプリを作成します。ログイン、ログアウトの他にも、アカウント作成やメモの作成といった、基本的な機能はすべて備えたメモアプリの作成です。

Firebaseについて

Firebaseを利用する際は、プロジェクトのセットアップを行い、ここで様々な手順を踏んでようやく利用できるようになります。ただ、この作業自体は、セットアップ時に親切な指示を色々としてくれるので、意外と簡単です。
とりあえず、「firebase」と検索をかけて、トップに出てきたGoogleのサイトにアクセスすると、以下の画像のような画面になると思います。この画面で、「使ってみる」をクリックすると、プロジェクトのセットアップや、プロジェクトの管理を行うFirebaseコンソールに遷移します。

f:id:PsyTouSan:20220222234746p:plain
ここの「使ってみる」をクリック

これがFirebaseコンソールです。ここのプロジェクト作成をクリック。

f:id:PsyTouSan:20220223000830j:plain
Firebaseコンソール

あとは、指示に従ってプロジェクトの作成を完了してください。

Firebase Authentication

今回は、メールアドレスとパスワードによる認証方法を使用するので、sign-in-methodタブから、メール/パスワードをクリックします。

f:id:PsyTouSan:20220223085748p:plain
メール/パスワードをクリック

クリックして、メール/パスワードを有効にして保存してください。

f:id:PsyTouSan:20220223090024p:plain
有効にしたら、保存をクリック

Firebase Authenticationの設定はこれで完了です。続いて、Realtime Databaseの設定をしていきます。

Firebase Realtime Database

Realtime Databaseの画面に入ると、最初には以下の画面が表示されているのでデータベースを作成をクリックします。

f:id:PsyTouSan:20220225124913p:plain:w450
「データベースを作成」をクリック

すると、データベースのロケーションを設定します。ここでは米国(us-central1)を選択します。

f:id:PsyTouSan:20220223092624p:plain:w500
ロケーションの指定

次に、セキュリティルールの設定を行います。とりあえずロックモードでいいです。

f:id:PsyTouSan:20220223093103p:plain:w500
ロックモードで開始

完了したら、右下の「有効にする」をクリックしてデータベースの構築を完了してください。詳しいことは、この記事では述べませんので、これ以上に詳しい手法について知りたい場合は、ご自身で調べてみてください。この記事よりよっぽど分かりやすい記事がたくさんアップされています。
とりあえず、アプリ開発の方に進んでいきます。

アプリの仕様

最初に今回開発するアプリの大まかな仕様について決めていきたいと思います。

画面遷移はActivityResultLauncherを主軸とする

シンプルな画面遷移を行う場合、多くはstartActivityメソッドを使うケースが多いですが、個人的にはActivityResultLauncherの方が好きです。アクティビティ間(フラグメント間)で何かしらの値を渡したい場合、例えば、アクティビティAでユーザが入力した文字列を、アクティビティBへ渡したい場合に活用することができます。さらに、最近になってstartActivityForResult()メソッドが非推奨となった影響もあり、このアプリではActivityResultLauncherをメインで使うことにします。

アクティビティの役割

作成するアクティビティの役割をまとめます。それが以下の通りです。




Javaファイル名

XMLファイル名

役割

MemoListActivity

activity_memo_list

メモのリスト表示

CreateMemoActivity

activity_create_memo

メモの作成

LoginActivity

activity_login

既存アカウントにログイン

CreateAccountActivity

activity_create_account

新たなアカウントの作成

MemoData

無し

メモ一つが持つデータ型

CustomListAdapter

無し

独自のリストアダプタ

アクティビティとは言っていますが、MemoDataとCustomListAdapterはアクティビティではなく、ただのJavaファイルです。

アクションバーの非表示

今回は、アクションバーを非表示にしたいと思います。なので、themes.xmlファイルを開いて以下のように記述してください。

<resources xmlns:tools=...>
    <!-- Base application theme. -->

    //以下の文
    <style name="Theme.MemoApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">

        //省略

    </style>
</resources>

文の最後に「NoActionBar」と追記しているだけです。これで、アプリ画面上部に表示されるアクションバーが表示されなくなります。

レイアウトの作成

まずはレイアウトを作成します。右下のFloatingActionボタンを配置して、それをクリックすることで新たなメモを作成できるようにします。また、Stringデータは面倒だし分かりにくくなると思うので使いません。

activity_memo_list

メモのリスト表示を行うアクティビティのレイアウトファイル(activity_memo_list.xml)です。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MemoListActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/tool_bar"
            android:layout_width="match_parent"
            android:layout_height="?android:actionBarSize" />

    </com.google.android.material.appbar.AppBarLayout>

    <ListView
        android:id="@+id/memo_list"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/app_bar" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/add_new_memo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="15dp"
        android:layout_marginBottom="15dp"
        android:src="@drawable/add_24"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

特に難しい部分はありません。非表示にしたツールバーを、9行目から19行目の部分で記述しています。言い換えれば、xmlファイルでこの記述をしない場合はツールバーが表示されなくなります。

activity_create_memo

メモをリストに追加するアクティビティのレイアウトファイルです。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".CreateMemoActivity">

    <EditText
        android:id="@+id/memo_title"
        android:layout_width="0dp"
        android:layout_height="60dp"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="40dp"
        android:layout_marginRight="20dp"
        android:hint="メモのタイトルを入力"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/memo_contents"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:hint="コンテンツを入力"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/memo_title" />

    <Button
        android:id="@+id/add_new_memo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginRight="30dp"
        android:backgroundTint="#adadad"
        android:text="追加"
        android:textSize="18sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/memo_contents" />

</androidx.constraintlayout.widget.ConstraintLayout>

custom_memo_card.xml

メモリストに表示されるリストのカード一枚分のレイアウトです。これを応用すれば、自分の思うようなウィジェットを、リスト状に表示することができます。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingBottom="6dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:id="@+id/memo_date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Date"
            android:textSize="15sp"
            android:layout_marginLeft="5dp" />

        <TextView
            android:id="@+id/memo_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:text="Sample Title"
            android:textSize="27sp"
            android:maxLines="1"
            android:ellipsize="end"
            android:textAppearance="?android:attr/textAppearance"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/memo_contents"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="sample contents "
            android:textSize="18sp"
            android:maxLines="2"
            android:ellipsize="end"
            android:layout_marginLeft="5dp"
            android:layout_marginBottom="6dp"/>

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

今回の開発で初めて知った、少し便利な関数を紹介します

30行目(android:maxLines)

TextViewに表示できる最大行数を設定することができます。指定した行数を超える文字を表示しようとしても、指定した行数を超えることがなくなります。

31行目(android:ellipsize)

今回は"end"の属性を与えています。この場合は、行数を超える文章を表示させようとすると、最後に「...」が追加されます。ユーザ目線では、「この後も文章が続いているんだな」と思わせることができます。

activity_login.xml

ログインを行うためのアクティビティのレイアウトです。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".LoginActivity">

    <EditText
        android:id="@+id/email"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textEmailAddress"
        android:layout_marginTop="40dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:hint="メールアドレスを入力"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:hint="パスワードを入力"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/email" />

    <Button
        android:id="@+id/loginButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="ログイン"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/password" />

    <TextView
        android:id="@+id/createAccount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="※アカウントを新たに作成する"
        android:layout_marginTop="16dp"
        android:textSize="14sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.15"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/loginButton" />

</androidx.constraintlayout.widget.ConstraintLayout>

activity_create_account.xml

アカウント作成を行うためのアクティビティのレイアウトです。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".CreateAccountActivity">


    <EditText
        android:id="@+id/email"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textEmailAddress"
        android:layout_marginTop="40dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:hint="メールアドレスを入力"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:hint="パスワードを入力"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/email" />

    <EditText
        android:id="@+id/rePassword"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:hint="もう一度パスワードを入力"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/password" />

    <Button
        android:id="@+id/createAccount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="アカウントを作成する"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/rePassword" />


</androidx.constraintlayout.widget.ConstraintLayout>

inputType属性を与えることで、テキストボックスに様々な特徴を持たせることができます。例えば、"textPassword"を与えると、入力した文字が中黒で表示されるようになります。

Javaソースコード

続いて、Javaソースコードを紹介します。上記に示したアクティビティの順番通りに紹介します。また、Javaのコードは、できるだけコードが短くなるように基本的にはラムダ式で記述しています。

MemoListActivity.java

//パッケージの宣言部分なので省略
package...

//ただのインポート文の羅列なので省略
import...

public class MemoListActivity extends AppCompatActivity {

    private DatabaseReference reference;
    private FirebaseUser user;
    private String uid;

    //CustomListAdapterについては後述します
    private CustomListAdapter customListAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memo_list);

        setSupportActionBar(findViewById(R.id.tool_bar));
    }

    @Override
    protected void onResume() {
        super.onResume();

        //ログイン状態の確認
        user = FirebaseAuth.getInstance().getCurrentUser();
        if (user == null){

            //ログインしていない場合、ログイン画面へ遷移
            Intent intent = new Intent(MemoListActivity.this, LoginActivity.class);
            resultLauncher.launch(intent);
        } else {
            appSetup();
        }
    }

    private void appSetup(){
        Toolbar toolbar = findViewById(R.id.tool_bar);
        ListView memoListView = findViewById(R.id.memo_list);
        ArrayList<MemoData> memoListItem = new ArrayList<>();

        toolbar.setTitle("Memo List");

        user = FirebaseAuth.getInstance().getCurrentUser();
        assert user != null;
        uid = user.getUid();

        FirebaseDatabase database = FirebaseDatabase.getInstance();
        reference = database.getReference("users").child(uid);

        customListAdapter = new CustomListAdapter(this, R.layout.custom_memo_card, memoListItem);
        memoListView.setAdapter(customListAdapter);

        databaseListener();


        FloatingActionButton addMemo = findViewById(R.id.add_new_memo);

        addMemo.setOnClickListener(v -> {
            Intent intent = new Intent(MemoListActivity.this, CreateMemoActivity.class);
            resultLauncher.launch(intent);
        });

        memoListView.setOnItemLongClickListener((parent, view, position, id) -> {
            MemoData memoData =(MemoData) memoListView.getItemAtPosition(position);
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("メモの削除")
                    .setMessage("選択したメモを削除しますか?")
                    .setPositiveButton("削除",(dialog, which) -> {
                            reference.child("Memo").child(memoData.get_memoTitle()).removeValue((error, ref) -> {
                        databaseListener();
                        Toast.makeText(MemoListActivity.this, "メモを削除しました", Toast.LENGTH_SHORT).show();
                            });
                    })
                    .setNegativeButton("やめる",(dialog, which) -> {})
                    .show();
            return false;
        });
    }

    private void logout(){
        AuthUI.getInstance()
                .signOut(this)
                .addOnCompleteListener(task -> {
                    Intent intent = new Intent(MemoListActivity.this, LoginActivity.class);
                    Toast.makeText(this, "ログアウトしました", Toast.LENGTH_SHORT).show();
                    resultLauncher.launch(intent);
                });
    }

    //画面遷移を行うためのインターフェース
    ActivityResultLauncher<Intent> resultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
            result -> {
        if (result.getResultCode() == LoginActivity.RESULT_CODE_LOGIN){
            Toast.makeText(this, "ログインしました", Toast.LENGTH_SHORT).show();
        }
        if (result.getResultCode() == CreateMemoActivity.RESULT_CODE_CREATE_MEMO){
            Intent intent = result.getData();
            assert intent != null;
            String[] memo = intent.getStringArrayExtra("memo");
            MemoData memoData = new MemoData(uid, memo[0], memo[1], memo[2]);

            //Realtime Databaseにデータを保存する
            reference.child("Memo").child(memo[1]).setValue(memoData).addOnCompleteListener(task -> {
                if (task.isSuccessful()){
                    databaseListener();
                    Toast.makeText(MemoListActivity.this,"メモを追加しました",Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(MemoListActivity.this,"メモの追加に失敗しました",Toast.LENGTH_SHORT).show();
                }
            });
        }
    });

    //ツールバーにオプションメニューを追加する
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.memo_list_tool_bar, menu);
        return true;
    }

    //ツールバーのオプションをクリックしたときの処理
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
            if (item.getItemId() == R.id.logout){
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("ログアウト確認")
                        .setMessage("ログアウトしますか?")
                        .setPositiveButton("はい", (dialog, which) -> {
                            logout();
                        })
                        .setNegativeButton("やめる", (dialog, which) -> {

                        })
                        .show();
            }
        return super.onOptionsItemSelected(item);
    }

    //データベースの更新を検出する
    private void databaseListener(){
        customListAdapter.clear();
        user = FirebaseAuth.getInstance().getCurrentUser();
        assert user != null;
        uid = user.getUid();

        FirebaseDatabase database = FirebaseDatabase.getInstance();
        reference = database.getReference("users").child(uid);
        reference.child("Memo").addChildEventListener(new ChildEventListener() {
            //データベースに追加された要素があれば実行される
            @Override
            public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
                MemoData memoData = snapshot.getValue(MemoData.class);
                customListAdapter.add(memoData);
                customListAdapter.notifyDataSetChanged();
            }

            @Override
            public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {

            }

            //データベースに削除された要素があれば実行される
            @Override
            public void onChildRemoved(@NonNull DataSnapshot snapshot) {
                MemoData memoData = snapshot.getValue(MemoData.class);
                customListAdapter.add(memoData);
                customListAdapter.notifyDataSetChanged();
            }

            @Override
            public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {

            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {

            }
        });
    }
}

メインアクティビティ的な立ち位置なので、かなり長くなってしまいました。部分的にコメントを書きましたが、一応少しだけ説明を加えます。

21行目(setSupportActionBar)

activity_memo_list.xmlファイルで追加したツールバーに、オプションメニューを表示させるためのメソッドです。

54行目(customListAdapter)

後述するクラスをインスタンス化しています。このクラスは、リスト表示をになるアダプタを継承したクラスです。そのクラスを用いることでアダプタをカスタマイズしています。

107行目(setValue(object))

Realtime databaseに値をセットします。Realtime databaseを通じて、アカウントごとにメモを保存します。すなわち、ログアウトをしたとしてもメモの内容が消えることはなく、ログインすることで再びメモを表示することができます。また、アカウントごとに保存なので、機種変更やアプリの再インストールなどでデータが消えることもなく、アカウントにログインさえすれば同じ内容のメモを表示することができます。ちなみに、Firebaseコンソールでは以下の画像のように表示されています。

f:id:PsyTouSan:20220227093013j:plain
Firebaseコンソール側の画面
121行目(inflater.inflate)

ツールバーのオプションメニューに表示する内容をメニューファイルを渡しています。このメニューファイルは次の通りです。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/logout"
        android:icon="@drawable/logout_24"
        android:title="Logout"
        app:showAsAction="ifRoom"/>
</menu>

メニューファイルの作成は、「Project」ファイル内の「res」⇒「menu」を右クリックして、「New」⇒「Menu Resource File」から作成できます。
このファイルの5行目のdrawableは、vectorファイルです。vectorファイルの作成は、Android Studioの画面の左上の「File」から「New」⇒「Vector Asset」から作成できます。また、showAsAction属性には"ifRoom"としています。これは、ツールバーにアイコンを表示する余裕がある場合は、そのアイコンを表示し、できない場合はオーバーフローメニューの中に格納するように表示するための属性です。

CreateMemoActivity.java

新たなメモを作成するアクティビティです。MemoListActivityの右下に表示されたボタンから遷移します。

package...

import...

public class CreateMemoActivity extends AppCompatActivity {

    public static final int RESULT_CODE_CREATE_MEMO = 3000;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_create_memo);

        EditText memoTitle = findViewById(R.id.memo_title);
        EditText memoContents = findViewById(R.id.memo_contents);
        Button addButton = findViewById(R.id.add_new_memo);

        addButton.setOnClickListener(v -> {
            //メモタイトルに何も入力されていない場合は、必須項目であることをユーザに知らせる。
            if (!memoTitle.getText().toString().isEmpty()){

                //コンテンツに何も入力されていない場合は「詳細なし」で登録する。
                if (memoContents.getText().toString().isEmpty()) {
                    memoContents.setText("詳細なし");
                }
                String[] memo = {getCurrentDate(), memoTitle.getText().toString(), memoContents.getText().toString()};
                Intent intent = getIntent();

                intent.putExtra("memo", memo);
                setResult(RESULT_CODE_CREATE_MEMO, intent);
                finish();

            } else {
                memoTitle.setError("必須入力の項目です");
            }
        });
    }

    //現在日時を取得するメソッド
    private String getCurrentDate(){
        @SuppressLint
                ("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm");
        Date d = new Date();
        return dateFormat.format(d);
    }
}

こんな感じです。メモの追加は、Addボタンをクリックしたときに追加され、クリック時にタイトルが未記入の場合はメモを追加できないようになっています。ただし、メモの内容が未記入の場合は「詳細なし」として格納されます。またメモの追加時に、その時点での日付を取得し、メモの作成日時としています。

LoginActivity.java

既存のアカウントにログインするためのアクティビティです。また、アカウント作成をするための画面にも遷移することができます。

package...

import...

public class LoginActivity extends AppCompatActivity {

    public static final int RESULT_CODE_LOGIN = 1000;
    private FirebaseAuth mAuth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        activitySetup();
    }

    private void activitySetup(){
        EditText email = findViewById(R.id.email);
        EditText passWord = findViewById(R.id.password);
        Button login = findViewById(R.id.loginButton);
        TextView createAccount = findViewById(R.id.createAccount);

        login.setOnClickListener(v -> {

            //入力欄が空欄でないか確認
            if (!email.toString().isEmpty()) {

                //入力欄が空欄でないか確認
                if (!passWord.toString().isEmpty()) {
                    //ログイン処理の開始
                    mAuth = FirebaseAuth.getInstance();
                    mAuth.signInWithEmailAndPassword(email.getText().toString(), passWord.getText().toString())
                            .addOnCompleteListener(this, task -> {
                                if (task.isSuccessful()) {
                                    Intent intent = getIntent();
                                    setResult(RESULT_CODE_LOGIN, intent);
                                    finish();
                                }else {
                                    Toast.makeText(LoginActivity.this, "ログインに失敗しました", Toast.LENGTH_SHORT).show();
                                }
                            });

                }else {
                    passWord.setError("必須項目です");
                }
            }else {
                email.setError("必須項目です");
            }
        });

        createAccount.setOnClickListener(v -> {
            Intent intent = new Intent(LoginActivity.this, CreateAccountActivity.class);
            resultLauncher.launch(intent);
        });
    }

    ActivityResultLauncher<Intent> resultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
            result -> {
        if (result.getResultCode() == CreateAccountActivity.RESULT_CODE_CREATE_ACCOUNT){
            Intent intent = new Intent(LoginActivity.this, MemoListActivity.class);
            setResult(RESULT_CODE_LOGIN, intent);
            finish();
        }
            });
}
33行目(signInWithEmailAndPassword())

ログイン処理の開始は、33行目のsignInWithEmailAndPasswordメソッドによって行われています。このメソッドの第一引数にメールアドレス第二引数にアカウントのパスワードを渡すことで、ログイン処理が実行されます。ログイン処理が完了した場合は、addOnCompleteListener内の処理が実行されます。そして、task.isSuccessfulメソッドによって、完了したログイン処理が成功したか失敗したかをboolで返してくれます。

CreateAccountActivity

既存のアカウントがない場合は、新たにアカウントを作成する必要があります。ログイン画面から遷移することができます。

package...

import...

public class CreateAccountActivity extends AppCompatActivity {

    public static final int RESULT_CODE_CREATE_ACCOUNT = 2000;

    FirebaseAuth mAuth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_create_account);

        activitySetup();
    }

    private void activitySetup(){
        EditText email = findViewById(R.id.email);
        EditText password = findViewById(R.id.password);
        EditText rePassword = findViewById(R.id.rePassword);
        Button create = findViewById(R.id.createAccount);

        mAuth = FirebaseAuth.getInstance();

        create.setOnClickListener(v -> {
            if (password.getText().toString().equals(rePassword.getText().toString())){

                //ここからアカウント作成の処理が開始される
                mAuth.createUserWithEmailAndPassword(email.getText().toString(), password.getText().toString())
                        .addOnCompleteListener(task -> {
                            if (task.isSuccessful()){
                                Toast.makeText(CreateAccountActivity.this, "アカウントを作成しました!",Toast.LENGTH_LONG).show();
                                setResult(RESULT_CODE_CREATE_ACCOUNT, getIntent());
                                finish();
                            } else {
                                Toast.makeText(CreateAccountActivity.this, "アカウント作成に失敗しました",Toast.LENGTH_LONG).show();
                            }
                        });
            }
        });

    }
}

ログインの場合と似ていますが、これでアカウントが作成することができます。

MemoData

これまでに紹介したソースコードの中にも、ちょこちょこ顔を出していたクラスです。一つのメモに格納するデータを意味しています。

package...

public class MemoData {

    public String _uid,_memoTitle,_memoContents,_date;

    public MemoData(String uid, String date, String memoTitle, String memoContents){
        this._uid = uid;
        this._date = date;
        this._memoTitle = memoTitle;
        this._memoContents = memoContents;
    }

    public MemoData(){}

    public String get_uid() {
        return _uid;
    }

    public String get_date() {
        return _date;
    }

    public String get_memoTitle() {
        return _memoTitle;
    }

    public String get_memoContents() {
        return _memoContents;
    }
    
}

見ての通り、ただのアクセサーメソッドの集まりです。コンストラクタの記述は忘れないようにしてください。
一つのメモに格納させるデータは、ユーザID、日付、メモタイトル、メモの内容の4つです。

CustomListAdapter

ようやく最後です。ArrayAdapterを継承したクラスを作成します。このクラスもこれまでのコード内に何回も顔を出しています。

package...

import...

public class CustomListAdapter extends ArrayAdapter<MemoData> {

    private final int Resource;
    private final List<MemoData>  _MemoData;

    public CustomListAdapter(@NonNull Context context, int resource, List<MemoData> memoData) {
        super(context, resource, memoData);
        Resource = resource;
        _MemoData = memoData;
    }

    @Override
    public int getCount() {
        return super.getCount();
    }

    @Nullable
    @Override
    public MemoData getItem(int position) {
        return super.getItem(position);
    }

    //リストカードに日付、メモタイトル、メモ内容を表示させる
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {

        final ViewHolder holder = new ViewHolder();
        View v;
        if (convertView != null){
            v = convertView;
        } else{
            v = LayoutInflater.from(getContext()).inflate(Resource, null);
            holder.memoDate = v.findViewById(R.id.memo_date);
            holder.memoTitle = v.findViewById(R.id.memo_title);
            holder.memoContents = v.findViewById(R.id.memo_contents);
        }

        MemoData data = _MemoData.get(position);

        holder.memoDate = v.findViewById(R.id.memo_date);
        holder.memoDate.setText(data.get_date());

        holder.memoTitle = v.findViewById(R.id.memo_title);
        holder.memoTitle.setText(data.get_memoTitle());

        holder.memoContents = v.findViewById(R.id.memo_contents);
        holder.memoContents.setText(data.get_memoContents());

        return v;
    }

    static class ViewHolder{
        TextView memoDate,memoTitle,memoContents;
    }
}

ArrayAdapterを継承することで、独自のリストアダプタを形成することができます。10行目のクラスは、ArrayAdapterを継承している場合は必ず記述しなければなりません。

まとめ

以上で終わりです。長めの記事になってしまいました。ただ、Firebaseを活用すれば、簡単に高クオリティのアプリ開発が可能になりますから、今後も勉強していきたいと思います。今回は、ユーザ認証(Authentication)と、クラウドデータベース(Realtime Database)を利用しましたが、Firebaseはこれ以外にもかなり多くのサービスを展開しているので、それぞれ何ができるのか気になります。今後のアプリ開発次第ではお世話になるかもしれません。

【日記】ついに成人、酒を嗜み、課題を済ます

記事の投稿が久しぶりとなってしまいました。
私も遂に成人式を迎え、中学の頃の友人と久々に会ってきました。なので成人の記念としてここに一筆刻んでおきます。

いつかは訪れるであろうと思っていた成人の儀式が、まさか本当に現実になるとは思ってもいませんでした。

まず 「誰だ?」と思う

中学を卒業後、殆ど顔を見ることなく5年の月日が経過した訳ですが、それにしても変わりすぎて分からない人もいました。
久しぶりに再会した友人の多くは、顔を見れば誰だか分かりますが、一部の人は「久しぶり!」と話しかけられても、「ん? 誰だ?」となってしまう人がいました。例えば、身長が見違えるほど伸びていたり、痩せていたり、逆に太っていたりと。さらに、髪の毛が伸びすぎて誰だかわからない人もいました。

女子はさらに分からない

しかし、それは男子の話。女子は、マスクをしている上に、可憐な着物姿なので余計に誰だか分かりません。なんとなく面影が残っていたり、中学の頃によく会話をしていた人であれば、誰だか見分けはつきます。ただ、クラスが違って少ししか交流が無かったり、あるいは5年のブランクでガラッと印象が変わっていると、本当に誰だか分かりません
そしてみんな大人っぽくなった印象でした。中学の頃からほとんど会うことはなかったので(高専という組織では尚更)、一気に垢抜けて社会人になったように感じます。

スーツが似合う者

普段はお目にかかれない友人のスーツ姿を見ると、どこか既視感を感じる人もいます。玄関を叩く借金の取り立て人、NHKの集金人、漫才師等々、その人の個性が滲み出ていて面白いですね。

タバコを吸う人が意外といた

大人数で参加する同窓会に出席しましたが、その会場には喫煙所が設けられており、大広間では禁煙なのでタバコを吸う人は喫煙所へ行きます。そこには結構多くの人が足を運んでいました。ガラス張りなので中の様子が伺えるのですが、そこへ目を向けると「お前も吸ってるのか!」と驚きます。最近では、紙製のタバコよりも電子タバコが流行っているみたいですね。

学校という現実

時間は無限ではないので、楽しい時間もいつかは終わりを迎えます。そして、成人の日を挟んで火曜日から学校(就職している人は会社)ですが、これがすごく。学校に通っている最中も、成人式当日の出来事について浸っていました。多分、人生で一番インスタグラム、ツイッターの閲覧頻度が高いかもしれません。心なしか、投稿の「いいね」が付く速度も速いような気がします。
みんな元気にしているか、今は何しているのか、どんな写真を撮ったのか、めちゃくちゃ気になります。

もしかしたら二度と会えないかも

やっぱりこれですね。二十歳を過ぎると、こうして皆に会うことができるような通過儀礼はないので、積極的に会わない限り顔を合わせることはないでしょう。なので、余計にまた会いたいと感じます。ただ死ぬわけではないので、連絡をして都合が合えば再会はできますから、あまり神経質になる必要はないと思います。というか、そう考えないと現実に戻れません。

酒を嗜む

私はすでに二十歳の誕生日を迎えているので普段からお酒は飲んでいます。成人式当日も車を運転せずに、同窓会とその後の二次会でお酒と会話を楽しみました。

日本酒は苦手な人が多い

私は日本酒をよく飲んでいます。おすすめの日本酒を紹介している記事も書いているので、ついでにご覧ください。

psytousan.hatenablog.com

居酒屋でも冷の日本酒を頂きました。スーパーでもよく見かける「白鶴」です。ただ、白鶴のどの種類の日本酒かは分かりませんでした。

f:id:PsyTouSan:20220111213523j:plain:w620
居酒屋さんで飲んだ白鶴

友人にも飲んでみるかと誘い、実際に飲んでもらいました。しかし、アルコール度数が他よりも高いからなのか、あまり受けが良い飲み物ではなさそうでした。

人の酔う姿

お酒をよく飲むとはいっても、人がお酒を飲んで酔う姿を見るのは初めてでした。顔を赤くする人、声が大きくなる人、普段とは性格が変わる人、お酒を飲んでもあまり変わらない人、など様々でした。ただ、お酒を飲むことで場の雰囲気がかなり盛り上がります。

課題を終わらせる

成人式であろうと、課題は襲ってきます。なんでこんな時期に提出期限を設けたんだと怒りが湧いてきそうですが、見過ごしてしまえば落単してしまう可能性も十分あるのでどうにかするしかありません。

12枚のA4レポートを仕上げる(クソ)

成人の日の翌日が提出期限のデザイン実験レポートを仕上げました。「デザイン実験って何だ?」という方が殆どだと思いますので簡単に説明すると、最大四人でチームを組んで、半年かけて何か物を作るというもの。デザインというワードがついているものの、デザインという要素はあまり感じられません。ただ、これが結構大変です。おまけに、成人式の思いにふけたせいで、全然捗りませんでした。
半年かけて作ったものを原理から、方法、結果まで記述する必要があるからです。成人式翌日の夜10時頃にようやく完成しました。
ちなみに私が製作したものは、加速度センサと可変抵抗を利用したモーションキャプチャ的なやつです。一見凄そうに見えるかもしれませんが、腕一本分を作るのがやっとでした…。
一応、その副産物の記事も載せておきます。

psytousan.hatenablog.com

感想(成人式は毎年やるべき)

非常に楽しかったです。多分、新成人の多くはそう感じたと思います。
翌日も、他地域の学校の友人(bunnoichi’s diary)と成人式について話しましたが、やはり「楽しかった」と感じたそうです。

日常に戻る

式を終えれば日常に戻ります。学生は学校へ、就職している人は会社へ向かうと思います。お酒を飲んで酔っていた・はしゃいでいた人間が、これから社会人として働く人なのか(もちろん自分も)と考えると少し感慨深いですね。式から二日経つ今日も、インスタグラムの投稿には成人式関連の画像が散見されます。
兎にも角にも、この思い出は今後忘れることはないでしょう。

最後に

私は二次会まで参加しました。帰宅は、夜中の1時を過ぎていました。しかし、そんな最後の最後に嬉しい出来事が?!
ポストに投函されていた郵便物をチェックすると、その中には…

f:id:PsyTouSan:20220111224200j:plain:w400

久しぶりに遊びたいと思いました。

二十歳になって間もない人間が薦める日本酒

この記事は群馬高専Advent Calendar 21日目の記事です。

こんにちは。
二十歳になってあまり時間が経っていませんが、日本酒をよく飲むようになりました。なので、今回はその日本酒について書こうと思います。また、日本酒についてよく知らない人や、普段日本酒を日常酒として飲んでいない人でも楽しめるような記事にしていきたいと思います。

日本酒の楽しみ方

日本酒は小難しい印象を抱かれてしまいがちですが、正しい知識を備えて楽しく飲むことができれば魅力的なお酒だと思います。なので、個人的に楽しいと思う飲み方を紹介したいと思います。

温度の変化を楽しむ

日本酒は、温度が高くてもおいしく飲める酒です。といいますね。ただ、燗で飲むことが推奨される日本酒であれば、燗で楽しむことができます。ただ、一般的には冷やして飲むほうが多いと思われます。というのも、やっぱり冷やして飲んだ方が飲みやすく、爽快感もありますから、忙しい現代社においては、ふくよかでゆっくりと味わえるよりも、爽快でキレの良いを好む人が多いです。そして、なにより冷の方が面倒じゃなくていいですね。燗は湯煎するなり、レンジで温めたりと、ひと手間かかります。冷であれば、冷蔵庫に放置しておけば直ぐに飲めるというメリットもありますね。私も、日本酒は冷で飲むことがありますが、燗にするときと比べると、準備する手間が少ないので楽でいいですね。

燗にすると、香りが立つ

温度が高いと、揮発しやすいのでそれだけ鼻に抜ける香りが強くなります(故に、燗酒は飲みにくいと言われるのかも)。さらに、冷の状態では見えてこなかった味わいが顔を出し始めるので、「ふくよかになる」とも表現されます。また、日本酒に含まれるアミノ酸も強く引き出されます。なので、もともと香りの強い吟醸酒吟醸大吟醸純米吟醸大吟醸あたり)は、燗には向かないお酒とされています。ただ、中にはこれら吟醸酒でも、燗で飲むことをコンセプトに醸造されたお酒もありますね。たとえば、朝日酒造の久保田の碧寿なんかは、それに相当します。ただ私は飲んだことないので、いずれ試してみたいですね。ただ、ほかのお酒と比べると少々値が張るのがネックですね。
www.asahi-shuzo.co.jp

面倒が故に、楽しめる

日本酒を燗にする場合、レンジで温めたり、徳利に入れて湯煎するなどの方法がありますが、温度計でしっかりと温度を管理して酒を温めるのは面倒に感じる人が多いかと思います。しかし、その面倒な思いをした後に、肴をつまんで飲む燗酒は普段飲むお酒とは格別な美味しさがあります。まず、燗酒は冷酒と比べると飲みにくいというのは前述したとおりですが、逆を返せば落ち着いて飲むことができるということになります。なので、料理を味わいつつ、口直しに燗酒を口に含めば、たちまち日本酒特有の香り、旨味や甘味が広がりますから、これが楽しいですね。しかも、温めた物体はいずれ冷めます。なので、45度程度に温めたお酒も飲んでいるうちに40度、35度と低下していきますから、複数の温度域で酒を楽しむことができるのも、燗酒ならではだと思います。

大体の温度

燗と一言にいっても温度にはいくつか種類があります。日向燗(30度)、人肌燗(35度)、ぬる燗(40度)、上燗(45度)、熱燗(50度)、飛び切り燗(55度)といった感じです。ただ、一般的にはぬる燗から上燗あたりで頂くパターンが多いような気がします。先ほど紹介した久保田の碧寿も、ぬる燗で飲むことを推奨しているようです。

料理を用意する

日本酒は料理と一緒に頂くことで、一層おいしく感じられると思います。日本酒そのものがアルコール度数が高いお酒なので、そこへタンパク質が豊富な魚類(刺身、缶詰や焼き魚など)とか豆腐と一緒に頂けば、お酒と料理を美味しく感じるだけでなく、口直しとしても役目を果たしてくれるでしょう。

個人的におすすめの日本酒

ここからは、個人的におすすめしたい日本酒を紹介します。

八海山

日本酒の中では、かなり有名な銘柄です。やはり、有名というだけあって、非常に美味です。
冷の状態で飲んだ時と、燗の状態で飲んだ時では、まるで別人に出会ったかのような味わいを見せてくれます。

f:id:PsyTouSan:20211211111245j:plain
八海山 普通酒

八海山には、いくつか種類がありますが写真のやつは、吟醸とか純米などの分類から外れている普通酒です。

味の特徴

冷で飲んだ時は、さっぱりしていてわずかに甘味を秘めているような味わいですが、これが燗にした瞬間全くの別物に仕上がります。一気に甘味が増して華やかになります。とりあえず飲んでほしい日本酒の一つです。

上善如水のひやおろし

これは、「ひやおろし」と呼ばれるタイプの日本酒で、春に製造し、夏を涼しい蔵で過ごして熟成させ、気温が下がってきた秋ごろに出荷されるものです。ひやおろし、というのは「冷(常温)」の状態で小売店等に卸されることから、そう呼ばれています。なので、秋限定にはなってしまいますが、紹介しておきます。

f:id:PsyTouSan:20211211111801j:plain
上善如水のひやおろし

ボトルも秋らしいデザインで、銘柄のフォントもモダンでおしゃれですね。手前に芋ようかんを配置した私のセンスも素晴らしい!

味の特徴

夏を蔵で過ごし、熟成された日本酒は、落ち着いた雰囲気があり、主張が激しくないです。それだけでなくわずかな甘味を秘めています。この甘味を顕著に味わいたいのであれば、ぜひ燗で頂くことをお勧めしたいです。最初に、冷やして飲んで、ある程度楽しんだら、上燗(45度)くらいに温めて少しずつ飲んでみてください。45度というのは、熱燗(50度)より低いとはいえ、燗酒の中では高めの温度です。しかし、放置していれば温度が下がっていきますので、その温度の変化を楽しむことができ、個人的に好きな飲み方です。すると、これまで静かであった甘味が、一気に主張してきます。常温や、ロックでは味わえないほどの甘味が体験できるので、ぜひ試してみてください。

菊正宗酒造 樽酒

樽酒というのは、完成した日本酒を杉材で造られた樽の中に数日間入れておき杉の香りを日本酒に移したものです。菊正宗酒造は、この樽酒のシェアはトップで、とくに有名なのが以下の写真に示したやつです。

f:id:PsyTouSan:20211221090031j:plain
樽酒と鰯蒲焼

これは、鰯の蒲焼の缶詰を温めたやつです。

味の特徴

非常にキレが良いですね。口に入れて飲み込んですぐに余韻が消えていきます。そして、樽酒なので木の香りがします。本当に酒から木の香りなんかするのかと疑問に思う人もいるかもしれません。しかし、本当に木の匂いですね。ホームセンターの資材売り場に売っているような木材の香りと同じような香りです。その香りも相まって、口直しだけでなく、鼻直し(?)もできる上にキレが非常に良いすっきりしたお酒なので、味の濃い料理とも合うかもしれません。実際、蒲焼の濃いたれの味に一切負けることはなく、最高のキレと杉を連想させる木香を広げてくれるでしょう。また、その香りのせいなのか、アルコールの香りを強く感じにくいのも特徴の一つです。

おすすめの地酒

ここまでは、ある程度有名な日本酒の紹介でしたが、ここからは私が住んでいる群馬の地酒を紹介します。少しマイナーになってしまいますが、参考程度までに。

谷川岳 とび辛(+15)

この+15というのは、日本酒度のことです。日本酒度というのは、4度の水と日本酒の比重によって決まる指標であり、甘味の要因である糖分が多い場合は比重が重くなり、すくなければ軽くなります。そして、比重が4度の水と同じ場合は日本酒度は"0"となります。そして、比重が重いときはマイナスに、軽いときはプラスになります。すなわち、日本酒度+15というのは、糖分が少ない辛口の日本酒ということになりますが、この+15というのは非常に高い数値になります。わざわざ+15という値をでかでかとラベルに書く程のことはありますね。つまり、その辛口がウリの商品ということです。

味の特徴

そして、実際に飲んだ感覚としては、思った通りの辛口です。また、ロックと燗の二種類で飲んでみましたが、ロックで飲んだ時の印象としては、めちゃくちゃさっぱりしていてもはや水を飲んでいるかのような感覚でした。キレが良すぎるというか、そんな感じです。味の濃い料理とも相性はいいと思います。燗にしても他の酒のように豊かさが顕著に表れるということはなく、頑なにキレの良さを保っています。しかし、キレが良いとはいっても旨味要素が強くロックで飲んだ時よりも、少し余韻が残ります。

水芭蕉

使用している酒米は、広島県産の山田錦だそうです。ただ、値段が少し高いですね。一般的なお値段だと、720mlボトルで1400円~2000円程度してしまうので、簡単には手は出せないと思います。ただ、贈り物として送ったり、休日の前日に飲んだりするのには最高だと思います。今回飲んだ奴は水芭蕉吟醸酒です。純米吟醸純米大吟醸の方は高くて手が出せませんでした。

f:id:PsyTouSan:20211219224722j:plain
水芭蕉と味噌サバ缶の肴
味の特徴

めちゃくちゃ美味しかったです。最初飲んだ時の感覚としては穏やかであり、余韻も少し残ります。そして、果実感の強い甘みがあります。香りのほうはというと、私が飲んだ時は冷やして(10度くらい)で頂きましたが、他の冷酒と比較しても強めに香りを感じることができます。総じて飲みやすく、アルコール度数が15%ではありますが、日本酒を始めて飲む方でもおすすめできるお酒です。

妙義山

妙義山とは、群馬を代表する山の一つで荒々しい岩肌を露出した山です。以下に示したのが特別本醸造妙義山です。この時は、おつまみとして鰆の刺身を頂きました。

f:id:PsyTouSan:20211219225514j:plain
妙義山と鰆の刺身
味の特徴

水芭蕉と比較すると、少し癖が強いかもしれません。初めて飲んだ時の印象として感じたのは、荒々しい見た目をした妙義山を模したような力強い味わいといった感じです。甘味はほとんど感じることはなく、対して旨味を強く感じることができます。冷で飲んだ際には、多少の旨味とキレ感じることができますが、燗にするとより一層その旨味が強化されます。注目すべきは、甘くないという事ですね。水芭蕉や越後桜のように、甘みはないので刺身相手でも、その味を壊すことなく引き立てるような気がしました。

まとめ

ここで紹介したお酒はどれもおいしいので、ぜひ試してほしいです。特に、初めて日本酒を飲む方であれば、八海山や水芭蕉なんかは飲みやすくていいと思います。また、八海山の普通酒をいただく場合は、冷だけでなく燗で頂くと温度の差を顕著に体験できるので、これまたおすすめです。本当は、越乃寒梅、獺祭、久保田千寿や貴醸酒なども紹介したかったですが、越乃寒梅と獺祭はまだ飲んでいませんし、久保田千寿も非常に有名でオーソドックスすぎます。さらに、貴醸酒に至っては日本酒の中でも高級な部類に入るので今回は紹介していません。ちなみに貴醸酒とは、仕込みの最後の段階で水の代わりに日本酒を用いて醸造された日本酒のことで、濃厚な甘みととろみが特徴なのだとか。もし、飲む機会があった場合はその時に記事にしようと思います。
というわけで以上です。

なぜ原神に課金をしたくなるのか。単なる射幸心なのか?

最近、原神にはまってしまい課金してガチャを引こうか悩んでいる。なので、原神を始めとするソシャゲ課金をする事は愚かなのかを考えることにした。その考えの記録としてここに刻む。


ここで、一つ定義しておきたいことがあります。ここで言うソシャゲとは課金要素を含むゲームとします。具体的な、「ソシャゲ」の分類とか意味はここでは問わないものとします。
また、誤解のないように記しておきますが、決して課金をすることが悪いことと言うつもりは一切ありません。
加えて、日常的に重課金をしているほど余裕のある方に向けて書いたものでもありません。
また、これまでの課金総額は3000円程度であり大金を貢いだ経験は一切ないので、重課金から抜け出せず悩んでいる方のアドバイスにはならないと思います。
あくまでも、ガチャを引きたくて課金をしたいと悩んでいる学生の独り言としてお考え下さい。


さて、本題に入ろうと思う。
私は、これまでにガチャを引くために課金をしようと考えたことがない。課金をしたことがないといっても、いわゆるお得パック的なものは何度か購入したことがある。ただ、欲しいキャラがいて、そのキャラクターのために課金しようと考えたことがないということである。さて、どんなゲームに課金しようと考えているのかと言えば、タイトルからもわかる通り「原神」である。知っている人も多いだろう。今年の夏ごろに原神を始めて、なんだかんだプレイしてきたが、キャラクターデザインも優秀だし、モーションもかっこいい。それに、キャラクター一人一人にしっかりとしたストーリーが設けられており、親近感を感じることができる。そして、自分好みのキャラクターが登場したことで、割と大きめの額を課金しようと考えるようになった。しかし冷静になって考えれば、課金をしなくても十分に楽しめるコンテンツである。原神はオープンワールドであり、フィールドも広大である上に、そこら中に宝箱が眠っている。さらに、定期的にイベントが行われたり、イベント限定のストーリーも楽しむことができる。なので、別に無課金であろうと微課金であろうと、十分に楽しめるコンテンツに仕上がっているのは確かだ。それなのに、なぜ課金をしたくなるのだろうか。その理由について考察してみようと思う。

課金のメリット

課金をすることでゲームを有利に進めることができるのは言うまでもないが、ここでは、課金をすることの意義について考察する。
課金をせずに普通にプレイする場合、原石(いわゆる石)をまぁまぁの量しか貰うことができず、それは決して多い量ではないだろう。そして、この原石というのは、ゲームをするうえで非常に重要な役割を果たす。その最たるものがガチャの存在だと思う。この記事で述べる「課金」をすることによって、リアルマネーを代償に原石を手に入れることができる。

好きなキャラでプレイできる

正直、この理由が一番大きい。自分が格好いい、あるいは可愛いと感じたキャラクターは、ぜひ手に入れたいと感じるのが正直なところだ。しかも原神の場合、手に入れたキャラクターは画面内で歩いたり、走ったり、飛んだり、泳いだり、壁を上ったりする。さらに、どのキャラクターも個性的なスキルや行動、ボイスまでしっかりとついている。

強くなれる

ガチャを引くと強くなれる。例えば、スキルや大技が非常に強力なキャラクターであれば、それは是非手に入れて、敵を楽に倒したいと思うかもしれない。
敵を楽に倒せて、何か良いことがあるのかと問いたくなるかもしれない。しかし、このゲームにはエンドコンテンツに相当する要素がある。原神をプレイしている方なら分かるかもしれないが、そのエンドコンテンツ相当の要素とは、「深境螺旋」のことである。この深境螺旋に登場する敵はどいつもこいつも強敵で、生半可な育成では決して突破できるものではない。そこへ、強力なパーティを組めるキャラクターがいたらどうだろう。あるいは、既に所持しているキャラクターをもう一度引き当てて、強力な凸効果を得ることができたらどうだろう。簡単、とは言わないまでも、そこそこ楽に攻略できるようになると思う。

プレイの幅が広がる

プレイの幅が広がるというのは、そのゲーム性を非常に豊かにしているものと思える。例えば、一発で見たことがないほど桁外れなダメージを出してみたり、一瞬にして周囲の敵に大ダメージを与えて殲滅したり、アタッカーバーバラのように世間的にはサポーターとかヒーラーと位置付けられているキャラクターを無理やりアタッカーとして運用したりするのは、ロマンがあるし、そのキャラクターに対する愛情を感じることができて、すごく面白いと思う。
でも、これをするにはやはりガチャを回してキャラクターを手に入れるところから始める必要がある。そもそも、キャラクターを持っていなければ、そういったプレイやパーティ編成は組めないのだ。

課金のデメリット

次に、課金のデメリットについて考える。

飽きた先に残るは"虚無"

私が、課金に対して消極的な姿勢を示す最大の要因がこれ。のゲームに課金したところで、ガチャは引けるが物体として残るものは何もなく、ただの虚無である。もし、ゲームに飽きた場合、これまでの課金が単純に無駄になると考えると、どうしても尻込みしてしまう。

お金にはもっと有意義な使い道がある

少なくとも、Youtuberで原神の動画を投稿している方ではない限り、わざわざ多少のランダム要素をふくむガチャにお金をつぎ込む必要性は感じられないということである。とはいえ、むやみに宝くじやパチンコで散財する人よりはましだとは思うが。要するに、ランダム性のあるガチャを回すより、お金を払えば確実に手に入る娯楽(旅行、スポーツ、カラオケなど)、のほうにフォーカスすべきではないかと考えると、これまた尻込みしてしまう。極めつけには、結構の額を課金した後に冷静になって振り返り、その金額で何ができるだろうと考えてしまうと、なかなかの地獄である。

何が課金欲を搔き立てるのか

射幸心

ガチャである以上、この射幸心という感情を語らない訳にはいかない。これは、「次はもしかしたらうまくいくかもしれない」という、苦労をせずに思いもしなかった利益を期待することである。これは、ソシャゲのガチャだけでなく、宝くじ、パチンコや競馬といったギャンブルでも応用されている。ただ、原神やその他ソシャゲには、親切に「天井」という概念が用意されている。なので、ある程度の金額を費やせば、欲しいキャラクターが確実に手に入るのだ。なので、先に述べたようなギャンブルに比べたら、よっぽど親切である。そもそも、なぜ人間は不確定な要素に対して、利益を得ようとする期待を抱いてしまうのかという疑問がある。現状、ゲームを楽しくプレイできているのであれば、わざわざ不確定な要素にエネルギーを注ぐことはせずに、もっと他の部分にエネルギーを注いだほうが良いように思える。しかし、射幸心を煽られて課金をする人がいるのも事実である。はっきりとした目的がない場合は、射幸心を煽られようともお金を消費するのは良くないだろう。基本的に後で後悔するパターンが多い。しかし、単なる損得でゲームの課金要素の良し悪しを考察するのもよくない。

キャラクターの魅力

課金をする際に一番重要視するのは、やはりキャラクターの魅力だと思う。キャラの性格、ビジュアル、モーションやスキル、大技などにキャラクターごとの個性的な魅力がある。もし、自分に刺さるビジュアルのキャラクターだったり、キャラクターの個性が自分好みであったりすれば、欲しいと強く思うのは当然と言われれば当然だ。さらに、ゲームである以上、キャラクターごとに適した運用方法がある場合が多い。原神であれば、元素爆発効果中に受けるダメージが増加する代償として攻撃性能を上げて緊張感を楽しんだり、無類の回復力でパーティを手厚くサポートするゾンビ戦法で戦ったり、圧倒的な攻撃範囲と爽快感で無数の敵を葬る戦い方などである。

期間限定という仕様

私は、単純な射幸心と欲求だけが、課金欲を促進するものではないと思っている。それが、期間限定ガチャである。しかも原神の場合、期間限定ガチャの機会を逃すと、次に復刻ガチャとして登場するがいつなのかわからない。なので、期間限定という仕様が創り出す時間制限からくる焦りが原因の一つとも考えられる。

絶対的に"悪い"課金とは

メリット、デメリットだけを並べてもなかなか良い結論は出ない。なので、これだけは許せないとされる"悪い課金方法"について考察する。次に示した内容のものは、最終的に悪い結果を生みかねない課金の仕方について考えたものである。

目標の無い課金

例えば、たいして目標が立っていないのに課金をするのはよくないと思う。単純に散財するだけだし、仮に散財して強力なキャラクターを手に入れたところで、その幸福感というのは一瞬に過ぎないと思う。なので、何か目標を持った状態で課金をするべきだと私は考える。例えば、ピックアップのキャラクターを手に入れるまで、とか天井一回分引くまで、など。

撤退ラインの甘い課金

課金を始めた後に諦めるのはなかなか苦渋の決断となるかもしれない。しかし撤退ラインを設けなければ、極端な散財を起こしかねない。要するに、「ここまでガチャを引いても、欲しいものが手に入らなければ諦めよう」という心である。これは、原神やその他ソシャゲに限った話ではないと思うが、欲しいものが手に入らずにダラダラと散財してしまう可能性もないわけではない。この段階に入ってしまうと、冷静な判断は下せないため、それよりも前に撤退ラインを決めておけば良いだろう。

計画性の無い課金

これは、目標の無い課金に少し似ているが、目標を達成するために必要な金額がどれくらいなのかを検討した方が良いということである。原神の場合、必要な原石の数と手に入る予定の原石の数の差を取れば、いくら課金すれば欲しいキャラが手に入るかがわかる。めっちゃ簡単ですね。この「手に入る予定の原石の数」というのは、ガチャ期間終了までに手に入る原石のことであり、空月の祝福とか天空紀行を購入する場合は、それらも考慮するといいでしょう。

生活に支障をきたす課金

これは、たまにニュースや記事に取り上げられるタイプの課金であるが、これは良くない事態であるのは言うまでもない。メディアでよく取り上げられているのは、相当な額を課金して日々の営みに苦しむ人間についての記事だ。この状態に至るまでつぎ込んでしまうのは良くない課金だ。

楽しめない課金

これは私の個人的な意見になってしまうが、課金してガチャを回した結果が芳しくない場合、精神的に厳しい部分があると思う。娯楽の一環としてソシャゲの課金をするとなれば、お金を費やす以上、しっかりと楽しくあるべきであろうと思うし、そっちの方が費やしたお金も報われる。なので、どのようにしてガチャ課金を楽しむかということも考えた方が良さそうである。

"良い課金"とは何か

上に示した項目が、悪い課金とするのであれば、良い課金とは何か。それは、目標を立て、撤退ラインを設け、計画的に課金し、生活に支障が出ない程度に、楽しく課金すればよいということになる。しかし、この中でも「楽しく課金する」とはどういうことなのかについて考えたい。どうすれば、楽しく課金できるのだろうか。

友人に引いてもらう

課金してガチャを引くにしても、自分が淡々と引くのではなく友人に引かせるのはどうだろうか。それも、同じゲームをプレイしている人の間でガチャをすれば、さぞ盛り上がると思う。もしかしたら、欲しいキャラクターが手に入るかもしれない、という緊張感を友人と共有できるので楽しいだろう。それに、一人で引いて落胆するよりも精神的ダメージは少ない(ような気がする)。

目標達成が約束されている

友人とガチャを引くのは良いが、結構な額を課金し、他者に引かせておいて最終的にお通夜状態になるのも良いとは言えない。なので、天井確定分まで課金して原石を確保したりすれば問題ないかもしれない。

後で後悔しない課金

結局のところこれである。ガチャ課金をした挙句、欲しいキャラクター(もしくは武器)を手に入れることができなかったときに、一番のショックを覚えるかも知れない。これを避ける解決策としては、

  • 手に入れるのに必要な原石の数を計算する
  • できるだけ少ない金額で手に入れようと思わない

正直これくらいしかないと思う。いくら安く手に入れようと考えたところで、課金対象がランダム要素である以上、いくら課金すれば手に入るのか正確な値はわからない。その上、後悔する可能性は非常に高いので、後悔しない努力をしようと思っても難しい話だ。しかし、後悔するにしろ、その度合いを軽減できればそのほうが良いと思う。これらからいえることは、ガチャ課金をする場合ある程度決心をしてから行った方がよいだろう。そっちの方が後悔をしないというよりも、ずるずると沼にはまって後味の悪い課金をする可能性は低くなると思う。

課金に対する世間の目

課金をすることに対して肯定的な姿勢を示す人はあまりいないと思う。彼らの多くは、「罪悪感を覚える」なり「ゲームごときに課金など情けない」なり「喪失感を覚える」など、ネガティブな意見を抱いているだろう。私もそう思う。少し前までは、あらゆる大人達に「ゲームをすれば馬鹿になる」、「馬鹿にゲームは創れない」と言われる始末で、大人達のゲームに対する印象は散々なものであった。しかし、今となってはゲーム主体の大々的なイベントを開催したり、テレビでも大会の生中継を実施したり、その他ゲーマー向けのサービスも発展してきており、ゲームに対する世間の目は見違える程変化し、ある程度の地位を獲得してきているように思えた。ただ、ゲームとは所詮遊びであり、そんな遊びに対して多額の金額を費やすのは、どうしても気乗りしない。ただ、よく考えてみれば、遊びにお金をかけること自体、別に変なことではないと思う。むしろ、タダで遊ぼうだなんて言語道断であろう。何をして遊ぶにも、お金がかかるのはおかしいことではない。そう考えれば、例えばスポーツを楽しむために投資したり、アニメを見るためにサブスクリプションを契約したり、好きなアーティストのライブに行くためにお金を費やすことは罪悪感を覚えるだろうか?

罪悪感を覚える理由

お金を投資する対象が、ガチャであることが問題であると考えられる。結局のところ、ガチャというのはランダム要素であり、場合によってはこちらが損することもあり得る。それを考えると、ゲーム課金に対して否定的な態度をとるのも分からなくもない。

ゲームの購入は問題ない

ゲーム課金と言っても、これはゲームを購入することとは訳が違う。ゲームの購入とは、ゲームそのものを購入することであり、ランダム要素は一切存在しない。これに関して否定的な意見を持つ者は多くないと思う。少なくとも、ガチャ課金と比較すれば肯定的な意見を持つ者が多いと思う。
これから分かることとしては、ランダム要素が介入する課金は罪悪感を覚えやすいということだろうか。

課金以外のお金の使い道

ゲームに課金することがくだらないというのであれば、普段のお金の使い道はくだらなくないのか?例えば、ふと立ち寄ったコンビニでちょっとした散財をすることはくだらない事なのか?人間は意外にも、散財という行為に対してをあまり意識をしていない場合が多い。また、意識してお金を消費している場合でも、合算すると想像よりもお金を使っているという事実に驚く事もある。私もそうだ。こう考えると、くだらない散財というのはゲーム課金に限った話ではないかもしれない。むしろこうして、消費に対してしっかりと考える場合の方が、よっぽど有意義なようにも思える。意外な場面で散財していると思うと、少し恐ろしく感じる。

理想的な課金方法

ここまで、以下について述べてきた。

  • 課金のメリット、デメリット
  • 絶対的に悪い課金
  • 課金に対する世間の目

これから考えるに、どのように課金するのが幸せになりやすいのかを考えていきたい。

課金をする前に考えること

課金する予定の金額が大きい場合や、ガチャを回すために課金する場合は、以下に示す内容について考えた後に課金をしようと思う。

無料配布分の石を貯めておく

原神で、原石160個貯まった途端にガチャを引く方がいらっしゃると思いますが、これは得策じゃないかもしれない。次の復刻ガチャ、新キャラガチャなどに備えておけば、そもそも多額の課金をしなくとも天井まで引くことが可能だ。しかも、欲しいと思ったキャラクターがガチャに並んだ時に、撤退ラインを決めたり、課金を始めた後に結果が芳しくなくて諦めるという苦渋の決断をしなくなる可能性が高い。そのため、特に理由がない限りはガチャを引かないほうが良いだろう。

いくら課金すればで満足した結果になるか

一番良くないのは、理想的な結果を得られるまでだらだらと課金を続けてしまうことだ。仮に、理想的な結果を得られたところで後味が悪すぎるし、大きな損失を出してしまう。なので、ある程度金額を絞って、その金額を超えない範囲で課金を楽しむようにすれば良さそうである。

手に入ればこっちのもの

ガチャという楽しみは一瞬だが、手に入れたキャラクターはずっと使うことができる。人によっては、ガチャによる楽しみは一瞬だけだと考える人もいるかもしれないが、ガチャで手に入ったキャラクターはずっと楽しんで扱うことができる。よく考えてみれば、数万という金額が一瞬にしてガチャ石に変換されてしまうと考えると、一瞬にして大金を溶かしたようにも思えるが、大事なのはガチャを引く楽しみではなく、ガチャの結果だ。なので、ガチャのために奮発しても、今後もそのゲームで遊ぶつもりであれば、問題はないと思う。

無益な訳ではない

宝くじやパチンコと言ったギャンブルとは違い、全く利益が出ないということはない。いくらソシャゲのガチャがランダム要素を内包しているとはいっても、何も手に入らないということはない。少なくとも、原神の場合は星5の天井が用意されているし、星4の場合は10回引けば確定で手に入る仕様である。しかも、ピックアップされているキャラが手に入らなかった場合、次にキャラが手に入るときには確定でピックアップキャラが手に入るようになっている。かなり親切だと思う。なので、ガチャ課金が馬鹿らしいとは言っても、そこまで憂慮するほどのことではないと思うし、本当に欲しいと思ったキャラクターだったり、他の友人とゲームを楽しみたい出会ったりする場合は、むしろ引いた方が幸せになれると思う。

中毒性が高い事は間違いない

「次こそ出るだろう」とか「次は何が出るだろう」といった不確定な未来に対する期待からくる中毒性や、「他者より優位に立ちたい」とか「ゲームの流行に乗っかっていたい」といった純粋な"強さ"に対する欲求が、中毒性の原因なのかもしれないと考えた。これらを理由に、課金によって沼にはまる人もいるだろう。いくら課金しようとも、この中毒性が高さを忘れてはならない。

結論

というわけで、ここまで可能な限り多角的に考察をしてきたが結局のところ正解を出すのは難しい。しかし、課金するかどうかの判断は、個人の見方次第で大きいく変わると思う。ガチャ課金を無駄な浪費だとみなせば、課金に対して否定的な意見を持つだろう。しかし、ゲームも遊びであり趣味である。時には、現実世界でもコミュニケーションの起点ともなりうる。それを、「課金はよくない」の一点張りで片づけるのは良くない。なので、少なくとも悪い結果を生むような課金をしないようにすることを絶対に意識したい。そのほかに、可能な限り少ない金額で望んだ結果を手に入れようと思わない事も重要だ。

おまけ

最後に、課金したいという感情があっても、どうしても課金をできない状況(もしくは、課金する事が許せないプライド)の二つの感情が衝突したときに、どのように対処すればよいかを考えたい。

課金欲を鎮める方法

高ぶる課金欲を鎮める方をいくつか考えたので紹介する。

他のゲームをする

課金をしたいと感じる原因は、そのゲームに夢中になっているからであろう。夢中になっていないゲームに対して課金することは、まずないと思っている。例えば、廃課金者を対象にした記事を目にしたとき、「○○〇〇というゲームにこれまでに年間数十万の課金をしています」などと表現されることはよくあることだと思う。それを見た読者は「よくも○○○○にそこまで本気になれるな」といった感情を抱くのではないだろうか?そう思ってしまう原因としては、読者の多くがそのゲームをプレイしているとは限らないからだと考えている。なので、一つのゲームに集中するのではなく、いくつかのゲームを分散してプレイする事が、異常な課金欲をそそらなくさせる策なのではないかと考えている。

他者と会話する

他者と会話することは、人間関係を豊かにするだけでなく、自分の知らない世界を知ることにもつながる。なので、ゲームのことばかりを考えるのではなく、ゲーム以外の要素に注目しているうちに、課金に対する興味が冷めてくるかもしれない。しかも、相手と会話することによるアウトプットで、自己開示による信頼性の向上にもつながる。

課金の理由をアウトプット

まさに今の私である。これは、悩み全体に言えることだろう。どんなことであ、紙でもブログでも他者でも、自分の悩みを吐き出してみることで客観的にその悩みを見ることができる。最近では、悩みの解決手段として有名になりつつある方法だが、これは本当に効果があると思うので、どんな悩みであっても試してみたい。これのいいところは、紙に書いてアウトプットすることができるので、人に話すのが恥ずかしい悩みでも問題ないという事だ。

新たなことを始める

これは、他のゲームをするという項目に少し似た部分があるが、何か新しい事を始めるというのも手段の一つだ。例えば、ブログを書いてみるとか、ゲームをするのではなく創ってみるとか、普段は読まない本を読んでみるといった具合である。できるだけ、今自分が夢中になっているゲーム以外の部分に興味を分散させるように努力すれば、うまくいくかもしれない。

というわけで、以上で終わります。