見出し画像

【Unity】Unity1Week【おくる】に参加!【備忘録】【クリスマスに無限地獄】

2023年12月18日(月) 0時 〜 2023年12月24日(日) 20時の開発期間で開催されたunity1週間ゲームジャムに今回も参加いたしました。7回連続7回目の参加となります。

今回は「おくる」というテーマでした。以前から開発を考えていた「探し物ゲーム」というジャンルに挑戦し、その場面を「おくる」に関係があるシーンにしました(クリスマスパーティー会場(贈る)、配送センター(送る)、駅、空港(見送る))。

「探し物ゲーム」は絵の中から指定されたモノを探すゲームで、「ウォーリーを探せ!」をイメージしてもらえるとわかりやすいです。

アプリでは以下の3作品がダウンロード数が多いです。

今回、自分が作成したゲームが「ゆっくり探していってね!!」です。あらゆるシーンに潜んでいる「ゆっくり」を探してクリックするだけのゲームです。簡単に遊べますので、よろしければ是非プレイしてみてください。

こんなゲームです。

今回も制作にあたり、色々苦労がありましたので、そこら辺を備忘録として書き綴っていきます。


1.グラフィックを大量に用意する

(1)DALL-E3を使って、画像を生成する

探し物ゲームには大量の画像が必要ですが、フリー素材を用意するのは非常に骨が折れますし、あとでスマホ版をリリースするかもしれないと考えたときに権利関係がややこしくなるので(例えば「いらすとや」の場合、商用利用時に使用できるのは20点まで)、全てChatGPTのDALL-E3で出力することにしました。

<プロンプトの例1>
DALLE3で以下の画像を生成してほしいです。プロンプトは英語でお願いします。 アイテム同士が重ならないように。背景は透過、または無地。アニメ風で。ゲームで使用します。子供部屋に置いてありそうなアイテム。正面から見た絵。

例1の出力結果

<プロンプトの例2>
DALLE3で以下の画像を生成してほしいです。プロンプトは英語でお願いします。 背景は透過、または無地。アニメ風で。ゲームで使用します。正面から見た絵。全身を描く。子供。子供同士が重ならないように6人描く。

例2の出力結果

<プロンプトの例3>
DALL-E3で画像を生成してください。プロンプトは英語でお願いします。 ・アニメ風の画風。 ・画面比率が横1920、縦1080の画像。 ・着色はしてください。 ・ゲームで使用する想定。 ・物は少なめ。 ・住宅のリビング。 ・クリスマスという設定。

例3の出力結果

(2)CLIP STUDIO PAINTを使って、画像を切り出す

DALL-E3で作成した画像から使えそうな部分を切り出していきます。自分はPhotoshopを持っていないので、CLIP STUDIO PAINTで作業は行いました。手順は以下の通りです。

  1. 切り抜きたい部分がある画像をCLIP STUDIO PAINTで開く。

  2. 1で開いた画像を「画像素材レイヤー」から「ラスターレイヤー」に変更する。

  3. 新規で別のキャンバスを立ち上げる。今回は人物切り抜き用の幅256×高さ512のキャンバスを作成。元からあるレイヤーを黒などで塗りつぶす。

  4. 2のレイヤーから切り抜きたい範囲を、選択範囲ツールで選択し、Ctrl+Cでコピー、3のキャンバスの1番上にCtrl+Vでペーストする。

  5. 自動選択ツールで一緒にコピーしてきた背景を選択し、Deleteを繰り返し、綺麗にする。「色の誤差」の数値を変えたり、ごみ取りツールも有効。塗りつぶしたレイヤーをオンオフを切り替えて、消し残しがないか確認する。

  6. 塗りつぶしたレイヤーを非表示にして、「画像を統合して書き出し(ping)」を行い完成。

例2から切り出した女の子の画像

慣れれば1つ1分くらいでいけます。
効率よい作業のためには背景が無地であることと他のアイテムとの重なりがないことが重要です。
切り出しが面倒くさそうな画像はスルーしたほうがよいでしょう。

背景の一部も切り出すことで、その画像と背景の間に画像を置くことができ、遠近感を作り出すことができます。

クリスマスパーティー会場の背景の前景

2.探し物ゲームのゲームシステムを作る

(1)オブジェクトをステージに配置する

ステージのヒエラルキーは以下のようになっています。
自分はUIを配置する際にOptimaizeCanvas_portrait>BasePanalの下に配置します。これについての詳しい記述は過去のnoteをご覧ください。

ながーい、ヒエラルキー。(京〇銀行)

