UnityのButtonのImageが真っ黒になる現象に詰まった話

Unity

Unityでボタンの色を変えたいだけなのに、Imageが真っ黒になる。そんな現象に遭遇したので、実際のスクリプトを交えながら原因と解決策をまとめます。


現象

  • ButtonのImage.colorをスクリプトから変更したら、意図せず真っ黒に表示される
  • 直してもPlayするとまた黒くなる

例えば、以下のように色を切り替えたかっただけなのに:

if (dodgeButton != null)
{
    var image = dodgeButton.GetComponent<Image>();
    if (image != null)
    {
        image.color = (currentMoveResource >= dodgeCost) ? dodgeAvailableColor : dodgeUnavailableColor;
    }
}

意図に反して、ボタンが真っ黒になってしまいました。
(リソースがあればボタンの色を使える色合いに、リソースがなかったら使えない色合いに変更するだけのスクリプト)


原因

ButtonコンポーネントのTransition設定が”Color Tint”になっていると、 Unity内部でボタンの状態(通常・ホバー・押下など)に応じて自動的にImage.colorを書き換える仕様になっています。

つまり、スクリプトでImage.colorを変更しても、Button側の制御で上書きされてしまうわけです。


実際のスクリプトで発生していた場所

今回この問題が起きたのは、PlayerUnitController.cs内で、 プレイヤーがダッシュ可能かを表示するために、ボタンの見た目を切り替えようとした箇所でした。

具体的には、AwakeでButtonを取得し、Update内でリソース量に応じて色を変える処理を試みていました:

void Awake()
{
    if (dodgeButton == null)
    {
        GameObject dodgeButtonObj = GameObject.Find("DodgeButton");
        if (dodgeButtonObj != null)
        {
            dodgeButton = dodgeButtonObj.GetComponent<Button>();
        }
    }
    if (dodgeButton != null)
    {
        dodgeButton.onClick.AddListener(TryDodge);
    }
}

private void Update()
{
    if (dodgeButton != null)
    {
        var image = dodgeButton.GetComponent<Image>();
        if (image != null)
        {
            image.color = (currentMoveResource >= dodgeCost) ? dodgeAvailableColor : dodgeUnavailableColor;
        }
    }
}

この時、Buttonの”Transition”が”Color Tint”のままだったため、 せっかくスクリプトで変更したcolorが、Unity内部のButton処理によってリセットされ真っ黒になっていたのです。


解決策

解決方法はシンプルです。

1. ButtonのTransitionを”None”にする

ButtonコンポーネントのInspector上で、Transitionを”None”に変更すれば、 ボタン内部の状態変化でImage.colorが勝手に上書きされなくなります。

これだけで意図した色変更が正常に反映されるようになりました。

2. もっと厳密に管理したいならImage単体で制御する

もしさらに状態管理を厳密にしたい場合、ButtonではなくImage単体でUIを構築し、 完全に自前でクリック判定+色変化を管理するのも手です。


一般化できる知見:「状態制御は一元管理すべき」

この問題から学べる本質的な教訓は、

状態を管理するコンポーネントが複数存在するとバグりやすい

ということです。

特にUnityでは、標準コンポーネント(Button, Toggle, Dropdownなど)が裏で自動制御を持っているので、 意図せず「制御競合」が起きるケースが多々あります。

解決策は、状態管理は必ずどこか1箇所にまとめること。

スクリプトで明示的に操作したいなら、標準の自動制御は切っておくべき、ということですね。


まとめ

  • UnityのButtonはTransition=ColorTintが有効だとImage.colorを勝手に変更してしまう
  • スクリプトでImage.colorをいじるなら、ButtonのTransitionは”None”にする
  • 状態制御は競合させず一元化するのがバグ防止の基本

UnityのUIバグは”仕組み”を知れば怖くない!

公開中のアプリ一覧はこちら!

実際にUnityで開発してリリース済みのアプリ一覧をまとめています。

https://syunpp.com/公開中のアプリ一覧/

コメント

タイトルとURLをコピーしました