ノベルゲーム風にリレーして物語を書けるアプリをリリースした話【個人開発】


ノベルゲーム風に物語を作れるアプリ

サメが泳ぐのをやめられないのと同じで、個人開発者が個人開発をやめるのはそのもの死を意味する。
つまり個人開発者は何か開発しなければならない。

ぽあんぽあんと寝っ転がってアプリのアイデアを考えていた。
すると「ノベルゲーム風の物語をみんなで作れたらなぁ」というアイデアが浮かんだ。

ノベルゲーム風に物語を作れるアプリ、これの開発が決まった。
実際に作ったものはこちら↓

技術選定

今回は特に新規市場開拓、背伸びなどをせずに筆者の慣れている技術を使うことにした。
それは↓のとおりだ。

  • Django
  • Vue
  • DRF
  • Bootstrap

うーん、代り映えのない技術だ。
そろそろこれらも手あかがついてきた。
何か新しい刺激が必要だと感ずる。

まぁアイデアを具現化できるものであれば一番慣れている技術がいい。
今回のアプリはこれで作ることにした。

アプリの仕様

アプリの仕様はすぐに決まった。
特に悩むこともない。

ようは絵に文章とBGMをつけられればいいのだ。
つまり背景とキャラクターそれから文章と音楽だ。
これらの要素を選択できるエディターを用意する。

ノベルゲームはマウスのクリックなどで画面が切り替わる。
この1枚1枚の画面のことを何というの私はしらないが、とりあえず「コマ」と呼ぶことにした。
そしてコマが集まったものを「シーン」と名付けた。

エディターではシーンを作成し、それをほかの人たちに公開できるようにする。
シーンの作成では複数のコマを作成できる必要がある。
そして各コマには背景やキャラクター、文章やBGMを設定できるようにする。

作成したシーンは公開されるが、ほかの人がそのシーンの続きを書けるようにすると面白いと思った。
つまり、特定のシーンを親にして、子のシーンを作るわけである。
この構造はリンクリスト構造であり、WebのDBでも再現できる。
自己参照モデルを定義すればそれで済む。

仕様と言えばこれだけである。
私は開発に取り掛かった。

開発の入り口

開発ではまず最初にDjangoのアプリを作る。
これはコマンドで行える。
開発コードはstorymakerとした。

$ python manage.py startapp storymaker

だがstorymakerという名前はどうも色々なところで使われているらしい。
アプリを公開する場合は商標権や既存の他のアプリと名前が被っていないか確認する必要がある。
これを怠ると、アプリに人気が出たのに名前がコリジョンしているせいで名前を変更しなければならない、といったトラブルが起こる。
アプリの名前はユニークなものに限る。

それからWebpackを設定して、Vueの開発に入れるようにした。
DjangoとWebpackを組み合わせるにはdjango-webpack-loaderなどを使う。

WebpackでビルドしたVueのソースコードをDjangoで読み込み、画面に表示できればあとはVueをごりごり書いていくだけである。

Vueによるシーンエディターの作成

Vueでシーンエディターを作る。
エディターは一般的に言って複雑なものになりやすい。
また、標準のVueの機能だけではデータを表現するのに力不足なところもある。

そこでVuexを入れる。
Vuexはデータをグローバルなところに集め、散らかっているコンポーネントから一元的にデータを操作できるようにするものだ。
エディターの編集するデータは複数のコンポーネントから参照される。
そのためVuexは必須と言える。

シーンエディターでは背景とキャラクターのドロップダウンメニューを作る。
そして背景とキャラクターが選択された、プレビュー画面にその絵を表示する。

絵が必要なので絵を描く必要がある。
私は愛用のペイントツールSAIを起動してテスト用の絵を描いた。
そしてそれをシーンエディターで表示した。

無事に表示された。
これは別になんてことはない、特に技術的にむずかしいことはしていない。

そういう感じで文章用のテキストエリア、それからBGMの選択肢などを実装した。
BGMについてはFL StudioというDTMソフトで作成した。

あとはドロップダウンメニューなどで編集したコマをシーンのコマのリストに追加する機構を作る。
今回はデフォルトで新しいコマがリストに追加されている状態でエディターが起動するようにした。
こうすると「コマがない」状態を省略できるので仕様が簡単になった。
もっともコマは削除できるので、すべて削除するとバグになるところも出てくる。
やはり「コマがない」という「0」の状態は必要であったと思う。

シーンのデータはVueからaxiosを使ってDjangoのAPIと通信して保存する。
これでシーンがDBに保存される。

シーンは1つ以上のコマで構成されるので、DB的には↓のようなモデルでシーンとコマを繋いでいる。

from django.db import models
from storymaker.models.scene import Scene
from storymaker.models.koma import Koma


class SceneKomaXref(models.Model):
    scene = models.ForeignKey(Scene, on_delete=models.PROTECT, help_text='シーン')
    koma = models.ForeignKey(Koma, on_delete=models.PROTECT, help_text='コマ')

    class Meta:
        verbose_name = 'シーン X コマ'
        verbose_name_plural = 'シーン X コマ一覧'
        ordering = ('-id', )

    def __str__(self):
        return f'({self.id}) シーン X コマ'

これは特定のオブジェクト同士をつなげたい場合によく使われるモデル構造だ。
今回もこれにお世話になった。

シーンプレビュー

シーンのプレビュー

シーンプレビューはシーンを表示するコンポーネントだ。
これはコマプレビューというコンポーネントを持っている。

シーンプレビューの役割は複数のコマをコマプレビューに表示していくことだ。
コマプレビューはコマの表示に専念し、シーンプレビューは複数のコマの操作に専念する。
こうすることで複雑さが軽減出来て開発が楽になる。
いわゆる分割統治的なアプローチで一般的な方法と言える。

シーンプレビューを配置するページにはシーンの親と子も表示できるようにした。
これでリンクをたどるようにそのシーンの親子を見ることができる。

そして「このシーンの続きを作る」というボタンを配置する。
このボタンを押すとそのシーンを親にしてシーンエディターが起動する。
シーンに親を追加するにはparentという属性にIDを与えればいいだけなので、非常に実装が楽だった。
設計がシンプルだと実装もはかどると言える。

完成と公開

アプリの名前は「物語ツクraw」にした。
「物語作ろう」とかけている。

平日の仕事が終わってからと休日を使って1か月ぐらいで完成した。
一番時間のかかったのは絵などのリソースの用意だった。
リソースはAIで生成できる時代がもうそこまで来ているので、こういったリソースを作るスキルも市場価値が下がっていくかもしれない予想がある。
もっといえばソフトウェアもそうだ。

AIはパソコンを使った仕事をほとんど代替していく可能性がある。
コツコツスキルを積み立ててきた自分には悲しい話である。

話はそれたが、2022年の3月に「物語ツクraw」は無事公開できた。
物語を作るのが好きな人が使ってくれると嬉しい限りである。

おわりに

今回の反省点は、技術的に何か新しい挑戦をしなかったことだ。
この姿勢でいるとどんどんレガシーになっていってしまうのだが、やはり楽なほうに流れてしまう。
アプリを作る際はなにか新しいことに挑戦するようにしたほうがいいかもしれない。

こんなこと言ったら「物語ツクraw」も浮かばれないかもしれないが。