コンテンツにスキップ

20. クリックゲーム

チュートリアル 3 ~ 19 の内容を使って、アイテムをクリックするゲームを作ります。

20.1 アイテムの描画とクリック判定

  • クリック対象の絵文字と、クリック判定用の円を用意します
  • クリックされた場合、ランダムな位置に移動します

コード
# include <Siv3D.hpp>

// ランダムな座標を返す関数
Vec2 GetRandomPos()
{
	return{ Random(60, 740), Random(60, 540) };
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	// クリック目標の絵文字
	const Texture targetEmoji{ U"🍎"_emoji };

	// クリック目標の円
	Circle targetCircle{ 400, 300, 60 };

	while (System::Update())
	{
		// クリック目標をクリックしたら
		if (targetCircle.leftClicked())
		{
			// クリック目標の位置をランダムな位置に変更する
			targetCircle.center = GetRandomPos();
		}

		// クリック目標を描く
		targetEmoji.drawAt(targetCircle.center);
	}
}

20.2 マウスオーバー判定

  • クリック対象がマウスオーバーされている場合、マウスカーソルを手の形に変更します

コード
# include <Siv3D.hpp>

// ランダムな座標を返す関数
Vec2 GetRandomPos()
{
	return{ Random(60, 740), Random(60, 540) };
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	// クリック目標の絵文字
	const Texture targetEmoji{ U"🍎"_emoji };

	// クリック目標の円
	Circle targetCircle{ 400, 300, 60 };

	while (System::Update())
	{
		// クリック目標がマウスカーソルに重なっていたら
		if (targetCircle.mouseOver())
		{
			// マウスカーソルを手の形に変更する
			Cursor::RequestStyle(CursorStyle::Hand);
		}

		// クリック目標をクリックしたら
		if (targetCircle.leftClicked())
		{
			// クリック目標の位置をランダムな位置に変更する
			targetCircle.center = GetRandomPos();
		}

		// クリック目標を描く
		targetEmoji.drawAt(targetCircle.center);
	}
}

20.3 スコアの表示

  • アイテムをクリックするとスコアが加算されるようにします
  • スコアを画面に表示します

コード
# include <Siv3D.hpp>

// ランダムな座標を返す関数
Vec2 GetRandomPos()
{
	return{ Random(60, 740), Random(60, 540) };
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Font font{ FontMethod::MSDF, 48, Typeface::Bold };

	// クリック目標の絵文字
	const Texture targetEmoji{ U"🍎"_emoji };

	// クリック目標の円
	Circle targetCircle{ 400, 300, 60 };

	// スコア
	int32 score = 0;

	while (System::Update())
	{
		// クリック目標がマウスカーソルに重なっていたら
		if (targetCircle.mouseOver())
		{
			// マウスカーソルを手の形に変更する
			Cursor::RequestStyle(CursorStyle::Hand);
		}

		// クリック目標をクリックしたら
		if (targetCircle.leftClicked())
		{
			score += 100;

			// クリック目標の位置をランダムな位置に変更する
			targetCircle.center = GetRandomPos();
		}

		// クリック目標を描く
		targetEmoji.drawAt(targetCircle.center);

		// スコアを表示する
		font(U"SCORE: {}"_fmt(score)).draw(40, Vec2{ 40, 40 }, ColorF{ 0.1 });
	}
}

20.4 妨害アイテムの追加

  • クリックすると減点されてしまう妨害アイテムを追加します

コード
# include <Siv3D.hpp>

// ランダムな座標を返す関数
Vec2 GetRandomPos()
{
	return{ Random(60, 740), Random(60, 540) };
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Font font{ FontMethod::MSDF, 48, Typeface::Bold };

	// クリック目標の絵文字
	const Texture targetEmoji{ U"🍎"_emoji };

	// 妨害アイテムの絵文字
	const Texture trapEmoji{ U"🌶"_emoji };

	// クリック目標の円
	Circle targetCircle{ 400, 300, 60 };

	// 妨害アイテムの円
	Circle trapCircle{ 200, 150, 60 };

	// スコア
	int32 score = 0;

	while (System::Update())
	{
		// クリック目標がマウスカーソルに重なっていたら
		if (targetCircle.mouseOver() || trapCircle.mouseOver())
		{
			// マウスカーソルを手の形に変更する
			Cursor::RequestStyle(CursorStyle::Hand);
		}

		// クリック目標をクリックしたら
		if (targetCircle.leftClicked())
		{
			score += 100;
			targetCircle.center = GetRandomPos();
			trapCircle.center = GetRandomPos();
		}

		// 妨害アイテムをクリックしたら
		if (trapCircle.leftClicked())
		{
			score -= 200;
			targetCircle.center = GetRandomPos();
			trapCircle.center = GetRandomPos();
		}

		// クリック目標を描く
		targetEmoji.drawAt(targetCircle.center);

		// 妨害アイテムを描く
		trapEmoji.drawAt(trapCircle.center);

		// スコアを表示する
		font(U"SCORE: {}"_fmt(score)).draw(40, Vec2{ 40, 40 }, ColorF{ 0.1 });
	}
}

20.5 残り時間

  • ゲームに制限時間を設け、時間切れになるとアイテムをクリックできないようにします

コード
# include <Siv3D.hpp>

