質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

2回答

211閲覧

Unity StreamingAssetsフォルダ内のpng画像をすべて取得し、ランダムに選んでRaw画像のテクスチャに貼り付けたい

vwhite

総合スコア1

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2024/11/13 01:29

編集2024/11/13 06:41

実現したいこと

・StreamingAssetsフォルダ内のpng画像をすべて取得しリストに加えたい
・上記リストからランダムで選び出し、画面上のRawイメージの見た目を変えたい

前提

Unity、C#ともに初心者です。
検索して使えそうな要素を切り貼りでなんとか作っています。
用語についてもわからないことばかりなので、失礼ながら
「この位置にこれを置け!」くらいの具体性で回答頂けますと幸いです。

もちろんそれに限らず、解決の糸口になるものならなんでも歓迎します。
助けてください…。

StreamingAssetsを使っているのは、
ビルド後に差し替えできるようにしたいと考えているからです。

発生している問題・エラーメッセージ

・png画像をすべて取得できるはずのコードでエラーが出る。
ArgumentNullException: Value cannot be null.
Parameter name: oldValue
System.String.Replace (System.String oldValue, System.String newValue) (at <ecf276dcb8654fa5bed2c5ea1a4ad870>:0)
Test.Start () (at Assets/:::/:::/Test.cs:30)

・すべて取得できたとしても、string型のパスをtextureにする方法がわからない。

