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

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

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

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

DirectX

DirectX(ダイレクトエックス)は、 マイクロソフトが開発したゲーム・マルチメディア処理用のAPIの集合です。

Q&A

解決済

2回答

2038閲覧

DirectX 同じシーン内でほかのオブジェクトにアクセスしたい

piced

総合スコア60

C++

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

DirectX

DirectX(ダイレクトエックス)は、 マイクロソフトが開発したゲーム・マルチメディア処理用のAPIの集合です。

1グッド

0クリップ

投稿2021/08/07 06:55

#経緯
個人でゲーム制作をしています。
自分の設計では少し問題があるように感じたので質問をしました。

#やりたいこと
あるオブジェクトから同一シーン内にあるほかのオブジェクトにアクセスしたいが
オブジェクトから目的のオブジェクトにアクセスする方法がわからない
#質問
例えば、下のヘッダーにあるTestSceneクラスにあるplayerオブジェクトから
gridオブジェクトへスマートにアクセスするにはどうしたらいいでしょうか。

#設計、ヘッダー
イメージ説明

C++

1//SceneManager.h 2#pragma once 3 4#include "BaseScene.h" 5#include "Scene_Test.h" 6#include "Scene_Title.h" 7#include "Scene_Game.h" 8#include "Scene_Result.h" 9 10class SceneManager 11{ 12public: 13 enum SceneType 14 { 15 Null = -1, //切り替え先のシーンが決まっていないとき 16 Test, 17 Title, 18 Game, 19 Result, 20 21 SCENE_NUM 22 }; 23 24private: 25 SceneType m_currentScene; //現在のシーン 26 SceneType m_nextScene; //切り替え先のシーン 27 IScene *m_scenes[SCENE_NUM]; //各シーンを一元管理する配列 28 29 //シーンの実体 30 TestScene scene_test; //デバッグ用 31 TitleScene scene_title; //タイトル 32 GameScene scene_game; //ゲーム本編 33 ResultScene scene_result; //リザルト 34 35public: 36 void Init(); //マネージャ初期化処理 37 void Update(); //マネージャ更新処理 38 void ChangeScene(SceneType); //シーンを切り替え 39 40 void InitScene(); //現在のシーンの初期化処理を行う 41 void UninitScene(); //現在のシーンの終了処理を行う 42 void UpdateScene(); //現在のシーンの更新処理を行う 43 void DrawScene(); //現在のシーンの描画処理を行う 44};

C++

1//TestScene.h 2#pragma once 3#include "BaseScene.h" 4#include "Player.h" 5#include "Grid.h" 6 7class TestScene : public IScene 8{ 9private: 10 Player player; 11 Grid grid; 12 13public: 14 void Init()override; 15 void Uninit()override; 16 void Update()override; 17 void Draw()override; 18};

#蛇足
質問からそれたことになりますが、

私の設計やヘッダーを見て、
「こうするといいよー」みたいなこと気づかれましたら、
教えてくださると嬉しいです。

Serbonis👍を押しています

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

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

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

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

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

guest

回答2

0

ベストアンサー

私自身もこの問題に明確な解を見いだせず迷い中ですが、今までに得た中から私なりの考えを述べさせていただきたいと思いますので、ご参考にしていただければ幸いです。

私が見てきたものでは、シーンマネジャーのポインターをゲームオブジェクトに持たせるものが多かったです。具体的には以下のような感じです。

C++

1class Player { 2public: 3 Player( ISCene* s ) : scene{s}{} 4private: 5 IScene* scene; 6}; 7コード

下記の書籍のサンプルプログラムでも上記のように実装されていました。
ゲームプログラミングC++ Sanjay Madhav (著), 今給黎 隆 (監修), 吉川 邦夫 (翻訳)

また、下記の配信では、このような手法を「依存性の注入」と解説されていました。
C++ シューティング
https://www.youtube.com/watch?v=ZEooBwW9Mmk&list=PL2Ohk9z94md2Ck4nT3BNX5yWso2Ik-9Ej&index=1

ゲームオブジェクトがゲーム全体を知るマネージャーのポインターを保持することで、このポインター経由でマネージャーに問い合わせします。私はこの方法を知った時になるほどと思ったのと同時に下記の点を疑問にも思いました。

