【C#】Unityで一からギャルゲー(ノベルゲーム)作るためのサンプルコード
一応は前回の記事の続きにあたりますが、コードの内容はほとんど別物です。 前回の記事ではゲームの方針として脱出ゲームのようなものを考えていましたが、今回はやや方向性を変えギャルゲーっぽいものを作ることを想定します。
今回の記事でやりたいことは以下になります。
1.シナリオをスクリプト外のデータから読み込み、それに合わせて処理を行う
2.キャラクターの立ち絵に簡単なアニメーションをつける
3.テキストの文字送り機能の実装
コードは以下に上げてあります。 github.com
またUnityroomで動作確認できます。 unityroom.com
テキストベースのシナリオ処理
まず上で述べた1に関してです。データの保存形式にはcsvやjsonなどいろいろありますが、調べてみるとメタ文字を設定したテキストデータで記述するのが一般的らしいので、その例に倣います。今回は#charaタグで立ち絵を登場させ、#imageタグでそのグラフィックを変更するなどを行いました。今回は実装していませんが他には背景を変えたり、BGMや効果音などのタグを用意しそれに合わせて音を鳴らすなどもできそうです。
#scene=001 #speaker=主人公 {あ~ 今日もいい天気だなぁ} #speaker=??? {お~い 待ってよ~~~} #speaker=主人公 {ん?この声は…} #chara=hiroko #image_hiroko=smile #speaker=ヒロ子 {じゃ~ん あたしだよ~~~ 一緒に学校行こうよ~~~}
1シナリオの流れは、シーンという小単位に区切って管理するものとします。 1シーンに相当する文字列を保持しておくSceneクラスを用意し、 別途パーサークラスを用意して一行ずつ読み取らせることで処理を行います。
public class Scene { public string ID { get; private set; } public List<string> Lines { get; private set; } = new List<string>(); public int Index { get; private set; } = 0; public Scene(string ID = "") { this.ID = ID; } ...
読み取った位置行ごとの文字列に応じて、実際に処理させるクラスのメソッドを呼び出します。 例えば現在の行が#speakerタグのついた位置に達したときにはSetSpeakerメソッドが、#imageタグの位置に来たときはSetImageメソッドが呼び出される等です。
public void SetSpeaker(string name = "") { gui.Speaker.text = name; } public void SetImage(string name, string ID) { var character = Characters.Find(c => c.Name == name); character.SetImage(ID); }
前回はデリゲートメソッドを用いてボタンクリック時に実行されるメソッドを指定することで 選択肢を実装していましたが、今回は#optionというタグを付けることで 選択した先のシーンに遷移し、その先で処理を行うものとします。
#speaker=ヒロ子 {そういえば、今日はテストだったよね~勉強した?} #options={ {した,002} {してない,003} } #scene=002 #speaker=主人公 {したに決まってるだろ} #image_hiroko=aseri #speaker=ヒロ子 {ほんと~?あたし全くしてないよ~~~} #speaker=主人公 {おまえはいつもそうだな~早く学校行くぞ} #next=004 #scene=003 #speaker=主人公 {してない。面倒だから} #image_hiroko=smile #speaker=ヒロ子 {ほんと?やった~あたしと同じだ~} #speaker=主人公 {そんなんで喜ぶなよ…早く学校行くぞ} #next=004
用意されたタグ以外の処理を行いたいときはどうすればいいのか考えたのですが、MethodInfoクラスというものを用いることで実現できるようです。これによりテキストデータ内にメソッド名を直接書いてしまうことができるので実装の幅が広がります。自分も調べて初めて知りましたがかなり便利そうです。
if (line.Contains("method")) { line = line.Replace("method=", ""); var type = actions.GetType(); MethodInfo mi = type.GetMethod(line); mi.Invoke(actions, new object[] { }); }
これを用いることで、「○○というフラグが立っていればシーン00Xに遷移し、そうでなければシーン00Yに遷移する」という、いわゆるif-gotoの処理を行うことができます。フラグを立てる事自体もこれでできるでしょう。
public void HasKey() { if (ExistsKey()) sceneController.SetScene("00X"); else sceneController.SetScene("00Y"); }
DOTweenを使ったアニメーション
上記の2.3についてですが、これはアセットストアで配信されているDoTweenというアセットを導入することでごく簡単に実装することができます。簡単に説明するとUnityのtransformに対して時間経過で色々できる便利なやつで、Unityのアニメーションクリップを使わずともお手軽にアニメーションを導入できるので、複雑な動きを必要としない場合はこれだけで十分間に合うと思います。
詳しい説明は他サイト様にお任せするとして、例えばメッセージテキストのクリックを促す下矢印のアニメーションは、スクリプトから以下のように記述するだけでいいし、キャラクター登場時のエフェクトなんかもDoTweenでやっています。
public GameObject Delta; private void Start() { Delta.transform.DOMoveY(-0.2f, 1.0f).SetRelative().SetEase(Ease.InCubic) .SetLoops(-1, LoopType.Restart); }
なおDoTweenはFree版と有料のPro版が存在しますが、多分基本的な機能に違いはないと思います。自分はPro版導入してるのでもしFree版で動かなかったらすみません。
また3のテキストの文字送りに関して、本来であればUpdate関数内で一文字ずつ処理を行ったりしなければならないのですが、これもDoTweenのDOTextというメソッドを使うことでこれだけ簡単にできます。テキストのアニメーションを完了させることで文字送りのスキップも行なえます。 (参考:https://gist.github.com/anzfactory/da73149ba91626ba796d598578b163cc)
public void SetText(string text) { tempText = text; if (textSeq.IsPlaying()) { textSeq.Complete(); } else { gui.Text.text = ""; textSeq = DOTween.Sequence(); textSeq.Append (gui.Text.DOText ( text, text.Length * messageSpeed ).SetEase(Ease.Linear)); } }
結論
だいぶかいつまんで説明しましたが、今回の記事で自分が言いたかったのは
- MethodInfoを使えばだいたい何でもできそう
- DoTweenは便利
の2点に尽きます。実際今回のコードに少し手を加えるだけでそれなりに見れるゲームは作れると思います。
実際には他に優れたUnity向けのツクール系エディタやアセットが存在するのでそれを使えばいいだけの話なのですが
その他
お借りした背景素材: きまぐれアフター 様