こんにちは、 フーシャ です。
今回は前回の記事に続いてお題「おくる」のunity1weekに参加した話、第2弾として開発編です。
主に実装周りで使ったライブラリ・実装内容辺りについて書こうと思います。
企画・素材周りは前回の記事で書いてますので、気になる方はこちらから読んでみてください!
作ったゲームはこちらです!
使ったライブラリなど
今回の開発では、 ScriptableObject , UniTask , UniRx , DoTween 辺りを使っています。
既に使ってるって方もまだ使ったことないって方もいるかと思いますが、それぞれざっくりと説明していきます。
自分自身まだ使いこなせてない部分も多いですが、
今回のゲームでどんな感じに使ったかをメインにお話しできればなと思います!
ScriptableObject
ScriptableObject はシリアライズ可能なクラスで、よくデータの管理用に使われるクラスです。
既に使っている方も多いかと思います。
使い方としては
[CreateAssetMenu(fileName = "StageData", menuName = "ScriptableObject/Stage", order = 0)]
public class StageData : ScriptableObject
{
[SerializeField] private int _stageID;
[SerializeField] private int _questionCount;
[SerializeField] private float _timeMultiplyer = 1f;
[SerializeField] private CharacterData[] _characterDataArray;
public int StageID => _stageID;
public int QuestionCount => _questionCount;
public float TimeMultiplyer => _timeMultiplyer;
public CharacterData[] CharacterDataArray => _characterDataArray;
}
みたいにScriptableObjectを継承したクラスを作った後、Unityエディタ上でアセットを作成して、使用したいクラスで参照させるだけです。
ScriptableObject 内のデータとして、別の ScriptableObject のデータの参照を持たせたりもできるので、階層的にデータ構造を作成したりも可能です。
アセットデータとして持てるので、 Asset Bundle を使用したデータの読み込みにも使うことができます。
なので、運営系のゲームだと Addressable Assets を使用したりして Scriptable Object を読み込んで、マスタデータとして使ったりもしますね。
今回のゲームでは、セリフデータ、文字データ、ステージデータ辺りを Scriptable Object のデータとして作成しました。
ちなみに Scriptable Object 内の変数をただのpublic変数にすると、スクリプト上からもデータの変更が可能になってしまうため、特に理由がなければ上記のようにプロパティでのアクセスをおすすめします。
間違ってデータを書き換えちゃった、とかやりがちです。。。
UniRx
UniRx はイベント処理や非同期処理に特化したライブラリです。
Observer パターンとか MVP パターンとかでよく使われますね。
Observe パターン? MVP パターン?って方はこちらのUnity公式が出したデザインパターンの和訳がおすすめです。
他のデザインパターンについても色々と解説されています。(ある程度プログラミングが分かる方向け)
使い方としては、
public class UIView : MonoBehaviour
{
[SerializeField] Slider _volumeSlider;
public IObservable<float> VolumeObservable => _volumeSlider.OnValueChangedAsObservable();
}
[RequireComponent(typeof(UIView))]
public class UIPresenter : MonoBehaviour
{
[SerializeField] UIView _view;
public void StartSubscribes()
{
_view.VolumeObservable
.Subscribe((volume) => SoundManager.Instance.SetVolume(volume))
.AddTo(this);
}
}
みたいな感じで使っています。
簡単に解説すると
UIViewでSliderの変更がされたら、それをSoundManagerに伝えて音量を変更するって処理になっています。
普通に処理する場合と違うところは、Sliderの変更を検知するクラス自身がSoundManagerの処理を呼び出す訳ではない、という点です。
今回の使い方ならそのままSliderの変更を検知するクラスで呼び出しても全く問題はないんですが、
変更を検知するクラスでそのまま処理を呼び出す場合、処理を送る先が複数になるとその全てのクラスを知っていないといけなくなります。
処理が複雑になるにつれてどんどんスパゲッティコード化していきます。
変更を検知するクラスは変更を検知してイベントを発火するだけ。
どんなイベントを呼んでのかは知らない。
どんなイベントを発火するかはイベントの登録側が自由に設定する。
ということがUniRxを使うことでやりやすくなります。
これをすることでクラスの役割が細分化されて、疎結合なプログラムを作りやすくなっていきます。
慣れるまで少しややこしいですが、慣れてくるとかなり便利です!
UniTask
UniTask は非同期処理に特化したライブラリで、async/awaitと一緒に使います。
元々は UniRx の一部だったそうです。
UniTask はコルーチンと同じように何か処理を待ちたい時とかに使ったりするものですね。
ただ、コルーチンとの違いとしてMonoBehaviorが不要、返り値を持つことが出来る、などがあります。
その代わりにキャンセルの処理が少しめんどくさかったりもします。
使い方としては
public void StartSerif(~~ 省略 ~~)
{
~~~~ 省略 ~~~~
SetSerif(serif, time, clip);
}
public async UniTask SetSerif(string serif, float time, AudioClip clip = null)
{
if (clip != null)
{
SoundManager.Instance.PlayOneShot(clip);
}
_serifFukidashi.SetActive(true);
_serifText.text = Regex.Unescape(serif);
if (time <= 0) return;
await UniTask.Delay((int)(time * 1000));
_serifFukidashi.SetActive(false);
await UniTask.Delay(100);
}
みたいな感じです。
上記はずんだもんのセリフのセット処理で、
セリフの音声データを再生開始と同時に吹き出しを表示&テキストセット
→吹き出しの表示時間分待機
→待機が終わったら吹き出し非表示
という流れです。
await UniTask.Delay((int)(time * 1000));
これが time 秒待つ処理になってて、セリフデータの方でそれぞれ時間を入れてます。
UniTask.Delay はms単位なので、秒数にしたい場合は1000倍します。
この他にも例えば文字が出てきてから次の場所に移動するまでの時間を UniTask で待ったり、セリフ音声の再生が終わったかどうかを待ったりとかに使ってます。
また、もし返却値を持たせたい場合は
async UniTask<bool> Hoge(){ return true; }
のようにジェネリック型を指定することによって返却値を持たせることが出来ます。
詳しく知りたい方はこちらの記事ですごく詳しく説明されてますので、こちらが参考になると思います。
DoTween
最後はみんな大好き DoTween です。
簡単にプログラムだけでオブジェクトのアニメーションさせることが出来るライブラリですね。
今回真ん中の文字の動きは全部 DoTween で動かしてます。
DoTween のおかげでアニメーションは作らずにいい感じの動きが簡単に作れました。
使い方としては、
transform.DOMove(_questionPos.position, moveTime).SetEase(Ease.InBack);
みたいな感じです。
これは文字が画面左側に表示されてから真ん中に移動させる処理です。
_questionPos.positionの位置までmoveTime秒で移動するって形ですね。
SetEase でイージングを指定することで、いい感じの動きを作ることが出来ます。
直接transformなどの拡張メソッドとして使えるのですごくシンプルに書けます。
また、今回は使ってないですが、.WaitForCompletion()やAsyncWaitForCompletion()を使うことで DoTween の完了を コルーチン や UniTask で待つことも可能です。
便利ですね。
X (旧 Twitter ) への共有 / ランキング機能
今回のゲームでは、X (旧 Twitter )への共有機能 と ランキング機能 がついています。
これらは両方とも unityroom ( ないち ) さんのライブラリを使用して作成しました。
どちらもすごく簡単に実装することが出来たので、もしまだやってない方は試してみるのをおすすめします!
X (旧 Twitter ) への共有機能
任意のタイミング(ボタンを押したときなど)にポスト用のメソッドを呼び出すことで、
上の画像のように新しいブラウザでポスト画面が開かれるようになります。
スマホの場合はディープリンク対応がされているため、アプリが開かれます。
Xへのポスト機能用のライブラリはこちらです。
ただ、こちらのライブラリはTwitterがXになる前に開発されたもののため、少し注意が必要な部分があります。
現在http://twitter.com/share
のリンクはスマホからだと上手く動かないらしく、http://twitter.com/intent/tweet
に修正する必要があります。
そのためTweetData.cs内の
const string ShareUrl = "http://twitter.com/share?";
を
const string ShareUrl = "http://twitter.com/intent/tweet?";
に修正が必要です。
また、もしポスト画面を 新規ブラウザ ではなく 新規タブ で開かせたい場合は、OpenWindow.jslib内の
window.open(url,
'intent',
'left='+Math.round((screen.width/2)-(250))+',top='+F+
',width=640,height=320,personalbar=no,toolbar=no,resizable=no,scrollbars=yes');
を
window.open(url);
にすることで可能です。
個人的には新規ブラウザのままで良さそうだと思ったので、そのままにしています。
ランキング機能
unityroom 公式のランキング機能を実装すると、このようにみんなのランキングが表示出来るようになります。
スコアボードの表示方法については昇順、降順、単位などを unityroom 側で設定することが可能です。
ちなみにランキングスコアの登録には unityroom へのログインが必要なため、全員のスコアがランキングに登録されるわけではありません。
以前なら NCMB (ニフクラ mobile backend)を使用したランキング機能の実装も一般的でしたが、2024年3月末にサービスを終了するお知らせがあったため、今後 unityroom で手軽にランキング機能を実装するならこちらのライブラリを使用するのが一般的になるのかなと思います。
unityroom のランキング機能のライブラリはこちらです。
https://help.unityroom.com/how-to-implement-scoreboard
おわりに
今回は開発編ということで、今回の unity1week に投降したゲームはどのようなものを用いて作ったのか、どういう形で実装しているのか、についてお話ししました。
全てをお話し出来た訳ではないですが、今回の話が少しでも皆さんの実装の参考になれば幸いです。
また自分自身、各ライブラリを使いこなすレベルまではいけていないので、今後もっと学習していければなと思います!
今回の記事が面白かった方は、共有やXのフォローなどしていただけると嬉しいです。
それでは最後まで読んでいただき、ありがとうございました!
コメント