見た目をちょっと変えた(ついでにトグルとか動かした)| Unity

音楽流すのに手間取ってしまいましたが、そろそろ画面の雰囲気をどうにかしていこうと思います。

現状はこんなかんじ。ゲームビューをモニターいっぱいにしたら結構空間が広くてさみしいです。モニターに合わせてボタンのサイズを変化させるとかはできるのかしら…。

マナビーくんぽい感じをベースに、少しポップに丸くかわいい雰囲気を目指します。

スカイボックスを消す

空と奈落を消します。
MainCameraのCameraコンポーネントにあるEnvironmentで、Background Typeをskyboxからsolidに。背景色を設定できる箇所が表示されるので、とりあえず#FFF7EEを入れてみます。

?イメージと違う……。なんか汚くない……?

Global VolumeオブジェクトのVolume>Tonemapping>ModeをNoneに変更したところ、イメージ通りになりました。これでいいのかは謎。トーンマッピングって3Dで使うなんやかんやだよね……いいのか?

ゲームビューは色が変わりましたが、シーンビューはスカイボックスが表示されたままです。このままでも問題ないのですが、一応こちらからも色チェックがしやすいように背景色を設定します。
シーンビューの上のほうにあるキラキラ印付きレイヤーみたいなアイコンの三角をクリックし、Skyboxのチェックを外します。

Edit>Preferencesを開き、

Colors>Scene>Backgroundの色を#FFF7EEに変更します。

シーンビューも色が変更されました。

べた塗り画面ですがシーンビューでは3D操作が必要です。このまま視点をぐるっと変えたら一瞬で迷子になって上も下もわからず何も見えない恐怖を味わいました。
先ほど外したSkyboxのチェックを入れなおして、必要なときにだけSkyboxを消すことにします。
何かオブジェクトを置けば迷子にならなくなるかも。

フォントを変更

今回はRounded M+を使用します。太さはいろいろ試した末、Rounded-Lの1cからRegular・Bold・Blackの3種、2mからboldを入れました。(Blackは結局使用していませんが…)
プロジェクトビューのAssets直下にFontsフォルダを作って、配布サイトからDLした.ttfファイルを格納します。

Window>TextMeshProからFont Asset Creatorを選択。

今回は下記画像のように設定しました。Custom Character Listには利用したい文字を入力します。基本的な文字はこちらから、難しい漢字はこちらから拝借して入力しました。

最後にGenerate Font Atlasをクリックするとめちゃくちゃ時間がかかったのちUnity用のフォントデータができあがります。Saveを押してFontsフォルダに保存して完了です。

本当に時間がかかったので時計に使うつもりのフォントは時計に使う文字だけ入力しました。

できたら既存のテキストオブジェクトのフォントを変更します。
ClockTextのTextInputに適当な文字を入力し、Font Assetに作成したフォントデータをセットします。ついでにVertex Colorをオレンジに変更しました。

文字が表示されていればOK。

半角数字と:しか作っていないフォントは、それ以外の文字は四角になります。

数字以外もちゃんと作ったはずなのに見たことない文字化けしてるフォントもありました。なんだそれ何の文字

プロジェクトの該当フォントを選択し、インスペクターからAtlas Population ModeをDynamicにしたら直りました。

ボタンを画像にする

再生ボタン等を画像にします。画像は適当に描きました。あとで差し替えるんだたぶん
画像ファイルを準備したら、Unityプロジェクトに入れて設定を行います。何をしているのかはよく知らんけど……

Assetsフォルダの直下にSpritesフォルダを作成し、ボタンの画像をドラッグ&ドロップ。
Spriteフォルダに入った画像をすべて選択し、インスペクターのTexture TypeでSprite(2D and UI)を選択。Sprite ModeでSingleを選択して、Applyします。1枚目の画像はSprite Mode変更を忘れています。

これで画像自体の設定は完了。
次は実際のボタンを画像に差し替えます。ヒエラルキーでNextButtonを選択し、インスペクターのImage>Source imageにAssets/Sprite内の指定したい画像をドラッグ&ドロップします。他のボタンも同じように配置します。

もともとボタンに表示していた文字は不要なので、削除します。
NextButtonTextを選択し、インスペクターのTextMeshPro-Text(UI)内のText Inputに入っている文字を消します。NextButtonTextオブジェクトごと消してもいいのかなとも思いましたがよくわからんのでとりあえず残しておきました。

ゲームビュー(もしくはシーンビュー)を見ると、Nextボタンが画像になっています。

他のボタンも同じように画像にします。ついでに時計の文字サイズも大きくしました。これで画面は完成かもー

Playボタンはとりあえず三角の再生の画像のほうを入れておきました。クリックしたら縦線二本の一時停止の画像に切り替わるようにしたいので、あとで切り替えられるようにスクリプトを書き直します。

Manualボタンは構造をすこしいじって、ManualPlayButtonの中にHandleとText×2を入れました。
ManualPlayButtonの背景画像に薄い青の背景。HandleをImageオブジェクトで作って(ヒエラルキーの空いてる場所を右クリック→UI(Canvas)→Imageで作成)、背景画像に濃い青の画像。Textのほうはボタンにもともとついていたテキストをコピペし2つにして横に並べています。

