Unityで画像をソース上で動的に切り替える方法を色々試したけど、縮尺が変になる挙動が直らない

# Unity2019.3.0.a2と2018.4.0f1

2Dの話です。

## 画像が歪む

まずは画像を見てもらえると分かるのですが盾の画像がおかしなことになっています。
4つの盾は本来みんな同じ画像です。


2019.3.0.a2
![unity_tate_2019.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74504/64c95ec1-fa5b-f9b5-2e94-f167b98f443a.png)


## 仕様
盾を表示するロジックとしては画像下部の装備欄をクリックするとアイテム欄(CardBoardという名前をつけています)が出てきて、選択すると装備欄に反映されます。
![selected_card.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74504/33976293-f3a3-f888-bf7d-1ae2ff834efc.png)


ですが、この装備を何度か繰り返すと装備欄の画像の縮尺が変になってしまう不具合です。

## 原因調査
当然、ロジックがおかしいのか暗黙的に数値が変わっているのかと思いましたのでそのあたりを色々と試しました。

## 階層
動的に画像を切り替えるために以下のような階層にしました。

```text
CanvasUI
  EquipmentCardField
    EquipmentCardBox0
      ImageInvisibleSprite
    EquipmentCardBox1
      ImageInvisibleSprite
    EquipmentCardBox2
      ImageInvisibleSprite
    EquipmentCardBox3
      ImageInvisibleSprite
```

CanvasUIが画面全体のCanvasです。
EquipmentCardFieldが画像下部の装備欄です。
EquipmentCardBox0,1,2,3が画像の下地になっているImageです。
ImageInvisibleSpriteはImageですがアルファ値をいじって透明になっています。画像はemptyという真っ白なものを初期でセットしています。

基本的にはImageInvisibleSpriteの画像emptyを別な画像に切り替えてアルファ値を1.0にして透明じゃなくす方法を取っています。


## 動的に画像を変更する
### 直接画像ファイルを読み込んで画像を変更する

```C#
EquipmentCardBox equipmentCardBox = _equipmentCardBoxs[index];
equipmentCardBox.Equip(actionCard);
GameObject childGameObject = _equipmentCardBoxs[index].transform.Find("ImageInvisibleSprite").gameObject;
Sprite sprite = Resources.Load<Sprite>(equipmentCardBox.GetImageFilePath());
Image childImage = childGameObject.GetComponent<Image>();
childImage.sprite = sprite;
childImage.SetAlpha( 1.0f );
```

_equipmentCardBoxsは長さ4のEquipmentCardBoxの配列です。
EquipmentCardBoxはEquipmentCardBox0~3に一つずつ関連付けています。
equipmentCardBoxが装備を行う欄の一つになります。
.Equip()で装備を行っています。
画面に表示されている拳や盾などはCardという名称をつけています。
ActionCardはCardBoardでクリックしたCardです。

childGameObjectがImageInvisibleSpriteのGameObjectになります。
Resources.Load<Sprite>メソッドで画像ファイルのパスを指定してSpriteとして読み込んでいます。
childImage.sprite = sprite　で画像の切り替えを行っています。
その下のchildImage.SetAlpha( 1.0f );でアルファ値を切り替えています。SetAlphaは拡張メソッドです。
名前の通りアルファ値を変えるためのメソッドです。

この方式で試したところ装備欄の画像は歪みました。

### Texture2Dから新規にSpriteを生成して画像を変更する

```C#
EquipmentCardBox equipmentCardBox = _equipmentCardBoxs[index];
equipmentCardBox.Equip(actionCard);
GameObject childGameObject = _equipmentCardBoxs[index].transform.Find("ImageInvisibleSprite").gameObject;
Texture2D tex2d = Resources.Load(equipmentCardBox.GetImageFilePath()) as Texture2D;
Image childImage = childGameObject.GetComponent<Image>();
childImage.sprite =  Sprite.Create(tex2d, new Rect(0,0,tex2d.width,tex2d.height), Vector2.zero);
childImage.SetAlpha( 1.0f );
```

Resources.Load()でTexture2Dに変換してspriteの代入箇所でSprite.create()で新しいスプライトを生成しています。

この方式で試したところ装備欄の画像は歪みました。


### GameObjectのコンポーネントのspriteを代入する


```C#
EquipmentCardBox equipmentCardBox = _equipmentCardBoxs[index];
equipmentCardBox.Equip(actionCard);
GameObject childGameObject = _equipmentCardBoxs[index].transform.Find("ImageInvisibleSprite").gameObject;
Image childImage = childGameObject.GetComponent<Image>();
childImage.sprite =  actionCard.gameObject.GetComponent<Image>().sprite;
childImage.SetAlpha( 1.0f );
```
直接Resourcesフォルダの画像を読むのを止めてクリックしたCardのGameObjectからspriteを取り出して代入させてます。

この方式で試したところ装備欄の画像は歪みました。

### ScaleやSetNativeSizeを使う
```C#
childImage.rectTransform.localScale = new Vector3(1,1,1);
childImage.transform.localScale = new Vector3(1,1,1);
childImage.SetNativeSize();
```

上記の画像を切り替える方法でこれらのソースを組み合わせたパターンを試しましたがどれも同じ結果になりました。
装備欄の画像は歪みます。


### 数値を確認する
暗黙的にwidthやheight,scaleの数値が変わっているのかと思いPlayer再生中に値を確認したところ、何の変化もありませんでした。
見えてる値と実際の値が食い違っている可能性も考えてDebug.Log()で出力してみたところ、何の変化もありませんでした。

### その他
他にも色々と試しましたが割愛します。


## LTSを使ってみることに
UnityのLTSを調べてみました。一番新し目なLTSは2018.4.0f1でしたのでそこにパッケージをインポートして再作成。



## 何だか出来た。

2018.4.0f1
![unity_tate_2018.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74504/cfb834ba-fceb-06e3-944e-7483c887e1eb.png)

色々装備を変更してこねくり回しても期待どおりの動作をしました。
満足。

左のウィンドウの色が変わっているのは単純に変えたものをキャプチャしただけです

## まとめ
最新版を使うときは予期せぬ不具合があるかもしれないので気をつけたほうが良いかもしれないです。
本当は私の知らない仕様があってそういう挙動になっていしまっているのかもしれません。
私が分かったのはUnity 2018.4.0f1にしたら求めていた挙動になったということだけです。

分かりきったことではありますが、自分が詳しくないものを利用するときはLTSか安定版を使うのが無難ですね。