6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenSiv3D(0.6.3)で5行コピペでフレームレート固定(60FPS)を作る

Last updated at Posted at 2022-01-01
ここはとりあえず的に作ったもので、別ページに改良バージョンがあります。こちらをご使用ください。

以下の改良されています。

改良前 ・ソースコードにあれこれ追加で部品化や汎用性が考えられていない。
改良前 ・単純に1/60秒待っているので処理落ちしやすい。正確な時間処理はしていない

改良後 ・ヘッダーファイル追加で変更箇所がかなり少なくなる、主コードを圧迫しない
改良後 ・必ず60FPSで計算するようになり、かなり計算処理落ちは無くなる
※状況により描画しないで計算のつじつまを合わせる方法(市販ゲームで行っている昔からある方法)

steamで販売のものを作るとかなら、話は別ですが、自分用のちょっとしたミニゲームを作るくらいなら、これで十分と考えてその場で作ったものです。
上記リンク先はやるべきだとは普通に考えていました。
XNAライブラリでは標準装備だったので気持ちは引っ張られてはいましたが、
まあいっか、という感じで保留していました。
誰かこの面倒な処理を続けて作ってくれという他力本願の願いが本音なところ
何か問題が起きたら、改良しよう


以下、旧記事

OpenSiv3D(0.6.3)では、下記命令はありません

CPP
Graphics::SetTargetFrameRateHz(60);   // 1秒間60回 画面を書き換える設定にする

あれ? OpenSiv0.4.3 ではあったのに??と思って手短に作りましたが、まあいいや、で何か月か放置していました。
で、まあ嫌がっている人がほかに聞いたらけっこういらっしゃったので、こちらにUPする形になりました。

昔のPCでは、ライブラリ系では基本は60FPS(フレームパーセコンド、1秒間に画面を書き換える回数)
これには近年のPCでは困ったことがあります。
昔のコンピュータでは、フルスクリーンのゲームを作ると、何も設定せずとも60FPSになります。
家庭用ゲームと同じ設定です。(1部ヨーロッパの除く)

しかし、最近のPCではFPSが60以外も144とかも多く、この描画回数が多くなると、
動きが早くなってしまい、

人にプログラムを渡すと、

君 >>>> プログラムを渡す >>> 動きが早すぎになってしまうよ!!

image.pngB君

A君PC=60FPSのPC
B君PC=144FPSのPC

初心者はそんなことは分かりません。

描画ループの while (System::Update()) の上に3行を書きます。3行コピペ

System::Update()の上の行に貼り付ける内容
	int FPS = 60; // 1秒間に1画面を書き換える回数
	Stopwatch sw;	//FPS60
	sw.start();	//FPS60

	while (System::Update()) // ↑に追加 
	{


描画ループの while (System::Update())のループの最下 } の上に2行を追加します。2行コピペ

System::Update(){の上の行に貼り付ける内容
		while (sw.msF() < 1000.0 / FPS);	//1/60秒経過するまでループ
		sw.restart();	//FPS60  ストップウォッチをリスタート
	}

まあ、こんな感じで、たった5行コピペすればOKです。

これだと動作が分からないので下記にサンプルコードを載せます。

実行結果
image.png

Windowタイトルで60FPS表示されているのが分かります。
この設定をしないと使用しているPCではFPSが144くらいだったと思います。

サンプルコード

60fpsMain.cpp
# include <Siv3D.hpp> // OpenSiv3D v0.6.3

/*
	要望があれば改良記事を書きます。
*/