DOTweenをインポート

Manualボタンをクリックしたら青い画像が横にずれてAutoとManualが切り替わるようにしたいです。
オブジェクトを動かすためにDOTweenというツールを使います。DOTween はアニメーションを簡単に実装できるようにしてくれるらしいです。
こちらからDOTweenをアセットに追加します。追加するとUnityで開くボタンが出てくるのでクリック。
Unityでなんか開くので、ダウンロードをクリックします。

ボタンがImportに変わったらクリックして、出てきた画面でImport。

Open DOTween Utility PanelしてSetup DOTween…を選べと言われますので、Open DOTween Utility Panelをクリック。

Setup DOTween…を押したら何もせずそのままApply。

スクリプトを書き換える

UIManagerスクリプトをいじって画像の切り替え等を実装します。

あまり関係ないけど存在チェックの回数が多いので存在チェック用の共通関数を追加しました。前にやったっけ?全然覚えてない みんなそういうのどうやって覚えてるんだ?

// Asset/scriptの中にUnityUtilsスクリプトを追加
using UnityEngine;

public class UnityUtils : MonoBehaviour
{
    public static bool IsMissingAndLog(Object obj, string name, Object caller)
    {
        if (obj == null)
        {
            Debug.LogWarning($"[{caller.GetType().Name} on {caller.name}] {name} がありません。");
            return true;
        }
        return false;
    }

}

UIManagerに変数を3つ追加します。ここに画像などを設定していく予定。_playButtonTextと_manualPlayButtonTextは不要ですので後ほど削除します。

// UIManager

public class UIManager : MonoBehaviour
{
    [SerializeField] private Button _playButton;
    [SerializeField] private Button _nextButton;
    [SerializeField] private Button _prevButton;
    [SerializeField] private Button _manualPlayButton;  // 追加

    [SerializeField] private Sprite _playSprite;  // 追加
    [SerializeField] private Sprite _pauseSprite; // 追加

    [SerializeField] private TextMeshProUGUI _clockText;
    
    [SerializeField] private TextMeshProUGUI _playButtonText;   // あとで削除
    [SerializeField] private TextMeshProUGUI _manualPlayButtonText; // あとで削除
    
    // 以下略

void Awakeの中で画像の存在チェックができるように内容を変更します。テキストのチェックは不要なので削除。あとUtilの関数を使用するように変更しました。

private void Awake()
{
	UnityUtils.IsMissingAndLog(_playButton, nameof(_playButton), this);
	UnityUtils.IsMissingAndLog(_nextButton, nameof(_nextButton), this);
	UnityUtils.IsMissingAndLog(_prevButton, nameof(_prevButton), this);
	UnityUtils.IsMissingAndLog(_manualPlayButton, nameof(_manualPlayButton), this);
	UnityUtils.IsMissingAndLog(_manualPlayButtonHandle, nameof(_manualPlayButtonHandle), this);

	UnityUtils.IsMissingAndLog(_playSprite, nameof(_playSprite), this);
	UnityUtils.IsMissingAndLog(_pauseSprite, nameof(_pauseSprite), this);

	UnityUtils.IsMissingAndLog(_clockText, nameof(_clockText), this);
}

BGMの再生・停止が切り替わったときに呼ばれる関数HandleBgmStateChangedを修正し、再生・一時停止ボタンの画像を切り替えられるようにします。

// UIManager.cs

// BGM再生状態変更時
private void HandleBgmStateChanged(BgmState state)
{
	if (UnityUtils.IsMissingAndLog(_playButton, nameof(_playButton), this)) return;
	if (UnityUtils.IsMissingAndLog(_playSprite, nameof(_playSprite), this)) return;
	if (UnityUtils.IsMissingAndLog(_pauseSprite, nameof(_pauseSprite), this)) return;

	// ボタンのImageコンポーネントを取得
	Image img = _playButton.GetComponent<Image>();

	// 画像差し替え
	if (state == BgmState.Playing) img.sprite = _pauseSprite;   // 再生中 
	if (state == BgmState.Paused) img.sprite = _playSprite;     // 一時停止中
}

トグルをアニメーションさせるスクリプトToggleAnimator.csを作成します。

// ToggleAnimator.cs

using UnityEngine;
using DG.Tweening; // DOTween

public class ToggleAnimator : MonoBehaviour
{
    [SerializeField] private RectTransform handle;
    [SerializeField] private float duration = 0.25f;    // アニメーション時間
    public bool IsOn { get; private set; } = false;     // トグルの値


