Unityで画像を外部ファイルからオブジェクトにする

こんにちは、Unity初心者の ねぎ塩 と申します
この記事では、StandaloneFileBrowserを使ってUnity(2D)で実行中に外部のファイルを開き透過された画像(png)を参照することによってオリジナルの動物タワーバトルを作ろうとした話をします
はじめて記事を書くので過不足があるかもしれないですがよろしくお願いします
環境→ Unity6, Windows11
（この記事は名工大AdventCalendar 2024に参加しています）
### 1.StandaloneFileBrowserのダウンロード
これに関しては他の記事を参照してもらった方が分かりやすいと思います
https://namiton.hatenablog.jp/entry/2022/04/20/132255
この記事に書いてあるとおり手順に沿ってやれば問題ないと思います（エラーとpngの修正）
ダウンロードしたファイルの中に入っているシーンで画像が読み込めることが確認出来たら次の手順に移ります
他の記事も参考として置いておきます https://zenn.dev/plumchang/articles/9187928bcbcf93
### 2.中身を改変してシーンをまたぐ
ここからスクリプトの中身を変えて画像の参照先を保存します
ダウンロードしたファイルに入っているSampleのフォルダの中のCanveaSampleOpenImageのスクリプトをコピーしてOpenImage1とかにスクリプト名を変更し、編集していきます
編集していくのは下の方です
```:OpenImage1.cs
    private IEnumerator OutputRoutine(string url) {
        var loader = new WWW(url);
        yield return loader;
        output.texture = loader.texture;
    }
```
の部分を
```:OpenImage1.cs
    public static string ImageURL1; //ここと
    private IEnumerator OutputRoutine(string url1) {
        var loader = new WWW(url1);
        ImageURL1 = url1; //ここ
        yield return loader;
        output.texture = loader.texture; //あと全部のurlもurl1に変える
    }
```
のように付け加えます
こんなかんじでstaticを使っておくことによってスクリプト間の値(ImageURL1)の受け渡しが可能になります

**ここで画像が保存できるか確かめてみましょう**
まずSaveImageというスクリプトを作り以下の内容を加えます
```:SaveImage.cs
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using SFB; //ここに書いてあるものはOpenImage1から引っ張てきただけなので必要無いものも混ざっています

public class SaveImage : MonoBehaviour
{
    public RawImage image1;//画像を表示するためのもの
    
    public void OnClick()//ボタンをクリックすると画像が映される
    {
        StartCoroutine(Save1());
    }
    private IEnumerator Save1()
    {
        Debug.Log(OpenImage1.ImageURL1);
        var loader1 = new WWW(OpenImage1.ImageURL1); //「スクリプト名.変数名」で参照できる
        yield return loader1;
        image1.texture = loader1.texture;
    }
}
```
次に新しいシーンを二つ作ります（ここでは「Select」と「Save」にします）
そして二つのシーンにそれぞれ適当にRawImageとButtonを一つずつ作り、「Select」の方のボタン
にはOpenImage1を、「Save」にはSaveImageをアタッチし、スクリプトのimage1にRawImageを入れ、シーン間を移動できるようにします
【Selectシーン】
![スクリーンショット 2024-12-23 150717.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967038/1b668d1c-b19a-7632-be57-993aaea0fe92.png)
【Saveシーン】
![スクリーンショット 2024-12-23 151741.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967038/5c36264f-a366-5219-8599-cece5328a0cb.png)


**実行してみましょう**
Selectで画像を選択したあとSaveに移りボタンを押すとSelectで選んだ画像が表示されるはずです
やったー！![スクリーンショット 2024-12-23 152513.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967038/13bac3b7-a151-cf97-9ccc-228bbfd082eb.png)
![スクリーンショット 2024-12-23 152534.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967038/739083a4-847d-16f7-0245-20d9d86e4e64.png)

