[C++] 248 SDL : コンストラクタの初期化リスト ChatGPT

[M1 Mac, Monterey 12.6.3, clang 13.0.0, SDL 2.26.2, ChatGPT Plus, NO IDE]

コンストラクタの初期化リストについてあまりよく分かっていなかったので、ChatGPTに説明してもらいました。

大分理解が深まったように思います。

Q1: SDLアプリの以下のコードを解説してください。
Game::Game() : m_deltaTimeSeconds( 0.0f ) ,
m_framesUntilFall( s_initialFramesPerFallStep ) ,
m_framesPerFallStep( s_initialFramesPerFallStep ) , m_numUserDropsForThisTetronimo( 0 ) ,
m_numLinesCleared( 0 ) ,
m_level( 0 ) ,
m_score( 0 ) ,
m_hiScore( 0 ) ,
m_gameState( kGameState_TitleScreen )
{ m_field.staticBlocks = nullptr; }

このコードは、SDLを使用して作成されたゲームのコンストラクタであり、以下のように各メンバー変数を初期化しています。

  • m_deltaTimeSeconds:前のフレームからの時間差を表す変数を0.0fで初期化しています。
  • m_framesUntilFall:新しいテトロミノが落下するまでのフレーム数を表す変数であり、s_initialFramesPerFallStep(定数)で初期化しています。
  • m_framesPerFallStep:テトロミノが1段落下するのに必要なフレーム数を表す変数であり、s_initialFramesPerFallStepで初期化しています。
  • m_numUserDropsForThisTetronimo:ユーザーがテトロミノを操作した際に、落下させた回数を表す変数であり、0で初期化しています。
  • m_numLinesCleared:ゲームでクリアしたラインの数を表す変数であり、0で初期化しています。
  • m_level:ゲームのレベルを表す変数であり、0で初期化しています。
  • m_score:ゲームのスコアを表す変数であり、0で初期化しています。
  • m_hiScore:過去に達成した最高スコアを表す変数であり、0で初期化しています。
  • m_gameState:ゲームの状態を表す変数であり、kGameState_TitleScreenで初期化しています。

また、m_field.staticBlocksnullptrで初期化しています。m_fieldは、ゲームフィールドを表す構造体であり、staticBlocksは静的なブロックの配列を指すポインタです。初期化することで、メモリが確保されていないことを表しています。後で動的に確保する予定があると思われます。

Q2: 以下のコンストラクタでも問題ないですか?
Game::Game() {
m_deltaTimeSeconds = 0.0f;
m_framesUntilFall = s_initialFramesPerFallStep;
m_framesPerFallStep = s_initialFramesPerFallStep; m_numUserDropsForThisTetronimo = 0;
m_numLinesCleared = 0;
m_level = 0;
m_score = 0;
m_hiScore = 0;
m_gameState = kGameState_TitleScreen;
m_field.staticBlocks = nullptr; }

はい、このコードでも問題ありません。このコードは、先に解説したコードと同様に、各メンバー変数を初期化しています。:での初期化リストの代わりに、コンストラクタの本文で各メンバー変数を初期化しています。{}で囲む必要はなく、必要最低限のコードになっているため、より簡潔になっています。ただし、先に解説したコードよりも少し冗長であると言えます。

Q3: 以下のコンストラクタでも問題ないですか?
Game::Game() : m_deltaTimeSeconds( 0.0f ) ,
m_framesUntilFall( s_initialFramesPerFallStep ) ,
m_framesPerFallStep( s_initialFramesPerFallStep ) , m_numUserDropsForThisTetronimo( 0 ) ,
m_numLinesCleared( 0 ) ,
m_level( 0 ) ,
m_score( 0 ) ,
m_hiScore( 0 ) ,
m_gameState( kGameState_TitleScreen ) ,
m_field.staticBlocks(nullptr)
{}

はい、このコードは正しい構文です。初期化リストを使用して各メンバー変数を初期化しており、最後の要素であるm_field.staticBlocksnullptrで初期化しています。また、コンストラクタの本体には、ブロックの終了を示すための{}が含まれています。したがって、このコードは問題ありません。

Q3への回答ではChatGPTは正しいとしていますが、実際はm_field.staticBlocks(nullptr)といった書き方ではエラーになります。メンバ変数のプロパティはこのようには初期化できません。{ m_field.staticBlocks = nullptr; }が正しい表現です。

[C++] 247 SDL : 画面に記号を表示する ChatGPT

[M1 Mac, Monterey 12.6.3, clang 13.0.0, SDL 2.26.2, ChatGPT Plus, NO IDE]

SDLアプリの初期画面に”Press □ to start”を表示させようとすると文字化けしてしまいます。

ChatGPTの回答の通りフォントを変えたりUnicode表記に変えても解決に至らなかったのですが、質問の仕方を変えてヒントを導き出しました。

TTF_RenderText_Blended()関数をTTF_RenderUTF8_Solid()関数に書き換えるだけでした。