// ランダムな座標を返す関数
Vec2 GetRandomPos()
{
	return{ Random(60, 740), Random(60, 540) };
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Font font{ FontMethod::MSDF, 48, Typeface::Bold };

	// クリック目標の絵文字
	const Texture targetEmoji{ U"🍎"_emoji };

	// 妨害アイテムの絵文字
	const Texture trapEmoji{ U"🌶"_emoji };

	// クリック目標の円
	Circle targetCircle{ 400, 300, 60 };

	// 妨害アイテムの円
	Circle trapCircle{ 200, 150, 60 };

	// スコア
	int32 score = 0;

	// 残り時間
	double remainingTime = 10.0;

	while (System::Update())
	{
		const double deltaTime = Scene::DeltaTime();

		// 残り時間を減らす
		remainingTime -= deltaTime;

		if (0 < remainingTime) // 残り時間がある場合
		{
			// クリック目標がマウスカーソルに重なっていたら
			if (targetCircle.mouseOver() || trapCircle.mouseOver())
			{
				// マウスカーソルを手の形に変更する
				Cursor::RequestStyle(CursorStyle::Hand);
			}

			// クリック目標をクリックしたら
			if (targetCircle.leftClicked())
			{
				score += 100;
				targetCircle.center = GetRandomPos();
				trapCircle.center = GetRandomPos();
			}

			// 妨害アイテムをクリックしたら
			if (trapCircle.leftClicked())
			{
				score -= 200;
				targetCircle.center = GetRandomPos();
				trapCircle.center = GetRandomPos();
			}
		}

		// クリック目標を描く
		targetEmoji.drawAt(targetCircle.center);

		// 妨害アイテムを描く
		trapEmoji.drawAt(trapCircle.center);

		// スコアを表示する
		font(U"SCORE: {}"_fmt(score)).draw(40, Vec2{ 40, 40 }, ColorF{ 0.1 });

		if (0 < remainingTime) // 残り時間がある場合
		{
			// 残り時間を表示する
			font(U"TIME: {:.1f}"_fmt(remainingTime)).draw(40, Arg::topRight(760, 40), ColorF{ 0.1 });
		}
		else // 時間切れの場合
		{
			// 時間切れのメッセージを表示する
			font(U"TIME'S UP!").drawAt(60, Vec2{ 400, 300 }, ColorF{ 0.1 });
		}
	}
}

20.6 【完成】ゲームのリセット

  • ゲーム終了画面で Enter キーを押すと、スコアと残り時間をリセットしてゲームを再プレイできるようにします

コード
# include <Siv3D.hpp>

// ランダムな座標を返す関数
Vec2 GetRandomPos()
{
	return{ Random(60, 740), Random(60, 540) };
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Font font{ FontMethod::MSDF, 48, Typeface::Bold };

	// クリック目標の絵文字
	const Texture targetEmoji{ U"🍎"_emoji };

	// 妨害アイテムの絵文字
	const Texture trapEmoji{ U"🌶"_emoji };

	// クリック目標の円
	Circle targetCircle{ 400, 300, 60 };

	// 妨害アイテムの円
	Circle trapCircle{ 200, 150, 60 };

	// スコア
	int32 score = 0;

	// 残り時間
	double remainingTime = 10.0;

	while (System::Update())
	{
		const double deltaTime = Scene::DeltaTime();

		// 残り時間を減らす
		remainingTime -= deltaTime;

		if (0 < remainingTime) // 残り時間がある場合
		{
			// クリック目標がマウスカーソルに重なっていたら
			if (targetCircle.mouseOver() || trapCircle.mouseOver())
			{
				// マウスカーソルを手の形に変更する
				Cursor::RequestStyle(CursorStyle::Hand);
			}

			// クリック目標をクリックしたら
			if (targetCircle.leftClicked())
			{
				score += 100;
				targetCircle.center = GetRandomPos();
				trapCircle.center = GetRandomPos();
			}

			// 妨害アイテムをクリックしたら
			if (trapCircle.leftClicked())
			{
				score -= 200;
				targetCircle.center = GetRandomPos();
				trapCircle.center = GetRandomPos();
			}
		}
		else // 時間切れの場合
		{
			if (KeyEnter.down())
			{
				score = 0;
				remainingTime = 15.0;
				targetCircle.center = GetRandomPos();
				trapCircle.center = GetRandomPos();
			}
		}

		// クリック目標を描く
		targetEmoji.drawAt(targetCircle.center);

		// 妨害アイテムを描く
		trapEmoji.drawAt(trapCircle.center);

		// スコアを表示する
		font(U"SCORE: {}"_fmt(score)).draw(40, Vec2{ 40, 40 }, ColorF{ 0.1 });

		if (0 < remainingTime) // 残り時間がある場合
		{
			// 残り時間を表示する
			font(U"TIME: {:.1f}"_fmt(remainingTime)).draw(40, Arg::topRight(760, 40), ColorF{ 0.1 });
		}
		else // 時間切れの場合
		{
			// 時間切れのメッセージを表示する
			font(U"TIME'S UP!").drawAt(60, Vec2{ 400, 300 }, ColorF{ 0.1 });
			font(U"Press [Enter] to restart").drawAt(40, Vec2{ 400, 400 }, ColorF{ 0.1 });
		}
	}
}