    public void SetState(bool value, bool animate = true)
    {
        IsOn = value;
        float targetAnchorX = IsOn ? 1f : 0f;   // ONなら右端、OFFなら左端(1⇔0)
        float time = animate ? duration : 0;    // アニメーション時間

        handle.DOAnchorMin(new Vector2(targetAnchorX, 0.5f), time); // アンカーを端に寄せる
        handle.DOAnchorMax(new Vector2(targetAnchorX, 0.5f), time); 
        handle.DOPivotX(targetAnchorX, time);   // 基準点も端に寄せる
        handle.DOAnchorPos(Vector2.zero, time); // 座標を0に
    }

handle.DOAnchorMinhandle.DOAnchorMaxは、画像を範囲指定でびよんと伸ばして配置したいときに座標をずらして使うものらしいです。

トグルの初期配置時にアニメーションしてほしくないので、BgmControllerのイベントの定義を書き直して、初期値なのか通常のモード変更なのかを判別できるようにします。
OnBgmStateChangedは変更不要だったけどなんかむずむずしたので書式だけ合わせちゃった

// BgmController.cs

    // イベント
    public delegate void BgmStateChangedHandler(BgmState state);                        // BGM再生状態変更イベント
    public delegate void BgmModeChangedHandler(BgmMode mode, bool isInitial = false);   // BGMモード切替イベント <BgmMode, 初期値かどうか>

    public static event BgmStateChangedHandler OnBgmStateChanged;
    public static event BgmModeChangedHandler OnBgmModeChanged;

できたらトグルを動かします。UIManager.csHandleBgmModeChangedを書き換えます。

// UIManager.cs

    // BGMモード変更時
    private void HandleBgmModeChanged(BgmMode mode, bool isInitial)
    {
        if (UnityUtils.IsMissingAndLog(_nextButton, nameof(_nextButton), this)) return;
        if (UnityUtils.IsMissingAndLog(_prevButton, nameof(_prevButton), this)) return;
        if (UnityUtils.IsMissingAndLog(_manualPlayButton, nameof(_manualPlayButton), this)) return;

        // BGMモードのトグルボタン切替
		bool isManualMode = (mode == BgmMode.UserSelect);
        
        bool animate = !isInitial;
        _manualPlayButton.SetState(isManualMode, animate);

        // ManualModeのときだけ曲送りのボタンを有効化
        if (_nextButton != null) _nextButton.interactable = isManualMode;
        if (_prevButton != null) _prevButton.interactable = isManualMode;
        
    }

なんかよく見るとManualモードのときのモード名?がManualModeだったりUserSelectだったりふらふらしています…。ManualModeに統一します。

// UIManager.cs
private void HandleBgmModeChanged(BgmMode mode, bool isInitial)
    {
		// BGMモードのトグルボタン切替
		bool isManualMode = (mode == BgmMode.ManualMode);
	}
// BgmController.cs

public enum BgmMode{ Auto, Manual }  // BGMモード

public class BgmController : MonoBehaviour
{
	public void ToggleBgmMode()
	{
	    BgmMode nextMode = (_bgmMode == BgmMode.Manual) ? BgmMode.Auto : BgmMode.Manual;
	    // 以下略
	}
	
	private bool IsBgmModeSelect => _bgmMode == BgmMode.Manual;
}

UIManagerの不要な変数は削除しておきます。

// UIManager.cs

// 削除
[SerializeField] private TextMeshProUGUI _playButtonText;   
[SerializeField] private TextMeshProUGUI _manualPlayButtonText;

スクリプトは完成!Unityに戻ります。
ヒエラルキーでManualPlayButtonを選択し、インスペクターのAdd ComponentでToggle Animatorを検索して追加。追加したToggle AnimatorのHandleの枠にヒエラルキーのManualPlayButtonHandleをドラッグ&ドロップします。

ヒエラルキーでUIManagerオブジェクトを選択し、Manual Play Buttonの中にヒエラルキーのManualPlayButtonをドラッグ&ドロップ。Play SpriteとPause Spriteの中にプロジェクトのAssets/Spritesフォルダの画像をラッグ&ドロップします。

Unity側で保存したら完成。ゲーム再生ボタン(音楽の再生ボタンではない)をクリックして動作確認します。動いてればOK!(音は消してあるよ)

スクリプトをいろいろ書いたからこれからはスクリプトお休みして画像描いたり画面をいじっていこうーと思いながらはじめたのになぜかスクリプトを書いていました。画像配置ってスクリプトつかうんだなあ

次回のUnityいじりはちょっとした3Dオブジェクトを置けたらいいなと思っていますが、最近元気がなくて集中力皆無なのでなにかUnityとは別にリラックスしてる系のことをやりたいです……とはいえ回復傾向にあるのですぐに気が変わるかもしれませんが

  • 0
  • 0
  • 0

きやけ

いろんなものをちょっとずつかじって、結局よくわからない人です。ときどきなにかをつくります。

作者のページを見る

寄付について

「novalue」は、‟一人ひとりが自分らしく働ける社会”の実現を目指す、
就労継続支援B型事業所manabyCREATORSが運営するWebメディアです。

当メディアの運営は、活動に賛同してくださる寄付者様の協賛によって成り立っており、
広告記事の掲載先をお探しの企業様や寄付者様を随時、募集しております。

寄付についてのご案内