ゲームコントローラーに関するコードを書かせてもデタラメだらけでしたが、公式ドキュメントがしっかりしているカテゴリーについてはそれなりのコードを書いてくれますね。

所要時間は30分程度でした。Google検索活用だったら2,3時間、下手したら半日以上掛かっていたかもしれません。

修正前
修正後
void Renderer::DrawText( const char* text, int x, int y, uint32_t rgba /*= 0xffffffff */ )
{
	SDL_assert( text );

	SDL_Color color = MakeSDL_Colour( rgba );

	SDL_Surface* pSurface = TTF_RenderUTF8_Solid(mFont, text, color);
	// SDL_Surface* pSurface = TTF_RenderText_Blended( mFont, text, color ); 旧コード

	SDL_Texture* pTexture = SDL_CreateTextureFromSurface( m_pSdlRenderer, pSurface );
	int width, height;
	SDL_QueryTexture(pTexture, NULL, NULL, &width, &height);
	SDL_Rect dstRect = { x, y, width, height };
	SDL_RenderCopy( m_pSdlRenderer, pTexture, nullptr, &dstRect );
	SDL_DestroyTexture( pTexture );
	SDL_FreeSurface( pSurface );
}

ChatGPT調査
Q1: SDLアプリの画面に□を文字化けさせずに表示させる方法を教えてください

SDLアプリで正しくUnicodeテキストを表示させるためには、以下の手順を行う必要があります。

  1. 必要なフォントをインストールする
  2. フォントをロードする
  3. テキストを描画する

まず、必要なフォントをインストールする必要があります。□を含むテキストを表示させるためには、Unicodeの範囲に含まれるフォントを使用する必要があります。例えば、Noto Sansフォントは多言語対応のフォントで、Unicodeの範囲に含まれる多くの文字をサポートしています。このフォントをインストールすることをお勧めします。

次に、フォントをロードする必要があります。SDL_ttfライブラリを使用して、フォントをロードできます。フォントをロードするには、まずTTF_Init()関数を使用してライブラリを初期化し、TTF_OpenFont()関数を使用してフォントファイルを開きます。以下は、フォントをロードするためのサンプルコードです。

if (TTF_Init() == -1) {
    printf("TTF_Init failed: %s\n", TTF_GetError());
    return false;
}

TTF_Font* font = TTF_OpenFont("path/to/font.ttf", 24);
if (!font) {
    printf("TTF_OpenFont failed: %s\n", TTF_GetError());
    return false;
}

最後に、テキストを描画する必要があります。TTF_RenderUTF8_Solid()関数を使用して、テキストをサーフェスにレンダリングし、それをテクスチャに変換してレンダラーに描画することができます。以下は、テキストを描画するためのサンプルコードです。

SDL_Surface* surface = TTF_RenderUTF8_Solid(font, "□", { 255, 255, 255 });
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_Rect rect = { 0, 0, surface->w, surface->h };
SDL_RenderCopy(renderer, texture, NULL, &rect);
SDL_FreeSurface(surface);
SDL_DestroyTexture(texture);

このコードでは、□をサーフェスにレンダリングし、それをテクスチャに変換してレンダラーに描画しています。

なお、フォントをロードする際に、フォントファイルのパスを正しく指定することが重要です。また、描画する前に、描画色を適切に設定する必要があります。

Q2: SDL_Surface* surface = TTF_RenderUTF8_Solid(font, “□”, { 255, 255, 255 }); SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); 
と以下のコードを統合させてください。
renderer.DrawText( “Press □ to start”, renderer.GetLogicalWidth()/2 – 100, renderer.GetLogicalHeight()/2, 0xffffffff );

以下のように、DrawText関数の内部に上記のコードを追加することができます。

void Renderer::DrawText(const std::string& text, int x, int y, Uint32 color)
{
    SDL_Surface* surface = TTF_RenderUTF8_Solid(m_font, text.c_str(), { 255, 255, 255 });
    SDL_Texture* texture = SDL_CreateTextureFromSurface(m_renderer, surface);

    SDL_Rect dstRect = { x, y, surface->w, surface->h };

    SDL_SetTextureColorMod(texture, color >> 16, (color >> 8) & 0xff, color & 0xff);

    SDL_RenderCopy(m_renderer, texture, nullptr, &dstRect);

    SDL_DestroyTexture(texture);
    SDL_FreeSurface(surface);
}

このようにコードを変更すると、DrawText関数を呼び出すときに”Press □ to start”のように□を表示することができます。

[C++] 246 SDL : ゲームコントローラーを認識させる Intel Mac

[Intel Mac, Big Sur 11.6.5, clang 13.0.0, SDL 2.24.1, ChatGPT Plus, NO IDE]

Apple Silicon MacではワイヤレスPS4コントローラーが操作できませんでしたが、Intel Macでは動きました。