OptimaizeCanvas_portrait>BasePanal>Stage(空オブジェクト)の下にStege上のオブジェクトを配置していきます。Stageは背景画像を配置します。
Objectには「人物」「物」「背景の前景」「ゆっくり」を重なりや大きさを調整しながら、配置していきます。
なお、人物の大きさの調整にはスケールは使用せず、幅と高さを入力しています(理由は後述)。

(2)ゆっくり用のボタンを作る

画面上のゆっくりをクリックするゲームなので、画像をゆっくりにしたボタンを配置すればいいと思いましたが、そうすると、他の画像の透過部分が干渉し、ボタンが押せません。画像の重なりをそのままにボタンを押せるようにするために、ゆっくりは「画像」と「透明のボタン」に分けることにしました。
ヒエラルキー上の「YukkuriButton」の子オブジェクトは全て透明のボタンで「YukkuriClicker」コンポーネントがアタッチされています。
クリックすると、画面上のゆっくりが非表示になり、済スタンプが押されるアニメーションが再生されます。

(3) ターゲットウィンドウとスタンプアニメーションを作る

探すものが分からないと「探し物ゲーム」にならないので、探すものを表示するターゲットウィンドウを作ります。
Targetという外枠の画像を作成し、子の空オブジェクトLayoutGroupを作成、VerticalLayoutGroupをアタッチします。その子オブジェクトとして、3つ画像を置いて作っています。
さらにターゲットウィンドウに表示されているゆっくりを見つけると「済」というスタンプが押されます。

気持ちいい

作成方法は以下の動画を参考にしました。

少しわかりにくい点は、動画内で言っている「余韻」を付けるためには、以下のようなヒエラルキーにし、Stampは空オブジェクトにしてAnimatorをアタッチし、透過度が違う同じ画像を子オブジェクトにすることです。同階層のオブジェクトをAnimatorで制御することはできないためです。

おそらくAnimatorを持ったStamp_baseの子オブジェクトにStamp_ghostでもいける

(4)タイムラインで演出を作る

今回もタイムラインを多用しました。本当に便利です。
今回感じたのですが、タイムラインの音声は、前後が切れることが多いので、長めに設置したほうがいいです(特にタイムライン開始直後が顕著)。

カウントダウン時のタイムライン
ステージクリア時のタイムライン

(5)オブジェクトやボタンのアニメーションを付ける

今作では人物に簡単なアニメーションをつけていますが、ほとんどが使いまわしです。スケールのY値が1→1.1→1を繰り返すアニメーションなどです。
先述した人物の大きさの調節にスケールを使わない理由が、スケールで調節した画像には、このアニメーションの使いまわしが適用できないからです。

びよんびよん

0.5のスケールの画像が、1→1.1→1とアニメーションしていたら、0.5に調節した意味がないですよね。
これはボタンのアニメーションにも言えることなので、自分はボタンの大きさ調節もスケールを使っていません。

3.予想外のエラーを解消する

特にゲーム制作で詰まることはなかったのですが、クリスマスイヴから3日間、ビルドエラー地獄に苦しむ羽目になりました。
ちなみにこれは自分で作り出した「無限道」——インファナル・アフェアでした(映画「インファナル・アフェア」は名作なのでおススメです)。

(1)自分がやった対処方法一覧

  1. Unityのバージョンを変える

  2. Unityの再インストールをする

  3. Libraryフォルダを削除してから、Unity再起動でライブラリ入れ直し

  4. プロジェクト内の日本語ファイルを全て英語にする

  5. パッケージマネージャで更新できるものは更新する

  6. 関係なさそうなアセットを全て消す

が、ダメっ…!

↓3のリネームの際に使用

(2)元凶はなんだったのか

空のプロジェクトはビルドできていたので、アセットの問題だと思っていたのですが、いつも使用している画像ツイート(ポスト)機能に必要なプラグインが入っていないことが原因でした。

OpenWindow.jslibが入ってなかった

なぜ、こんなことが起こってしまったかというと、少し前に立ち上げ時の作業をまとめるという記事を書いたのですが、その際に、絶対使用するスクリプトや画像を「スターターセット」みたいにエクスポートしておいたんです。その中にプラグインを入れ忘れたんです。楽をしようとして、地獄を見たんです。ハイ。

ちなみに何故解決したかというと、「OpenWindow.jslib」が見つからないというようなエラーコードが出たので、「OpenWindow.jslib」をEverythingで検索すると他のプロジェクトのTweetScreenShotフォルダにあったので解ったわけです。