void Main()
{
	// 背景の色を設定 | Set background color
	Window::Resize(DisplayResolution::HD_1280x720);
	Scene::Resize(DisplayResolution::HD_1280x720);
	Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });
	Graphics::SetVSyncEnabled(false);

	// 通常のフォントを作成 | Create a new font
	const Font font{ 60 };

	// 絵文字用フォントを作成 | Create a new emoji font
	const Font emojiFont{ 60, Typeface::ColorEmoji };

	// `font` が絵文字用フォントも使えるようにする | Set emojiFont as a fallback
	font.addFallback(emojiFont);

	// 画像ファイルからテクスチャを作成 | Create a texture from an image file
	const Texture texture{ U"example/windmill.png" };
	const Texture s3kun{ U"example/siv3d-kun.png" };
	int x = 0;

	// 絵文字からテクスチャを作成 | Create a texture from an emoji
	const Texture emoji{ U"🐈"_emoji };


	// 絵文字を描画する座標 | Coordinates of the emoji
	Vec2 emojiPos{ 300, 150 };

	// テキストを画面にデバッグ出力 | Print a text
	Print << U"Push [A] key";

	Rect rect(100, 100, 200, 50);

	int FPS = 60; // 1秒間に1画面を書き換える回数
	Stopwatch sw;	//FPS60
	sw.start();	//FPS60

	while (System::Update()) // ↑に追加 
	{
		// テクスチャを描く | Draw a texture
		texture.draw(200, 200);

		// テキストを画面の中心に描く | Put a text in the middle of the screen
		font(U"Hello, Siv3D!🚀").drawAt(Scene::Center(), Palette::Black);

		// サイズをアニメーションさせて絵文字を描く | Draw a texture with animated size
		emoji.resized(100 + Periodic::Sine0_1(1s) * 20).drawAt(emojiPos);

		// マウスカーソルに追随する半透明な円を描く | Draw a red transparent circle that follows the mouse cursor
		Circle{ Cursor::Pos(), 40 }.draw(ColorF{ 1, 0, 0, 0.5 });

		// もし [A] キーが押されたら | When [A] key is down
		if (KeyA.down())
		{
			// 選択肢からランダムに選ばれたメッセージをデバッグ表示 | Print a randomly selected text
			Print << Sample({ U"Hello!", U"こんにちは", U"你好", U"안녕하세요?" });
		}

		// もし [Button] が押されたら | When [Button] is pushed
		if (SimpleGUI::Button(U"Button", Vec2{ 640, 40 }))
		{
			// 画面内のランダムな場所に座標を移動
			// Move the coordinates to a random position in the screen
			emojiPos = RandomVec2(Scene::Rect());
		}
		x += 2;
		x %= DisplayResolution::HD_1280x720.x + s3kun.width() * 2;
		s3kun.drawAt( x-s3kun.width() ,Scene::Center().y);

		font(U"1 frame time:{:.5f}"_fmt(Scene::DeltaTime()))
			.drawAt(Scene::Center().x,Scene::Height()-font.fontSize()*2,Palette::Black);
		font(U"FPS:{:.2f}"_fmt(1/Scene::DeltaTime()))
			.drawAt(Scene::Center().x,Scene::Height()-font.fontSize()*1,Palette::Black);

		auto rr=rect.rotated(Scene::Time());
		auto r0=rect.rotated(0);
		bool tt=rr.intersects(Rect(100,100,300,300));
		bool tt0=Rect(100,100,300,300).intersects(rr);
		rect.rotated((Scene::Time())).draw(Palette::Cadetblue);


		while (sw.msF() < 1000.0 / FPS);	//1/60秒経過するまでループ
		sw.restart();	// ストップウォッチをリスタート
	}
}


VisualStudio2019使用 OpenSIv3D 0.6.3

FPS固定だと処理落ちすると動きがゆっくりになります。

これの解決方法として家庭用ゲームでは一般的なのは フレームスキップ機能 です。
3Dゲームでは処理落ちは頻繁にあります。
例えばヘリに搭乗して空高く飛ぶと、ポリゴンめいっぱいの街並みが表示されたりする場面です。
(レインボーシックスベガス)
コマ落ちしているのが分かると思います。
処理落ちしたら、その時のフレームは描画をしないです。
処理速度で最も大きいのはだいたい描画です。
状況により、描画をせずにして時間経過における座標変化速度を維持しようとするのです。

PS3の3Dゲームとかが分かりやすいでしょうか。表示物が多いとすぐコマ落ちするんで。

マイクロソフトのXNAライブラリは描画ループで標準装備されていました。

次回は要望があればOpenSiv3Dでそれの実装コードの記事を載せたいと思います。数行追加すればできますね。

ついでにゲームでよくある一時停止についてもやるかも?

分かりにくい点などご指摘いただければ修正いたします。

6
2
13

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?