2018/12/15 updated
個人開発アドベントカレンダーで「坂道駆動開発のご紹介 -ITを通じて乃木坂46を応援する-」を書きました!
タイトルのとおりです。名前を入れるだけで遊べるWebアプリを作ってみました。
https://www.rpgparty.sakamichi46.com/
ツイート結果はこんな感じ。
そういえば自分の試した結果を流していなかった(^^;すごい豪華で恐縮ですが、開発者による調整はしていません。 勇者「キクタロー」のパーティを坂道RPGパーティジェネレータで生成したよ! #sakagene46 https://t.co/9tKDTmdM0R #sakagene46 @sakagene46さんから
— キクタロー@エバスク役職ボッター (@kikutaro_) 2018年7月2日
乃木坂ファンをターゲットにしてますが、乃木坂のメンバーを知らない方にも遊んでもらいたいです。そして、パーティを組んだメンバーのことを知って欲しいです!
開発のきっかけ
- 脳内メーカーみたいなシンプルに楽しめるWebアプリを作ってみたい
- あれって名前が同じだと毎回同じ結果だけどどうやってるんだろう?自分だったらどう実現するか作ってみよう
- Peing -質問箱みたいにツイートしたら画像がそのまま表示されるサービス作ってみたい
- 「中年の危機」ど真ん中のオッサンがWEBサービス作ってみた。の記事に刺激を受けた
- 乃木坂46をはじめとする坂道グループが好きすぎる
ちなみに乃木坂46には**「きっかけ」**というタイトルの曲があって、ミスチルの桜井さんがカバーするほど素晴らしい曲です。ご興味あるかたは是非聴いてみてください!
サービスの構成と利用した技術
構成はシンプルです。静的なHTMLはNetlifyに置いてます。バックエンドのアプリはSpring Bootで開発してHerokuのHobbyプランで動かしてます。生成した画像はCloudinaryというメディア管理のサービスを利用しました。
フロントエンド
最初はVue.js使ってあれこれしようとか思っていたのですが、まずはシンプルなhtmlで作りました。
メッセージがゲームっぽく流れていますが、これはiTyped.jsというJavaScriptライブラリで実現しています。
タイプライター風にメッセージを表示できるライブラリです。ライブラリのページが機能のすべてを物語ってる気がw
コードもシンプルで、こんな感じに書けます。タイプのスピードや消すスピード、ループするかなど細かく設定可能。
<script>
ityped.init(document.querySelector('#message'), {
strings: [
'あなたが勇者となって乃木坂46メンバーとパーティを組んで旅に出る…。',
'1期生,2期生,3期生…そして伝説の卒業生たちも…'],
typeSpeed: 150,
backSpeed: 0,
loop: false,
disableBackTyping: true,
cursorChar: ""
})
</script>
ゲーム風のフォントは「PixelMplus(ピクセル・エムプラス)」というフリーフォントです。カタカナや漢字も使えるのが素晴らしいです。
バックエンド
バックエンドに利用したWebフレームワークはSpring Boot 2.0.3.RELEASEです。今のところ全く活用できていないですがWebFluxを使っています。将来的にはノンブロッキングで色々やりたい…。ビューのテンプレートはThymeleafです。
コントローラはGETリクエストで入力された名前を受けるだけ。Spring BootのScopeはデフォルトでSingletonとのことで、今回はリクエストごとに単発の処理をしたくprotptypeスコープを使いました。この辺はまだあまり理解しきれていない気も…。(Java EEでいうApplicationScopeとRequestScopeのイメージであってるのかな)
@Controller
@Scope("prototype")
public class NogizakaController {
@Autowired
private NogizakaGenerator nogiGenerator;
@GetMapping("/generate/nogizaka46")
public String generateNogizaka46(@RequestParam(name = "name") String name, Model model) {
String imageUrl = nogiGenerator.generate(name);
model.addAttribute("name", name);
model.addAttribute("imageUrl", imageUrl);
return "index";
}
}
Javaで画像を加工する処理を書くのは初めてだったのですが、わりと簡単にできるんですね。
BufferedImage bufferedImage = new BufferedImage(600, 314, BufferedImage.TYPE_INT_RGB);
Graphics graphics = bufferedImage.getGraphics();
graphics.setColor(Color.BLACK);
graphics.fillRect(0, 0, 600, 314);
graphics.setColor(Color.WHITE);
...
当初は生成した画像データをBase64でエンコードした文字列をHTMLのimgタグのsrcに指定していました。
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "jpg", os);
url = "data:image/png;base64," + Base64.getEncoder().encodeToString(os.toByteArray());
ただ、これだとツイートの際、Twitter Cardで画像が表示されません。調べてみると、画像表示するには静的なURLが必要なようです。
AWSのS3使うかなぁ…と思いながら何か良いサービスがないか探していたところ、Cloudinaryの存在を知りました。
Web APIで簡単にファイルアップロードできます。一番の売りはURLを使って画像の加工ができるところのようです。サイトのトップページにある以下の例がわかりやすいですね。
各種言語のSDKも揃っていて、もちろんJavaもあります。Mavenだと以下の定義です。
<dependency>
<groupId>com.cloudinary</groupId>
<artifactId>cloudinary-http44</artifactId>
<version>1.18.0</version>
</dependency>
アップロード処理はこんな感じ。cloud_nameやapi_key、api_secretはCloudinaryのダッシュボードから取得できます。アップロードするメソッドの戻り値でURLなど様々なデータが取得できます。
File outputfile = new File(name + ".jpg");
ImageIO.write(bufferedImage, "jpg", outputfile);
Cloudinary cloudinary = new Cloudinary(ObjectUtils.asMap(
"cloud_name", "xxxxx",
"api_key", "yyyyy",
"api_secret", "zzzzz")
);
Map upload = cloudinary.uploader().upload(outputfile,
ObjectUtils.asMap(
"public_id", name,
"folder", "フォルダ名"));
String url = (String) upload.get("url");
アップロードされたファイルはダッシュボードで確認できます。便利!
Twitterのシェア機能について
Twitterのシェアボタンを置くにあたって、当初はTwitterが公式に出してるTwitter Publishを利用して、ボタンを作りました。
リリース後、あることに気が付きました。サイトへのアクセスが増えて、画像もたくさん生成されているにもかかわらず、ツイートでシェアされる頻度がめちゃくちゃ少ない。Google Analytics使っていたので、確認してみると、アクセスの8割くらいがスマホでした。
で、上記ボタンをスマホで試すと…なんとアプリではなくブラウザが立ち上がってログインを求められました。
これはスマホユーザからするとストレスですね。ユーザとしてはアプリを起動して、シェアの本文が埋め込まれて欲しいはずです。
で、調べてみたところ**「twitter://post?message~」**というリンクにするとアプリ起動できるとのこと。実際に試してみたところ、確かにアプリ起動のダイアログがでてきました。知らなかった…。
なお、このボタンだけにしてしまうと今度はPCからのアクセスで以下ダイアログが出るだけとなってシェアできません。
なので、とりあえず今は仮実装という感じで2つのリンクを入れています。今後、アクセスしている端末がスマホかPCか判別して、デバイスに合ったシェアボタンのみ表示するように変えていきたいと思います。
ロジック
脳内メーカーみたいに入力された文字が同じであれば同じ結果を返す、という処理をどう実装するかなぁ…と最初は悩みました。入力文字列は様々なものになりますが、なるべく統一的に処理したい、という。
最終的にはパスワードのハッシュ化などで使うSHA-256を使うことにしました。これを使えば入力文字列の長さに関係なく、256bit(32byte)のデータが生成できます。
パーティに割り当てられる人数は4人にして、64bit(8byte)ずつを割り当てました。あとはその割り当てられたbitを使って、一定のルールを決めました。ルールに基づいて抽出した数値をRandomのシードに指定して、名前が同じであれば同じ結果となることを実現させました。
ドット絵
地味に一番頑張ったは、プログラミングよりもドット絵な気がします(^^;
こちらは乃木坂ちゃん1stシングル「ぐるぐるカーテン」の衣装をイメージしたビット絵です。
ドット絵はスマホアプリの dotpict! を使いました。ネ申アプリ!
スマホのエディタ画面はこちらです。直感的なUIで使いやすかったです。
作成したドット絵を公開する投稿機能があって、SNSのように「フォロー」や「いいね」の機能まであります。これで無償アプリとか信じられない…。お金を払うと広告が消せるようですが、広告も邪魔にならないのであまり消す気にはならないっていうw ただ、作者様への感謝の意を込めて払いたいと思います。
こだわった点
色々ありますが、メンバーには卒業生を入れることにはこだわりました。橋本奈々未さんや深川麻衣さん、そして生駒里奈さんなど人気絶頂の中、卒業したメンバーは今もファンの間で根強い人気があります。そういったファンの想いも考慮して入れました。現役メンバーと同じ確率で選ばれるのも微妙なので、重みづけをしてレアキャラ感を出しています。
さいごに
現在は乃木坂46だけなのですが、今後は欅坂46、けやき坂46(ひらがなけやき)の提供をしていく予定ですので、坂道ファンの皆さんも、そうではない皆さんも是非遊んでいただければと思います。
リリースした後、アクセスが思っていたより多く、既にCloudinaryの無料プラン枠を超えそうなのが現在一番の懸念です(^^;とても良いサービスなのでお金を払うことに抵抗はないのですが、Freeの1つ上のPlusプランだと月$89と個人開発で使うにはお高め><;