Everythingにはいつも助けられていて、本当ないと生きていけないレベルです。皆さんも是非使ってみてください。Windowsの検索がアホらしくなります。

↓思い返せば、この時もEverythingに救われていた。

4.今回作成したコード

大して複雑なコードではないです。

(1)YukkuriClicker

ゆっくりに対応した透明ボタンにアタッチ。

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class YukkuriClicker : MonoBehaviour {
    [Header("フィールド上のゆっくり")][SerializeField] private GameObject onFieldYukkuri;
    [Header("スタンプの画像")][SerializeField] private GameObject _stamp;
    [SerializeField] private float waitTime = 0.3f;
    [SerializeField] private AudioClip _stampSe;
    [SerializeField] private AudioClip _yukkuriVoice;
    private Button _button;
    [SerializeField] private StageManager _stageManager;

    void Awake() {
        _stamp.SetActive(false);//スタンプを非アクティブにする
        _button = GetComponent<Button>();//ボタンコンポーネントを取得
    }
    
    public void OnClick() {
        _stageManager.yukkuriCount--;//yukkuriCountを減らす
        SoundManager.i.PlayVoice(_yukkuriVoice);//ゆっくりの声を再生
        _stamp.SetActive(true);//スタンプの画像をアクティブにする
        _button.interactable = false;//ボタンを押せないようにする
        onFieldYukkuri.SetActive(false);//フィールド上のゆっくりを非表示にする
        StartCoroutine(nameof(StampSe));//コルーチン開始
    }

    private IEnumerator StampSe() {
        yield return new WaitForSeconds(waitTime);//waitTime分あける
        SoundManager.i.PlaySe(_stampSe);//スタンプのSEを再生
        gameObject.SetActive(false); // クリックされたオブジェクトを非表示にする
        yield break;
    }
}

(2)StageManager

各ステージに1つ配置。

using System;
using UnityEngine;
using UnityEngine.Playables;

public class StageManager : MonoBehaviour{
    
    [Header("クリア後タイムライン")][SerializeField] PlayableDirector _director;
    [Header("BGM")] [SerializeField] private AudioClip _bgm;
    [NonSerialized] public int yukkuriCount = 3;
    [SerializeField] private int NextStageNumber;//次のステージのナンバー

    //カウントダウンタイムラインからシグナルを受け取る
    public void SearchStart() {
        SoundManager.i.PlayBgm(_bgm);
    }
    
    void Update(){
        //yukkuriCountが0になったら
        if (yukkuriCount == 0) {
            SoundManager.i.StopBgm();//BGMを止める
            _director.Play();//クリア後タイムラインを再生
            DataManager.i.DataSave(NextStageNumber);//次のステージのナンバーを記録(未使用)
        }
    }
}

(3)TitleManager

タイトル画面に配置。ステージからタイトル画面に戻った時にBGMが鳴りっぱなしになるため。SoundManagerがDontDestroyOnLoad(シーン遷移で破棄されない)なので、意図的にBGMを停止する必要がある。今作ではステージクリア後にはBGMを停止するのでステージ→ステージの移動では発生しない。

using UnityEngine;

//タイトル画面に配置。ステージからタイトル画面に戻ってきたときにBGMが鳴りっぱなしになるため。
public class TitleManager : MonoBehaviour {
    private void Awake() {
        SoundManager.i.StopBgm();
    }
}

(4)DataManager(未使用)

ステージのクリア状況をセーブ・ロードする予定だったが、最初から全ステージ選べる方式にしたため、使用しなかった。

sing System;
using UnityEngine;

public class DataManager : MonoBehaviour{
    [NonSerialized] private int score;//スコア
    //カッコを上にやる
    public static DataManager i; //どこからでもアクセスできるようにする

    [NonSerialized] public int canPlayStage = 1;

    void Awake(){
        CheckInstance();
    }

    void CheckInstance(){
        if (i == null){
            i = this;
        } else{
            Destroy(gameObject);
        }
    }
    
    void Start() {
        DontDestroyOnLoad(gameObject);//シーン遷移しても破棄されない
        DataLoad();
    }
    

    //データをセーブする
    public void DataSave(int nextStage) {
        if(nextStage == 0) {return;}
        ES3.Save<int>("CANPLAYSTAGE", nextStage);
    }
    
    //データをロードする
    public int DataLoad(){
        score = ES3.Load<int>("CANPLAYSTAGE", defaultValue: 1);
        return canPlayStage;
    }
}

この記事が気に入ったらサポートをしてみませんか?