1.すべてのオブジェクトがマネージャーのポインターを持つのは(微々たるものとしても)メモリの無駄ではないか?そして、そのためにオブジェクトのインスタンス生成(コンストラクタを用意するなど)が微妙に面倒くさくないか?

2.各オブジェクトが問い合わせ先の(この場合はGrid)のクラス構造を知るためにヘッダをインクルードしなければならない。相手のクラスへの依存性が強く、修正やリビルドが頻発しないか?

いま自分では、すべての問い合わせを受け持つ専用クラスを用意して、そこに静的メンバ関数を用意するやり方でチャレンジしています。

C++

1//SceneManager.h 2struct SceneHelper; 3class SceneManager { 4friend struct SceneHelper; 5}; 6 7//SceneHelper.h 8struct SceneHelper { 9static auto GetStageNumber( void )->int; 10static auto GetPlayerPosition( void )->std::pair<float,float>; 11static auto GetGridSize( void )>std::pair<float,float>; 12};

SceneManegrと各シーンを切り離し、各シーンはSceneHelperを使用します。SceneHelperは組み込み型かstdライブラリの型を使って、できるだけ特有の構造体を作成・依存しないようにしています。

蛇足
上記の方法ではSceneManagerには各シーンの実体であるメンバ変数を持たせていません。

C++

1#if 0 2 //シーンの実体 3 TestScene scene_test; //デバッグ用 4 TitleScene scene_title; //タイトル 5 GameScene scene_game; //ゲーム本編 6 ResultScene scene_result; //リザルト 7#endif

SceneManegerが生成された時点で、すべてのシーンの実体ができてしまうのは無駄が多いのではないか?という観点からです。シーンが切り替わるタイミングで次のシーンを作成、次のシーンに完全に移行したら、前のシーンは削除するようにしています。次のシーンに引き続きたい情報やインスタンスは、SceneHelper経由でSceneManegerに預けるようにしています。

また、SceneTypeにシーンの識別IDを持たす方法の代わりに文字列をキーにするマップでシーンを保持して、シーンの切り替え時は次のシーンの名前を文字列で渡しています。

C++

1 IScene *m_scenes[SCENE_NUM]; //各シーンを一元管理する配列 2void ChangeScene(SceneType); //シーンを切り替え 3 45std::unordered_map<std::string,IScene*> m_scenes; 6void ChangeScene( const std::string& ); //シーンを切り替え

当然、コンパイル時の型チェックの恩恵にもあずかれませんが、SceneTypeという固有の型を排除して、シーンの追加/削除のフットワークを軽くしたい(SceneManegerの修正が不要になる)という観点でそうしました。

投稿2021/08/07 11:24

編集2021/08/07 11:27
Serbonis

総合スコア581

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

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

piced

2021/08/08 10:37

様々なことを教えていただき感謝の限りです。大変勉強になりました。 懇切丁寧な回答ありがとうございました。
guest

0

player も grid も TestScene が抱えているメンバなのであれば,
全てはこの枠(TestScene)の内側で閉じた話になりますから,ISceneだのSceneManagerだのいう物の存在は無関係に思えます.
(:そんな無関係な話を質問に持ち込まなくても良いのでは?)

grid なるオブジェクトの存在を知っているのは TestScene だけなのですから,TestScene 側から「何かしらの必要なもの」を player に与えてやれば良いだけではないでしょうか.
(TestScene のコンストラクタとかで,player に対して「何か」をsetしてやれば良いでしょう.)

ざっくり,以下の2パターンが考えられるかと.

  • gird へのポインタとか参照みたいなのをダイレクトに player に教えてやる

→以降,player は好き勝手に grid へアクセスする.

  • 「player が grid へアクセスすることで実施しようとしている作業」を行う手段を player に教えてやる

→player は直接 grid にアクセスしない.そもそも Grid なる型や grid なるインスタンスの存在自体を知らない.

後者側については,
例えば,player側からやりたい処理を行うためのメソッド群を持つインタフェースクラスでも作って,それ(へのポインタやら参照やら)を player 側に教えてやればよいでしょう.
このインタフェースクラスを TestScene が継承するならば,TestScene 側からthis(なり *this)を player に教えてやればよいでしょう.

投稿2021/08/16 04:27

編集2021/08/16 04:29
fana

総合スコア11658

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問