### 3.保存した画像のtexture型をsprite型にする
今はテスト段階でRawImageに表示しましたが、動物タワーバトルのように画像によって当たり判定をつけたい場合はsprite型に変える必要があります
なので先ほどのSaveImageスクリプトに変更を加えます
```: SaveImage.cs
public class SaveImage : MonoBehaviour
{
    private Texture2D tex1; //一時的なtextureの保存
    public static Sprite spr1; //spriteは他のスクリプトに移して使う
    public void OnClick()
    {
        StartCoroutine(Save1());
    }
    private IEnumerator Save1()
    {
        Debug.Log(OpenImage1.ImageURL1);
        var loader1 = new WWW(OpenImage1.ImageURL1);
        yield return loader1;
        tex1 = loa1.texture;
        spr1 = Sprite.Create(tex1, new Rect(0, 0, tex1.width, tex1.height), new Vector2(0.5f, 0.5f));
    }
]
```
ここで大事なのがSprite.Create関数です　texture型をsprite型に変換できます
あとspr1をstaticにしておくことでほかの関数に渡せるようにします（複数のspriteを保存して見やすくするためにこうしているだけです）
### 4.画像をオブジェクトにして落とす
書きたかったことの一番のところです
Resourcesからの画像取得ではなく実行中に外部から画像を取り入れるので、オブジェクトを作るときにすべてのコンポーネントをスクリプト内で書く必要があります
例えば、Rigidbody2Dを加えたいときは オブジェクト名.AddComponent<Rigidbody2D>(); でできます
ということで新しいスクリプトを作って整理します
```: FallObject.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FallObject : MonoBehaviour
{
    public GameObject obj; //原本オブジェ
    private GameObject geneObj; //生成オブジェ
    private void Start()
    {
        Original();
    }
    public void OnClick()
    {
        geneObj = Instantiate(obj, new Vector2(0, 3), Quaternion.identity); //objから複製
    }
    private void Original()
    {
        obj = new GameObject();
        obj.AddComponent<SpriteRenderer>();
        SpriteRenderer sprd = obj.GetComponent<SpriteRenderer>();
        sprd.sprite = SaveImage.spr1;
        obj.AddComponent<PolygonCollider2D>();
        obj.AddComponent<Rigidbody2D>(); 
    }
}
```
（説明めんどくさい）
重要なところだけ説明すると PolygonCollider2D で画像に沿った当たり判定が付きます（透過されてない画像だと四角のままです）
このスクリプトは Save シーンに新しくボタン（Objectボタン）を作りアタッチします
あと座標 Y:-3 くらいにステージを用意します（BoxCollider2Dをつける）
**テストする前にもうちょっと準備をします**
Saveシーンに作ってあるSaveボタンをSelectシーンに移します
**実行してみましょう**
先ほどと同じようにSelectから画像選択、Save、シーン移動するとオブジェクトが出現していると思います
あとオブジェクトボタンを押すと出るようになってます（連打するとめっちゃ重くなります）
**できました！！**
実行中にInspectorの画面から位置を移動させたり回転したら分かりやすいと思います。
### 余談１
Unity6以外の環境で試していないのでわかりませんが、Unity6でやってる人はさっきからConsoleに黄色の警告が出ていると思います
```
warning CS0618: 'WWW' is obsolete: 'Use UnityWebRequest, a fully featured replacement which is more efficient and has additional features'
```
↑こんなかんじの警告文
この警告は「WWW関数は古くて危ないから、新しいUnityWebRequest関数を使ってね～」という意味です
UnityWebRequest関数の細かい所は省きますが、調べてもらって書き直しをすれば警告は消えます　↓公式のやつ
https://docs.unity.cn/ja/2020.3/Manual/UnityWebRequest.html
ですがテスト段階では特に正常に作動しているのでやらなくてもいいかもしれないです
### 余談２
自分はこれを複数のスクリプトをOpenImage10まで作ってSaveImageで１～１０まで保存、そこからその１０個の画像をランダムに出現させて動物タワーバトルを作ろうとしてましたが結局完成しませんでした（泣）
あとなんかWebGLにするとファイルが入れれなくてうまくいかなかったので諦めました
（やる気を取り戻したら作り直します）
ですが一つのオブジェクトでタワーバトルすることはできたので実現は可能だと思います
今回、ゲームシステムの単純さからタワーバトルを選びましたがスイカゲームなんかも作れると思います
この技術を使ったゲームが増えていくことを楽しみにしています^^
ここまで読んでくださりありがとうございました