該当のソースコード

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.UI; 5using Random = UnityEngine.Random; 6using System.IO; 7 8public class Test : MonoBehaviour 9{ 10 public RawImage img; 11 string basePath; 12 13 //表示させる画像リスト 14 public List<Texture> cuts = new List<Texture>(); 15 16 void Start() 17 { 18 Random.InitState(System.DateTime.Now.Millisecond); 19 // ゲームオブジェクトのRaw画像を指定 20 img = GameObject.Find("Raw").GetComponent<RawImage>(); 21 22 // ファイルリストの取得 23 string[] files = GetStreamingAssetFiles("images", "*.png"); 24 25 // リスト一覧 26 for (int i = 0; i < files.Length; i++) 27 { 28   // 頂き物のコードで意味がわかっていない 29   // basePathの隣、""の中には何も入れなくていいのか? 30 files[i] = files[i].Replace(basePath, "");  31 cuts.Add(files); // 現状ではここは不可能 32 } 33 } 34 35 public void ChangeImage() //他のスクリプトからこれを呼び出している 36 { 37 // 表示する画像の数をランダムで算出 38 int random = Random.Range(0, cuts.Count); 39 img.texture = cuts[random]; 40 } 41 42 // すべて頂き物。下記のメソッドで、 43 //StreamingAssets内のpng画像をstringですべて取得できると考えている 44 static public string[] GetStreamingAssetFiles(string dir, string ext, bool isFullPath = true) 45 { 46 if (dir.IndexOf("/") != 0) 47 { 48 dir = "/" + dir; 49 } 50 if (dir.LastIndexOf("/") != dir.Length - 1) 51 { 52 dir = dir + "/"; 53 } 54 55 string basePath = Application.streamingAssetsPath + dir; 56 string[] files = System.IO.Directory.GetFiles(basePath, ext, System.IO.SearchOption.TopDirectoryOnly); 57 58 if (isFullPath && -1 == files[0].IndexOf(basePath)) 59 { 60 for (int i = 0; i < files.Length; i++) 61 { 62 files[i] = basePath + files[i]; 63 } 64 } 65 else if (!isFullPath && -1 < files[0].IndexOf(basePath)) 66 { 67 for (int i = 0; i < files.Length; i++) 68 { 69 files[i] = files[i].Replace(basePath, ""); 70 } 71 } 72 73 return files; 74 } 75}

試したこと

わざわざstringで取得せず、はじめからtextureで取得できないか。
byteで取得すればtextureにできるという文章も見た気がする。

そんな考えはしたけれども、実現の方法が一切わからず。

補足情報(FW/ツールのバージョンなど)

Unity 2023 2.20f1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

StreamingAssetsを利用し画像を読み込むのは、このサイトが参考になります
https://bluebirdofoz.hatenablog.com/entry/2022/07/24/001715

フォルダ内のPngをすべて読み取る場合、次のようになります。
(コード内のコメントを確認する際はコードを右にスクロールしてください。)

c#

1using System.Collections.Generic; 2using UnityEngine; 3using System.IO; 4 5 6//StreamingAssets/TestImageGroupフォルダ内のpngを取得。 7public class RawImgLoader : MonoBehaviour 8{ 9 private const string FOLDER_PATH = "TestImageGroup"; // StreamingAssetsフォルダ内のサブフォルダのパス 10 public List<Texture2D> LoadedTexturesList { get; private set; } // 読み込んだテクスチャを格納するリスト 11 12 private void Start() 13 { 14 InigializeLoadPNGs(); 15 } 16 17 // フォルダ内のPNGファイルを読み込み、テクスチャとしてリストに格納 18 private void InigializeLoadPNGs() 19 { 20 LoadedTexturesList = new List<Texture2D>(); //リスト初期化 21 string path = Path.Combine(Application.streamingAssetsPath, FOLDER_PATH);//フォルダ指定 22 23 if (Directory.Exists(path)) // ディレクトリが存在するか確認 24 { 25 string[] files = Directory.GetFiles(path, "*.png"); // 指定したディレクトリ内でPNGファイルのみを取得。メモ:検索パターンに一致するファイルを取得します。例: "*.png"でPNGファイルのみを取得。 26 foreach (string file in files) // 各ファイルを読み込み、テクスチャに変換してリストに追加 27 { 28 byte[] fileData = File.ReadAllBytes(file); // 取得したpngのバイナリデータを読み込む 29 Texture2D texture = new Texture2D(2, 2); // 新しいTexture2Dオブジェクトを作成。 30 texture.LoadImage(fileData); // 新しいTexture2Dオブジェクトに画像データを適用。 31 LoadedTexturesList.Add(texture); // 読み込んだテクスチャをリストに追加 32 } 33 Debug.Log($"<color=#88ff00>{files.Length} 枚のPNGファイルを {path} から読み込みました。</color>"); 34 } 35 else 36 { 37 Debug.LogError($"フォルダが存在しません: {path}"); 38 } 39 } 40 41} 42

シーン上のボタンから呼び出してRawImgに反映。

c#

1using UnityEngine; 2using UnityEngine.UI; 3 4 5//StreamingAssetsから画像を読み込んでImageに反映 6public class RandomRawImageDisplay : MonoBehaviour 7{ 8 public Button ChangeTexBtn; 9 public RawImage RawImage; 10 public RawImgLoader PngLoader; 11 12 private void Start() 13 { 14 ChangeTexBtn.onClick.AddListener(SetTexture);//ボタンがクリックされたときに発生するイベントをセット 15 } 16 //RawImageにテクスチャをセット 17 private void SetTexture() 18 { 19 RawImage.texture = PngLoader.LoadedTexturesList[Random.Range(0, PngLoader.LoadedTexturesList.Count)]; 20 } 21 22}

完成形イメージです。
https://drive.google.com/file/d/1MGZS2bj96Qpi5xjbIl-bTDEHl5gNQIq7/view?usp=drive_link

ご参考までにUnityパッケージもおいておきます。
https://drive.google.com/file/d/1yKRLimC3TrqOKv9dKDXZ7-VbTFPjos0W/view?usp=drive_link

蛇足になりますが、取得したテクスチャをPngとしてImageコンポーネントに反映させる場合です。
https://drive.google.com/file/d/1QC2WxJHwR8Db34mOIje7N9iRGe7A9QKr/view?usp=drive_link

これほんとややこしいですよね😅

投稿2024/11/13 09:09

編集2024/11/13 09:11
_aki__

総合スコア34

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

vwhite

2024/11/13 10:59

理想の実装ができました!本当にありがとうございます!! 参考サンプルを増やすため、以下に私が実装したコードを記載します。 私がわずかにでも迷ったことの説明も添えつつ…。 ```C# using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Random = UnityEngine.Random; public class RandomRawImageDisplay : MonoBehaviour { public RawImage RawImage; public RawImgLoader PngLoader; Animator animator; // Raw画像を変える際にアニメーションをつけた private void Start() { Random.InitState(System.DateTime.Now.Millisecond); // ランダムをよりランダムにするための一文。必要ないかもしれない this.animator = GetComponent<Animator>(); } public void SetTexture() { RawImage.texture = PngLoader.LoadedTexturesList[Random.Range(0, PngLoader.LoadedTexturesList.Count)]; this.animator.SetTrigger("***Trigger"); // アニメーションのトリガーを起動。***の名前は任意 } } ``` ボタン周りのコードを省いたのは、ボタンによってメソッドを呼び出したいわけではなかったからです。 SetTexture()のprivateの部分をpublicに変えて、外のスクリプトから呼び出して起動しています。 _aki__さんは2つのスクリプトを示してくれました。 1つめのスクリプトは、9行目の""内だけ、指定したいフォルダの名前に変更します。 私は2つのスクリプトを、どちらもヒエラルキーウィンドウに作成したRawイメージにアタッチしました。 (1つのRawイメージに2つのスクリプトをアタッチします。) そして、インスペクターのRandomRawImageDisplayの項目を開き、 「Raw Image」「Png Loader」の2つの項目に、 このスクリプトをアタッチしてあるRawイメージをヒエラルキーウィンドウからドラッグ&ドロップします。 実装は以上です。 これでも説明の不足があるとは思いますが…これから実装する方の一助になれば。 本当に助かりました。 重ね重ね、ありがとうございました。
guest

0

わざわざstringで取得せず、はじめからtextureで取得できないか。

このstringはファイルパスであり、Texture本体ではありません


そもそも、cutsにインスペクター上で直接Textureを設定すればいいので、以下で十分です。
この方法ならStreamingAssetsも使わないです。

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.UI; 5using Random = UnityEngine.Random; 6using System.IO; 7 8public class Test : MonoBehaviour 9{ 10 public RawImage img; 11 12 //表示させる画像リスト 13 //※インスペクター上で設定 14 public List<Texture> cuts = new List<Texture>(); 15 16 public void ChangeImage() //他のスクリプトからこれを呼び出している 17 { 18 // 表示する画像の数をランダムで算出 19 int random = Random.Range(0, cuts.Count); 20 img.texture = cuts[random]; 21 } 22}

ただ、これだとフォルダの中身を全て設定するのが面倒です。
なので、「StreamingAssetsの中にあるフォルダの中身のファイルパスを全て受け取り、それから読み込む」ということを行うためにそのようなことをしているのでしょう。
(とはいえ、この程度であればエディタ拡張を使えばいいのではないか、とは思います)


以下、stringでファイルパスを受け取る場合の話です。

C#

1 files[i] = files[i].Replace(basePath, "");

上記のReplace()basePath""に置き換える、すなわちfiles[i]からbasePathを削除するという意味です。
したがって、""となっているのは正しいです。

ここでエラーになる原因は""ではbasePathのほうです。
11行目でstring basePath;と定義されていますが、初期値は何もない(この場合nullになる)ですし、GetStreamingAssetFiles()は内部(55行目)で独自に定義しているため、エラーの行に至るまでnullのままになっています。

そもそもGetStreamingAssetFiles()の中に以下の記述があります。

C#

1 if (isFullPath && -1 == files[0].IndexOf(basePath)) 2 { 3 // (中略) 4 } 5 else if (!isFullPath && -1 < files[0].IndexOf(basePath)) 6 { 7 for (int i = 0; i < files.Length; i++) 8 { 9 files[i] = files[i].Replace(basePath, ""); 10 } 11 }

これは、isFullPath = falseの場合にエラー発生行と同じ処理を行うことになっており、以下のコードで済ませばいい話です。

C#

1 // 23行目を変更 2 string[] files = GetStreamingAssetFiles("images", "*.png", false);

さらに、cuts.Add(files);も、stringのファイルパスとTextureは別物なので、正しく動作しないでしょう。
ファイルパスからTextureとして読み込む方法が必要です。
この変更方法としては、以下が考えられます。

投稿2024/11/13 05:31

編集2024/11/13 07:26
fiveHundred

総合スコア10130

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

vwhite

2024/11/13 06:38

ご回答頂きありがとうございます! ・stringで取得しているのが「ファイルへの道筋」であって「ファイルそのもの」でないこと ・basePath,""で行われていること について理解できました。 StreamingAssetsを使いたいのは、ビルド後に差し替えできるようにしたいと考えているからです。 前提に書き忘れていましたので、追記いたします。 エディタ拡張も考えましたが、こちらの形が理想だったので、実現を目指しています。 そして23行目の変更について、その部分を変えてもエラーは変わらず、 変更したうえで11行目を削除すると、 error CS0103: The name 'basePath' does not exist in the current contextのエラーが出て ゲームの再生もできなくなってしまいます。 11行目で定義することは間違いであることはわかりましたが、 その場合、どうすればbasePathを認識させられるのでしょうか?
fiveHundred

2024/11/13 07:10

エラー行(30行目)も削除してください。 また、回答にもありますが、「cuts.Add(files);」も変更する必要があるでしょう。
vwhite

2024/11/13 07:32 編集

30行目も削除したところ、無事にゲームを再生でき、 StreamingAssets内のimagesフォルダにあるpngをすべて取得できたことを確認しました! ありがとうございます。 肝心な「cuts.Add(files);」の部分ですが、 stringのpathをTextureのリストに加える方法として、 具体的なアイデアをご教示いただけないでしょうか? この部分についても色々調べたのですが、理解が追い付かずにこちらへ質問した次第です。 質問通りの方法で答えて頂ければ一番理解しやすいですが、 最終的にpngをRaw画像に反映させられれば良いので、 異なるアプローチでもご提案いただけますと幸いです。
fiveHundred

2024/11/13 07:45

Textureにした後でcutsに追加すればよろしいのですが、具体的にどこがわからないのですか?
vwhite

2024/11/13 07:50

その、Textureにする方法が…。 もしかして、簡単で常識的なことなのでしょうか。 質問にある通り、 「この位置にこれを置け!」という感じで使えるように 教えていただけませんでしょうか。
fiveHundred

2024/11/13 08:01

回答のリンク先にサンプルがあるので確認してください。
vwhite

2024/11/13 09:44

サンプルのご提示ありがとうございました。 1時間ほど格闘しましたが、できたことは26行目以降に for (int i = 0; i < files.Length; i++) { byte[i] data = File.ReadAllBytes(files[i]); } として、エラーメッセージ CS0270: Array size cannot be specified in a variable declaration (try initializing with a 'new' expression) を吐かせることだけでした。 本当に申し訳ないのですが、この機能の実装に関して、 知識も理解も全く追い付いていないのです。 File.ReadAllBytes()の説明ページには public static byte[] ReadAllBytes (string path);の一行しかなく、 1時間考えても、file[i]にはstringのpathが入っているんだから ()の位置に入れられるんじゃないの?程度の結論にしか至れません。 このスクリプト全体も、丸一日あれこれ書き換え続けた末に ようやくこれにしかならなかったのです。 ひとつの質問、かつ知識の浅すぎる素人が時間を取らせてしまうのは大変に心苦しいのですが、 「どの位置にどう置けば」File.ReadAllBytes()を正しく使えて、 結果byteで取得できたものを、 ImageConversion.LoadImage()をどう使うことでTextureに変換できるのか、 完成したコードで示していただくことはできないでしょうか。 本当にごめんなさい。 回答をご検討いただけますと幸いです。
fiveHundred

2024/11/13 10:05

> byte[i] data = File.ReadAllBytes(files[i]); byte[]は型なのですから、iは付けません。 > 「どの位置にどう置けば」 > 完成したコードで示していただくことはできないでしょうか。 厳しいこと言うようですが「理解できないからコードで示して」というスタンスだと成長しないですし、このサイトの非推奨(※)としている丸投げになる可能性もあります。 (※:https://teratail.com/help/avoid-asking ) とりあえず、_aki__さんが一連のコード全体を示していただいておりますので、そちらを参考にしてみてください。 「texture.LoadImage(fileData);」が「ImageConversion.LoadImage(texture, fileData);」になるぐらいで、私の意図した内容とほぼ同じです。
vwhite

2024/11/13 11:04

_aki__さんの回答を参考に、理想の実装をすることができました。 fiveHundredさんも丁寧なご回答をいただき、ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問