void App::Run()
{
	Uint32 lastTimeMs = SDL_GetTicks();
	auto lastTime = std::chrono::high_resolution_clock::now();

	bool bDone = false;
	while( !bDone )
	{
		GameInput gameInput = {};
		SDL_JoystickUpdate();

		// respond to events
		SDL_Event event;
		while( SDL_PollEvent( &event ) )
		{
			if(event.type == SDL_QUIT)
			{
				bDone = true;
			}

			if(event.type == SDL_JOYBUTTONDOWN)
			{
				switch (event.jbutton.button){
					case 3:{	// △ボタン:終了
						bDone = true;
						break;
					}
					case 2:{	// □ボタン:スタート
						gameInput.bStart = true;
						// cout << "□ボタン押下" << endl;
						break;
					}
					case 13:{	// 左ボタン
						gameInput.bMoveLeft = true;
						break;
					}
					case 14:{	// 右ボタン
						gameInput.bMoveRight = true;
						break;
					}
					case 1:{	// ○ボタン:時計回り回転
						gameInput.bRotateClockwise = true;
						break;
					}
					case 0:{	// ×ボタン:反時計回り回転
						gameInput.bRotateAnticlockwise = true;
						break;
					}
					case 12:{	// 下ボタン:一気に落下
						gameInput.bHardDrop = true;
						break;
					}
					case 11:{	// 上ボタン:徐々に落下
						gameInput.bSoftDrop = true;
						break;
					}
					case 4:{	// shareボタン:停止(効かない)
						gameInput.bPause = true;
						// cout << "shareボタン押下" << endl;
						break;
					}
					default:{
						cout << "default" << endl;
						break;
					}
				}
			}
		}

		Uint32 currentTimeMs = SDL_GetTicks();
		Uint32 deltaTimeMs = currentTimeMs - lastTimeMs;
		lastTimeMs = currentTimeMs;
		HP_UNUSED( deltaTimeMs );
		
		auto currentTime = std::chrono::high_resolution_clock::now();
		auto deltaTime = currentTime - lastTime;
		std::chrono::microseconds deltaTimeMicroseconds = std::chrono::duration_cast<std::chrono::microseconds>(deltaTime);
		float deltaTimeSeconds = 0.000001f * (float)deltaTimeMicroseconds.count();
		lastTime = currentTime;

		mGame->Update( gameInput, deltaTimeSeconds );

		mRenderer->Clear();
		mGame->Draw(*mRenderer );
		mRenderer->Present();
	}
}

[C++] 245 SDL : ゲームコントローラーを認識させる Apple Silicon

[M1 Mac, Monterey 12.6.3, clang 13.0.0, SDL 2.26.2, ChatGPT Plus, NO IDE]

以下のコードでPS4のワイヤレスゲームコントローラーを認識させることができましたが、肝心のボタンが反応しません。

有線接続すると操作できるようになりました。

ワイヤレス接続については明日以降Intel Macで試してみます。

bool App::Init( bool bFullScreen, unsigned int displayWidth, unsigned int displayHeight )
{
	if( SDL_Init( SDL_INIT_EVERYTHING ) != 0)
	{
		fprintf( stderr, "SDL failed to initialise: %s\n",SDL_GetError() );
		return false;
	}

	<中略>

	// mJoystick
	mJoystick = SDL_JoystickOpen(0);
    if (!mJoystick) {
        std::cerr << "Failed to open joystick: " << SDL_GetError() << std::endl;
        return false;
    }

	int num_axes = SDL_JoystickNumAxes(mJoystick);
    int num_buttons = SDL_JoystickNumButtons(mJoystick);

	printf("Joystick: %s\n", SDL_JoystickName(mJoystick));
	cout << "JoystickNumAxes: " << num_axes << endl;
	cout << "JoystickNumButtons: " << num_buttons << endl;

	return true;
}
Joystick: PS4 Controller
JoystickNumAxes: 6
JoystickNumButtons: 16

ChatGPT調査
Q1: SDL2アプリに接続したPS4コントローラーがApple Siliconではうまく動作しないという情報はありますか

一部のユーザーが報告しているように、SDL2アプリに接続したPS4コントローラーがApple SiliconベースのMacコンピューターで動作しないことがあります。これは、macOS上でのHIDの処理に関連する問題である可能性があります。

ただし、問題が発生するかどうかは、使用するSDL2バージョンやmacOSのバージョンによって異なります。最新のSDL2バージョンである2.0.16では、この問題が解決されたと報告されています。また、macOS 12(Monterey)では、この問題が修正されると予想されています。

したがって、問題が発生している場合は、まず最新のSDL2バージョンを使用することをお勧めします。また、macOSのアップデートがリリースされるのを待つこともできます。

Q2: 一部ユーザーの報告についてリンクを貼ってください

以下にApple SiliconでのPS4コントローラーの問題に関する報告を示します。

ただし、これらは個人の報告であり、すべてのユーザーに当てはまるわけではありません。また、Apple SiliconでのSDL2アプリケーションでの問題には関する報告もありますので、注意してください。