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

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

新規登録して質問してみよう
ただいま回答率
85.48%
マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

329閲覧

[ゲーム開発] 非同期のアセットのロードで同じファイルがロードされない仕組みを作りたい

samidare_chan

総合スコア15

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2024/01/02 03:47

編集2024/01/03 04:53
質問内容

提示コードですが非同期処理を用いて、アセットを非同期でロードする処理なのですが現状3つの画像がそれぞれ順番にロードされてしまう原因が知りたいです。
提示コードの非同期版のコードでは同じ画像が来た時待機するという処理を行っているのですが、なぜか同期版と同じ秒数処理時間がかかっているため同期処理と同じになって
いると考えられます。

調べたこと

非同期版と同期版両方のプログラムを記述して実行時間を計測

知りたいこと

1,アセットのロードで同じファイルがロードされないようにしながら非同期で行いたい。
2,そもそもどの非同期機能を使ってアセットロードを実装するのが定番なのか知りたいです。

提示コードについて

提示コードはコンポーネント指向を用いています。Componentを継承して非同期で行うアセットロード処理を作り、それをGameObject(コンポーネントをの挙動を管理する)で
処理をまとめて、更に上のSceneで全部のGameObject の非同期処理を一つにまとめ、最後に全部が終わるまで待機してからUpdate()等を実行するという処理内容です。

コンソール
new sprite found sprite new sprite 2000
ソースコード

cpp

1#include <vector> 2#include <future> 3#include <iostream> 4#include <glm/glm.hpp> 5#include <thread> 6#include <mutex> 7#include <condition_variable> 8#include <chrono> 9#include <random> 10#include <thread> 11 12struct Sprite 13{ 14 int handle; 15 std::string path; 16}; 17 18 19std::vector<Sprite> sprites; //ロードした画像リスト 20std::vector<std::string> nowLoad; //現在ロード中の画像リスト 21std::mutex loadMutex; 22std::condition_variable loadCV; 23 24 25/*############################################################## 26# 乱数取得 27##############################################################*/ 28int GetRandom(int rand) 29{ 30 // 現在の時間を取得して種(seed)として使用する 31 auto now = std::chrono::high_resolution_clock::now(); 32 auto seed = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count(); 33 34 // 乱数生成器を初期化 35 std::mt19937_64 generator(seed); 36 37 // ミリ秒単位の乱数を生成 38 std::uniform_int_distribution<int> distribution(0, rand); 39 int random_number = distribution(generator); 40 41 //std::cout << "ミリ秒単位の乱数: " << random_number << std::endl; 42 43 return random_number; 44} 45 46/*############################################################## 47 48# 非同期 版 49 50# スプライトロード関数  51※ テスト用のためロード時間を乱数でミリ秒待機 52##############################################################*/ 53Sprite LoadSprite(const char* filePath) 54{ 55 std::unique_lock<std::mutex> lock(loadMutex); 56 57 // 他のスレッドが同じ画像をロード中であれば待機 58 loadCV.wait(lock, [&filePath]() 59 { 60 for(const std::string& path : nowLoad) 61 { 62 if (path == std::string(filePath)) 63 { 64 return false; 65 } 66 } 67 68 return true; 69 }); 70 71 //スプライトリストから取り出す 72 for (const Sprite& s : sprites) 73 { 74 if (s.path == std::string(filePath)) 75 { 76 std::cout << "found sprite" << std::endl; 77 return s; 78 } 79 } 80 81 std::cout << "new sprite" << std::endl; 82 83 // ロード中の画像リストに追加 84 nowLoad.push_back(std::string(filePath)); 85 lock.unlock(); // ロックを解除して他のスレッドがロードを開始できるようにする 86 87 //画像ロード 88 int milliseconds = 1000; 89 //int milliseconds = GetRandom(1000); 90 std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); 91 92 93 lock.lock(); // ロックを再取得して安全に状態を更新する 94 95 96 //ロードが終わったためリストから削除 97 int i = 0; 98 for(const std::string& s : nowLoad) 99 { 100 if(s == filePath) 101 { 102 break; 103 } 104 105 i++; 106 } 107 nowLoad.erase(nowLoad.begin() + i); 108 109 //ロード済みスプライトリストに追加 110 sprites.push_back(Sprite{ milliseconds, std::string(filePath) }); 111 112 // 画像のロードが完了したことを通知 113 loadCV.notify_all(); 114 115 return sprites.back(); 116 117 118} 119 120/*############################################################## 121 122# 同期 版 123 124# スプライトロード関数  125※ テスト用のためロード時間を乱数でミリ秒待機 126##############################################################*/ 127Sprite Sync_LoadSprite(const char* filePath) 128{ 129 //スプライトリストから取り出す 130 for (const Sprite& s : sprites) 131 { 132 if (s.path == std::string(filePath)) 133 { 134 std::cout << "found sprite" << std::endl; 135 return s; 136 } 137 } 138 139 std::cout << "new sprite" << std::endl; 140 141 //画像ロード 142 int milliseconds =1000; 143 //int milliseconds = GetRandom(1000); 144 std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); 145 146 147 //ロード済みスプライトリストに追加 148 sprites.push_back(Sprite{ milliseconds, std::string(filePath) }); 149 150 return sprites.back(); 151 152 153} 154 155 156 157 158 159/*############################################################## 160# Component 基底 161##############################################################*/ 162class Component 163{ 164public: 165 166 Component() 167 { 168 169 } 170 171 virtual void (*PreLoad())() 172 { 173 return []() -> void 174 { 175 std::cout<<"component"<<std::endl; 176 }; 177 } 178 179}; 180/*############################################################## 181# GameObject 基底 182##############################################################*/ 183class GameObject 184{ 185public: 186 GameObject() 187 { 188 189 } 190 191 std::vector<std::future<void>> getPreLoad() 192 { 193 std::vector<std::future<void>> futures; 194 195 for(Component *c : components) 196 { 197 futures.push_back(std::async(std::launch::async,c->PreLoad())); 198 } 199 200 return futures; 201 } 202 203 void Update() 204 { 205 206 } 207 208 void Render() 209 { 210 211 } 212 213 std::vector<Component*> components; 214}; 215 216/*############################################################## 217# Scene 基底 218##############################################################*/ 219class Scene 220{ 221public: 222 223 std::vector<GameObject*> gameObjects; 224 void Preaload() 225 { 226 std::vector<std::future<void>> futures; 227 for(GameObject *g : gameObjects) 228 { 229 for(std::future<void> &f : g->getPreLoad()) 230 { 231 futures.push_back(std::move(f)); 232 } 233 } 234 235 for(std::future<void> &f : futures) 236 { 237 f.wait(); 238 } 239 } 240 241}; 242 243/*############################################################## 244# Control コンポーネント 245##############################################################*/ 246class Control : public Component 247{ 248public: 249 Control(): Component() 250 { 251 252 } 253 254 void (*PreLoad())() override 255 { 256 return []() ->void 257 { 258 259 260// 同期 261/* 262 auto startTime = std::chrono::high_resolution_clock::now(); 263 264 Sprite sp1 = Sync_LoadSprite("tile1.jpg"); 265 Sprite sp2 = Sync_LoadSprite("tile1.jpg"); 266 Sprite sp3 = Sync_LoadSprite("tile2.jpg"); 267 268 auto endTime = std::chrono::high_resolution_clock::now(); 269 auto time = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime); 270 271 std::cout<<time.count()<<std::endl; 272*/ 273 274//非同期 275 auto startTime = std::chrono::high_resolution_clock::now(); 276 277 Sprite sp1 = LoadSprite("tile1.jpg"); 278 Sprite sp2 = LoadSprite("tile1.jpg"); 279 Sprite sp3 = LoadSprite("tile2.jpg"); 280 281 auto endTime = std::chrono::high_resolution_clock::now(); 282 auto time = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime); 283 284 std::cout<<time.count()<<std::endl; 285 286 287 288 289 290 291 292 }; 293 } 294}; 295 296/*############################################################## 297# Player ゲームオブジェクト 298##############################################################*/ 299class Player : public GameObject 300{ 301public: 302 Player(): GameObject() 303 { 304 components.push_back(new Control()); 305 } 306}; 307 308/*############################################################## 309# Game シーン 310##############################################################*/ 311class Game : public Scene 312{ 313public: 314 Game(): Scene() 315 { 316 gameObjects.push_back(new Player()); 317 } 318}; 319 320 321 322 323int main() 324{ 325 326 Game *g = new Game(); 327 328 g->Preaload(); 329 330 331 332 return 0; 333}

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

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

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

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

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

maisumakun

2024/01/02 06:18

どのようなシチュエーションでしょうか? (「最初のロードは同期処理で済ませて、その後は非同期で同じ画像を使い回す」では支障が出る状況なのですか?)
samidare_chan

2024/01/03 04:53

そうですが、ゲーム開発なのですがアセットのロードは最初にまとめて行うとう処理が定番と聞いているのですが実際どうなのでしょうか?
guest

回答1

0

ベストアンサー

ゲーム開発なのですがアセットのロードは最初にまとめて行うとう処理が定番と聞いているのですが実際どうなのでしょうか?

そうですね、ゲームのステージ中にロード処理が入ってしまうとゲーム体験を著しく損ねかねませんので、ステージが始まる前に必要な素材はすべてロードしておくのが妥当です。

投稿2024/01/03 08:46

maisumakun

総合スコア145184

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問