떨어진 음식 모으기 게임 만들기 Day 2

123675 단어 siv3dtech
이 과정은 초급 C++ 문법과 OpenSiv3D 기능을 사용해 음식을 수집하는 간단한 게임을 만든다.
  • 이전 노선: https://zenn.dev/reputeless/articles/course-catch-the-food-1
  • 9. 음식 이동


    음식을 떨어뜨려라.
    음식의 이동량은 게이머의 이동량과 마찬가지로 속도와 프레임 시간을 바탕으로 계산된다.
    또 프로그램을 더 잘 예측하기 위해 주 순환 중 3개 영역을'플레이어의 이동','음식의 이동','그림'으로 나눠 각각 { } 범위로 둘러쌌다.이렇게 하면 다른 부분에서 견본const double move 등 국부 변수를 사용하여 변수의 범위를 유한한 양호한 상태에 있게 할 수 있다.(변수 하나를 어디서나 사용할 수 있으면 프로그램의 변경과 유지보수가 번거로워진다.)
    {
    	// セクション A
    	{
    		int32 a = 10; // この変数が使えるのは現在のスコープ { } 内のみ
    	}
    
    	// セクション B
    	{
    		// ここで a は使えない
    	}
    
    	// セクション C
    	{
    		int32 a = 30; // ここで別の変数 a を作れる
    	}
    }
    
    # include <Siv3D.hpp> // OpenSiv3D v0.4.3
    
    // 食べ物クラス
    struct Food
    {
    	Vec2 pos;
    };
    
    void Main()
    {
    	// プレイヤーのテクスチャ
    	const Texture playerTexture{ Emoji{ U"😋" } };
    
    	// 食べ物のテクスチャ
    	const Texture foodTexture{ Emoji{ U"🍰" } };
    
    	// プレイヤーが毎秒何ピクセルの速さで移動するか
    	double playerSpeed = 300.0;
    
    	// 食べ物が毎秒何ピクセルの速さで落下するか
    	double foodSpeed = 200.0;
    
    	// プレイヤーの位置
    	Vec2 playerPos{ 400, 500 };
    
    	// 食べ物
    	Array<Food> foods;
    	foods << Food{ .pos = Vec2{ 300, 100 } };
    	foods << Food{ .pos = Vec2{ 500, 200 } };
    	foods << Food{ .pos = Vec2{ 700, 300 } };
    
    	while (System::Update())
    	{
    		// プレイヤーの移動
    		{
    			if (KeyLeft.pressed()) // 左矢印キーが押されていたら
    			{
    				playerPos.x -= (playerSpeed * Scene::DeltaTime());
    			}
    			else if (KeyRight.pressed()) // 右矢印キーが押されていたら
    			{
    				playerPos.x += (playerSpeed * Scene::DeltaTime());
    			}
    
    			// playerPos.x を 50 以上、750 以下に収める
    			playerPos.x = Clamp(playerPos.x, 50.0, 750.0);
    		}
    
    		// 食べ物の移動
    		{
    			const double move = (foodSpeed * Scene::DeltaTime());
    
    			for (auto& food : foods)
    			{
    				food.pos.y += move;
    			}
    		}
    
    		// 描画
    		{
    			// 空を描く(グラデーション)
    			Rect{ 0, 0, 800, 520 }
    				.draw(Arg::top = ColorF{ 0.1, 0.3, 0.6 }, Arg::bottom = ColorF{ 0.3, 0.7, 1.0 });
    
    			// 地面を描く
    			Rect{ 0, 520, 800, 80 }.draw(ColorF{ 0.2, 0.6, 0.3 });
    
    			// プレイヤーを描く
    			playerTexture.drawAt(playerPos);
    
    			for (const auto& food : foods)
    			{
    				// 食べ物を描く(サイズは 0.5 倍)
    				foodTexture.scaled(0.5).drawAt(food.pos);
    			}
    		}
    	}
    }
    

    10. 바닥에 떨어진 음식 제거


    앞으로 음식을 늘리려면 음식이 계속 떨어지면 처리 원가가 끊임없이 증가한다.이동이나 드로잉이 발생하지 않도록 바닥에 떨어진 음식을 배열에서 삭제합니다.Array의 구성원 변수.remove_if(pred)는 수조에서 조건pred을 충족시키는 요소를 삭제한다.음식이 땅에 떨어졌는지 판단하는 함수CheckFood()를 만들어 건네주기remove_if().
    또한 게임 변수의 상태를 쉽게 이해하기 위해'디버깅 디스플레이'구역에 음식의 개수를 표시할 수 있다.
    # include <Siv3D.hpp> // OpenSiv3D v0.4.3
    
    // 食べ物クラス
    struct Food
    {
    	Vec2 pos;
    };
    
    // 食べ物を消す条件を記述した関数
    bool CheckFood(const Food& food)
    {
    	// 食べ物の Y 座標が 600 より大きいか
    	return (600 < food.pos.y);
    }
    
    void Main()
    {
    	// プレイヤーのテクスチャ
    	const Texture playerTexture{ Emoji{ U"😋" } };
    
    	// 食べ物のテクスチャ
    	const Texture foodTexture{ Emoji{ U"🍰" } };
    
    	// プレイヤーが毎秒何ピクセルの速さで移動するか
    	double playerSpeed = 300.0;
    
    	// 食べ物が毎秒何ピクセルの速さで落下するか
    	double foodSpeed = 200.0;
    
    	// プレイヤーの位置
    	Vec2 playerPos{ 400, 500 };
    
    	// 食べ物
    	Array<Food> foods;
    	foods << Food{ .pos = Vec2{ 300, 100 } };
    	foods << Food{ .pos = Vec2{ 500, 200 } };
    	foods << Food{ .pos = Vec2{ 700, 300 } };
    
    	while (System::Update())
    	{
    		// デバッグ表示
    		{
    			ClearPrint();
    			Print << U"食べ物の個数: " << foods.size();
    		}
    
    		// プレイヤーの移動
    		{
    			if (KeyLeft.pressed()) // 左矢印キーが押されていたら
    			{
    				playerPos.x -= (playerSpeed * Scene::DeltaTime());
    			}
    			else if (KeyRight.pressed()) // 右矢印キーが押されていたら
    			{
    				playerPos.x += (playerSpeed * Scene::DeltaTime());
    			}
    
    			// playerPos.x を 50 以上、750 以下に収める
    			playerPos.x = Clamp(playerPos.x, 50.0, 750.0);
    		}
    
    		// 食べ物の移動
    		{
    			const double move = (foodSpeed * Scene::DeltaTime());
    
    			for (auto& food : foods)
    			{
    				food.pos.y += move;
    			}
    		}
    
    		// 地面に落ちた食べ物の消去
    		{
    			// 条件を満たす要素を配列から削除
    			foods.remove_if(CheckFood);
    		}
    
    		// 描画
    		{
    			// 空を描く(グラデーション)
    			Rect{ 0, 0, 800, 520 }
    				.draw(Arg::top = ColorF{ 0.1, 0.3, 0.6 }, Arg::bottom = ColorF{ 0.3, 0.7, 1.0 });
    
    			// 地面を描く
    			Rect{ 0, 520, 800, 80 }.draw(ColorF{ 0.2, 0.6, 0.3 });
    
    			// プレイヤーを描く
    			playerTexture.drawAt(playerPos);
    
    			for (const auto& food : foods)
    			{
    				// 食べ物を描く(サイズは 0.5 倍)
    				foodTexture.scaled(0.5).drawAt(food.pos);
    			}
    		}
    	}
    }
    

    11. 바닥에 떨어진 음식을 제거한다(고무로 개량한다)


    만약 C++의 문법인'등자식'을 사용한다면 CheckFood() 함수는 remove_if()에서 λ식으로 기술할 수 있고 프로그램은 짧아질 것이다.
    # include <Siv3D.hpp> // OpenSiv3D v0.4.3
    
    // 食べ物クラス
    struct Food
    {
    	Vec2 pos;
    };
    
    void Main()
    {
    	// プレイヤーのテクスチャ
    	const Texture playerTexture{ Emoji{ U"😋" } };
    
    	// 食べ物のテクスチャ
    	const Texture foodTexture{ Emoji{ U"🍰" } };
    
    	// プレイヤーが毎秒何ピクセルの速さで移動するか
    	double playerSpeed = 300.0;
    
    	// 食べ物が毎秒何ピクセルの速さで落下するか
    	double foodSpeed = 200.0;
    
    	// プレイヤーの位置
    	Vec2 playerPos{ 400, 500 };
    
    	// 食べ物
    	Array<Food> foods;
    	foods << Food{ .pos = Vec2{ 300, 100 } };
    	foods << Food{ .pos = Vec2{ 500, 200 } };
    	foods << Food{ .pos = Vec2{ 700, 300 } };
    
    	while (System::Update())
    	{
    		// デバッグ表示
    		{
    			ClearPrint();
    			Print << U"食べ物の個数: " << foods.size();
    		}
    
    		// プレイヤーの移動
    		{
    			if (KeyLeft.pressed()) // 左矢印キーが押されていたら
    			{
    				playerPos.x -= (playerSpeed * Scene::DeltaTime());
    			}
    			else if (KeyRight.pressed()) // 右矢印キーが押されていたら
    			{
    				playerPos.x += (playerSpeed * Scene::DeltaTime());
    			}
    
    			// playerPos.x を 50 以上、750 以下に収める
    			playerPos.x = Clamp(playerPos.x, 50.0, 750.0);
    		}
    
    		// 食べ物の移動
    		{
    			const double move = (foodSpeed * Scene::DeltaTime());
    
    			for (auto& food : foods)
    			{
    				food.pos.y += move;
    			}
    		}
    
    		// 地面に落ちた食べ物の消去
    		{
    			// 条件を満たす要素を配列から削除(ラムダ式で記述)
    			foods.remove_if([](const Food& food){ return (600 < food.pos.y); });
    		}
    
    		// 描画
    		{
    			// 空を描く(グラデーション)
    			Rect{ 0, 0, 800, 520 }
    				.draw(Arg::top = ColorF{ 0.1, 0.3, 0.6 }, Arg::bottom = ColorF{ 0.3, 0.7, 1.0 });
    
    			// 地面を描く
    			Rect{ 0, 520, 800, 80 }.draw(ColorF{ 0.2, 0.6, 0.3 });
    
    			// プレイヤーを描く
    			playerTexture.drawAt(playerPos);
    
    			for (const auto& food : foods)
    			{
    				// 食べ物を描く(サイズは 0.5 倍)
    				foodTexture.scaled(0.5).drawAt(food.pos);
    			}
    		}
    	}
    }
    

    12. 음식이 나온다


    시간이 지나면 음식이 나온다.
    음식이 몇 초 간격으로 나타났다는 변수foodSpawnTime를 나타내고, 지난번 음식이 나온 후 몇 초 경과한 변수timeAccumulator를 추가로 나타내며,'음식이 나타났다'는 부분에서 일정 시간 간격으로 무작위 장소에서 음식이 생성된다.Random(min, max)는 되돌아오는 min 이상max 이하의 무작위 함수다.
    # include <Siv3D.hpp> // OpenSiv3D v0.4.3
    
    // 食べ物クラス
    struct Food
    {
    	Vec2 pos;
    };
    
    void Main()
    {
    	// プレイヤーのテクスチャ
    	const Texture playerTexture{ Emoji{ U"😋" } };
    
    	// 食べ物のテクスチャ
    	const Texture foodTexture{ Emoji{ U"🍰" } };
    
    	// プレイヤーが毎秒何ピクセルの速さで移動するか
    	double playerSpeed = 300.0;
    
    	// 食べ物が毎秒何ピクセルの速さで落下するか
    	double foodSpeed = 200.0;
    
    	// 何秒ごとに食べ物が出現するか
    	double foodSpawnTime = 1.0;
    
    	// 前回の食べ物の出現から何秒経過したか
    	double timeAccumulator = 0.0;
    
    	// プレイヤーの位置
    	Vec2 playerPos{ 400, 500 };
    
    	// 食べ物
    	Array<Food> foods;
    	foods << Food{ .pos = Vec2{ 300, 100 } };
    	foods << Food{ .pos = Vec2{ 500, 200 } };
    	foods << Food{ .pos = Vec2{ 700, 300 } };
    
    	while (System::Update())
    	{
    		// デバッグ表示
    		{
    			ClearPrint();
    			Print << U"食べ物の個数: " << foods.size();
    			Print << U"timeAccumulator: " << timeAccumulator;
    		}
    
    		// プレイヤーの移動
    		{
    			if (KeyLeft.pressed()) // 左矢印キーが押されていたら
    			{
    				playerPos.x -= (playerSpeed * Scene::DeltaTime());
    			}
    			else if (KeyRight.pressed()) // 右矢印キーが押されていたら
    			{
    				playerPos.x += (playerSpeed * Scene::DeltaTime());
    			}
    
    			// playerPos.x を 50 以上、750 以下に収める
    			playerPos.x = Clamp(playerPos.x, 50.0, 750.0);
    		}
    
    		// 食べ物の出現
    		{
    			// 経過時間の蓄積
    			timeAccumulator += Scene::DeltaTime();
    
    			// 食べ物の出現時間よりも長く経過していた場合
    			while (foodSpawnTime <= timeAccumulator)
    			{
    				// 食べ物をランダムな場所に出現させる
    				foods << Food{ .pos = Vec2{ Random(50.0, 750.0), -50 } };
    
    				// 経過時間を減らす
    				timeAccumulator -= foodSpawnTime;
    			}
    		}
    
    		// 食べ物の移動
    		{
    			const double move = (foodSpeed * Scene::DeltaTime());
    
    			for (auto& food : foods)
    			{
    				food.pos.y += move;
    			}
    		}
    
    		// 地面に落ちた食べ物の消去
    		{
    			// 条件を満たす要素を配列から削除(ラムダ式で記述)
    			foods.remove_if([](const Food& food) { return (600 < food.pos.y); });
    		}
    
    		// 描画
    		{
    			// 空を描く(グラデーション)
    			Rect{ 0, 0, 800, 520 }
    				.draw(Arg::top = ColorF{ 0.1, 0.3, 0.6 }, Arg::bottom = ColorF{ 0.3, 0.7, 1.0 });
    
    			// 地面を描く
    			Rect{ 0, 520, 800, 80 }.draw(ColorF{ 0.2, 0.6, 0.3 });
    
    			// プレイヤーを描く
    			playerTexture.drawAt(playerPos);
    
    			for (const auto& food : foods)
    			{
    				// 食べ物を描く(サイズは 0.5 倍)
    				foodTexture.scaled(0.5).drawAt(food.pos);
    			}
    		}
    	}
    }
    

    13. 유저가 음식 획득


    유저가 음식에 접촉했을 때, 음식을 제거하세요.
    구분자를 사용하면 요소에 접근하고 삭제할 수 있습니다.다음 주기를 생성합니다.
    for (auto it = foods.begin(); it != foods.end();)
    {
    	if (...) // 要素を削除する場合
    	{
    		it = foods.erase(it); // 要素を削除して次の要素へ
    	}
    	else // 要素を削除しない場合
    	{
    		++it; // 次の要素へ
    	}
    }
    
    # include <Siv3D.hpp> // OpenSiv3D v0.4.3
    
    // 食べ物クラス
    struct Food
    {
    	Vec2 pos;
    };
    
    void Main()
    {
    	// プレイヤーのテクスチャ
    	const Texture playerTexture{ Emoji{ U"😋" } };
    
    	// 食べ物のテクスチャ
    	const Texture foodTexture{ Emoji{ U"🍰" } };
    
    	// プレイヤーが毎秒何ピクセルの速さで移動するか
    	double playerSpeed = 300.0;
    
    	// 食べ物が毎秒何ピクセルの速さで落下するか
    	double foodSpeed = 200.0;
    
    	// 何秒ごとに食べ物が出現するか
    	double foodSpawnTime = 1.0;
    
    	// 前回の食べ物の出現から何秒経過したか
    	double timeAccumulator = 0.0;
    
    	// プレイヤーの位置
    	Vec2 playerPos{ 400, 500 };
    
    	// 食べ物
    	Array<Food> foods;
    	foods << Food{ .pos = Vec2{ 300, 100 } };
    	foods << Food{ .pos = Vec2{ 500, 200 } };
    	foods << Food{ .pos = Vec2{ 700, 300 } };
    
    	while (System::Update())
    	{
    		// デバッグ表示
    		{
    			ClearPrint();
    			Print << U"食べ物の個数: " << foods.size();
    			Print << U"timeAccumulator: " << timeAccumulator;
    		}
    
    		// プレイヤーの移動
    		{
    			if (KeyLeft.pressed()) // 左矢印キーが押されていたら
    			{
    				playerPos.x -= (playerSpeed * Scene::DeltaTime());
    			}
    			else if (KeyRight.pressed()) // 右矢印キーが押されていたら
    			{
    				playerPos.x += (playerSpeed * Scene::DeltaTime());
    			}
    
    			// playerPos.x を 50 以上、750 以下に収める
    			playerPos.x = Clamp(playerPos.x, 50.0, 750.0);
    		}
    
    		// 食べ物の出現
    		{
    			// 経過時間の蓄積
    			timeAccumulator += Scene::DeltaTime();
    
    			// 食べ物の出現時間よりも長く経過していた場合
    			while (foodSpawnTime <= timeAccumulator)
    			{
    				// 食べ物をランダムな場所に出現させる
    				foods << Food{ .pos = Vec2{ Random(50.0, 750.0), -50 } };
    
    				// 経過時間を減らす
    				timeAccumulator -= foodSpawnTime;
    			}
    		}
    
    		// 食べ物の移動
    		{
    			const double move = (foodSpeed * Scene::DeltaTime());
    
    			for (auto& food : foods)
    			{
    				food.pos.y += move;
    			}
    		}
    
    		// 地面に落ちた食べ物の消去
    		{
    			// 条件を満たす要素を配列から削除(ラムダ式で記述)
    			foods.remove_if([](const Food& food) { return (600 < food.pos.y); });
    		}
    
    		// プレイヤーによる食べ物の獲得
    		{
    			// プレイヤーのあたり判定円
    			const Circle playerCircle{ playerPos, 60 };
    
    			// イテレータで全ての食べ物をたどる
    			for (auto it = foods.begin(); it != foods.end();)
    			{
    				// 食べ物のあたり判定円
    				const Circle foodCircle{ it->pos, 30 };
    
    				if (playerCircle.intersects(foodCircle)) // 交差していたら
    				{
    					it = foods.erase(it); // 要素を削除して次の要素へ
    				}
    				else
    				{
    					++it; // 次の要素へ
    				}
    			}
    		}
    
    		// 描画
    		{
    			// 空を描く(グラデーション)
    			Rect{ 0, 0, 800, 520 }
    				.draw(Arg::top = ColorF{ 0.1, 0.3, 0.6 }, Arg::bottom = ColorF{ 0.3, 0.7, 1.0 });
    
    			// 地面を描く
    			Rect{ 0, 520, 800, 80 }.draw(ColorF{ 0.2, 0.6, 0.3 });
    
    			// プレイヤーを描く
    			playerTexture.drawAt(playerPos);
    
    			for (const auto& food : foods)
    			{
    				// 食べ物を描く(サイズは 0.5 倍)
    				foodTexture.scaled(0.5).drawAt(food.pos);
    			}
    		}
    
    		// あたり判定円のデバッグ表示
    		{
    			// プレイヤーのあたり判定円
    			Circle{ playerPos, 60 }.draw(ColorF{ 1.0, 1.0, 1.0, 0.5 });
    
    			for (const auto& food : foods)
    			{
    				// 食べ物のあたり判定円
    				Circle{ food.pos, 30 }.draw(ColorF{ 1.0, 1.0, 1.0, 0.5 });
    			}
    		}
    	}
    }
    

    14. 음식의 변화 증가



    떨어진 음식의 변화를 증가시키다.NumFoods의 상수에 따라 음식의 종류를 결정한다.그리고 Food류에 음식의 종류를 나타내는 구성원 변수type를 첨가하여 0~(NumFood - 1) 범위의 값을 설정한다.이 값을 색인으로 사용하기 위해 음식 무늬의 배열std::array<Texture, NumFoods>을 준비했다.
    # include <Siv3D.hpp> // OpenSiv3D v0.4.3
    
    // 食べ物の種類
    constexpr size_t NumFoods = 3;
    
    // 食べ物クラス
    struct Food
    {
    	Vec2 pos;
    
    	size_t type = 0; // 0: 🍬, 1: 🍩, 2: 🍰
    };
    
    void Main()
    {
    	// プレイヤーのテクスチャ
    	const Texture playerTexture{ Emoji{ U"😋" } };
    
    	// 食べ物のテクスチャ
    	const std::array<Texture, NumFoods> foodTextures =
    	{
    		Texture{ Emoji{ U"🍬" }},
    		Texture{ Emoji{ U"🍩" }},
    		Texture{ Emoji{ U"🍰" }},
    	};
    
    	// プレイヤーが毎秒何ピクセルの速さで移動するか
    	double playerSpeed = 300.0;
    
    	// 食べ物が毎秒何ピクセルの速さで落下するか
    	double foodSpeed = 200.0;
    
    	// 何秒ごとに食べ物が出現するか
    	double foodSpawnTime = 1.0;
    
    	// 前回の食べ物の出現から何秒経過したか
    	double timeAccumulator = 0.0;
    
    	// プレイヤーの位置
    	Vec2 playerPos{ 400, 500 };
    
    	// 食べ物
    	Array<Food> foods;
    	foods << Food{ .pos = Vec2{ 300, 100 }, .type = 2 };
    	foods << Food{ .pos = Vec2{ 500, 200 }, .type = 1 };
    	foods << Food{ .pos = Vec2{ 700, 300 }, .type = 0 };
    
    	while (System::Update())
    	{
    		// デバッグ表示
    		{
    			ClearPrint();
    			Print << U"食べ物の個数: " << foods.size();
    			Print << U"timeAccumulator: " << timeAccumulator;
    		}
    
    		// プレイヤーの移動
    		{
    			if (KeyLeft.pressed()) // 左矢印キーが押されていたら
    			{
    				playerPos.x -= (playerSpeed * Scene::DeltaTime());
    			}
    			else if (KeyRight.pressed()) // 右矢印キーが押されていたら
    			{
    				playerPos.x += (playerSpeed * Scene::DeltaTime());
    			}
    
    			// playerPos.x を 50 以上、750 以下に収める
    			playerPos.x = Clamp(playerPos.x, 50.0, 750.0);
    		}
    
    		// 食べ物の出現
    		{
    			// 経過時間の蓄積
    			timeAccumulator += Scene::DeltaTime();
    
    			// 食べ物の出現時間よりも長く経過していた場合
    			while (foodSpawnTime <= timeAccumulator)
    			{
    				// 食べ物をランダムな場所に出現させる
    				foods << Food{ .pos = Vec2{ Random(50.0, 750.0), -50 },
    					.type = Random<size_t>(0, NumFoods - 1) };
    
    				// 経過時間を減らす
    				timeAccumulator -= foodSpawnTime;
    			}
    		}
    
    		// 食べ物の移動
    		{
    			const double move = (foodSpeed * Scene::DeltaTime());
    
    			for (auto& food : foods)
    			{
    				food.pos.y += move;
    			}
    		}
    
    		// 地面に落ちた食べ物の消去
    		{
    			// 条件を満たす要素を配列から削除(ラムダ式で記述)
    			foods.remove_if([](const Food& food) { return (600 < food.pos.y); });
    		}
    
    		// プレイヤーによる食べ物の獲得
    		{
    			// プレイヤーのあたり判定円
    			const Circle playerCircle{ playerPos, 60 };
    
    			// イテレータで全ての食べ物をたどる
    			for (auto it = foods.begin(); it != foods.end();)
    			{
    				// 食べ物のあたり判定円
    				const Circle foodCircle{ it->pos, 30 };
    
    				if (playerCircle.intersects(foodCircle)) // 交差していたら
    				{
    					it = foods.erase(it); // 要素を削除して次の要素へ
    				}
    				else
    				{
    					++it; // 次の要素へ
    				}
    			}
    		}
    
    		// 描画
    		{
    			// 空を描く(グラデーション)
    			Rect{ 0, 0, 800, 520 }
    				.draw(Arg::top = ColorF{ 0.1, 0.3, 0.6 }, Arg::bottom = ColorF{ 0.3, 0.7, 1.0 });
    
    			// 地面を描く
    			Rect{ 0, 520, 800, 80 }.draw(ColorF{ 0.2, 0.6, 0.3 });
    
    			// プレイヤーを描く
    			playerTexture.drawAt(playerPos);
    
    			for (const auto& food : foods)
    			{
    				// 食べ物を描く(サイズは 0.5 倍)
    				foodTextures[food.type].scaled(0.5).drawAt(food.pos);
    			}
    		}
    	}
    }
    

    15. 음식에 대한 평점


    점수를 도입하면 음식에 따라 점수가 달라진다.Foodtype를 인덱스로 접근할 수 있는 득점 배열std::array<int32, NumFoods>을 준비한다.
    # include <Siv3D.hpp> // OpenSiv3D v0.4.3
    
    // 食べ物の種類
    constexpr size_t NumFoods = 3;
    
    // 食べ物クラス
    struct Food
    {
    	Vec2 pos;
    
    	size_t type = 0; // 0: 🍬, 1: 🍩, 2: 🍰
    };
    
    void Main()
    {
    	// プレイヤーのテクスチャ
    	const Texture playerTexture{ Emoji{ U"😋" } };
    
    	// 食べ物のテクスチャ
    	const std::array<Texture, NumFoods> foodTextures =
    	{
    		Texture{ Emoji{ U"🍬" }},
    		Texture{ Emoji{ U"🍩" }},
    		Texture{ Emoji{ U"🍰" }},
    	};
    
    	// 食べ物の獲得スコア
    	const std::array<int32, NumFoods> foodScores =
    	{
    		10, 20, 100
    	};
    
    	// プレイヤーが毎秒何ピクセルの速さで移動するか
    	double playerSpeed = 300.0;
    
    	// 食べ物が毎秒何ピクセルの速さで落下するか
    	double foodSpeed = 200.0;
    
    	// 何秒ごとに食べ物が出現するか
    	double foodSpawnTime = 1.0;
    
    	// 前回の食べ物の出現から何秒経過したか
    	double timeAccumulator = 0.0;
    
    	// プレイヤーの位置
    	Vec2 playerPos{ 400, 500 };
    
    	// スコア
    	int32 score = 0;
    
    	// 食べ物
    	Array<Food> foods;
    	foods << Food{ .pos = Vec2{ 300, 100 }, .type = 2 };
    	foods << Food{ .pos = Vec2{ 500, 200 }, .type = 1 };
    	foods << Food{ .pos = Vec2{ 700, 300 }, .type = 0 };
    
    	while (System::Update())
    	{
    		// デバッグ表示
    		{
    			ClearPrint();
    			Print << U"食べ物の個数: " << foods.size();
    			Print << U"timeAccumulator: " << timeAccumulator;
    			Print << U"スコア: " << score;
    		}
    
    		// プレイヤーの移動
    		{
    			if (KeyLeft.pressed()) // 左矢印キーが押されていたら
    			{
    				playerPos.x -= (playerSpeed * Scene::DeltaTime());
    			}
    			else if (KeyRight.pressed()) // 右矢印キーが押されていたら
    			{
    				playerPos.x += (playerSpeed * Scene::DeltaTime());
    			}
    
    			// playerPos.x を 50 以上、750 以下に収める
    			playerPos.x = Clamp(playerPos.x, 50.0, 750.0);
    		}
    
    		// 食べ物の出現
    		{
    			// 経過時間の蓄積
    			timeAccumulator += Scene::DeltaTime();
    
    			// 食べ物の出現時間よりも長く経過していた場合
    			while (foodSpawnTime <= timeAccumulator)
    			{
    				// 食べ物をランダムな場所に出現させる
    				foods << Food{ .pos = Vec2{ Random(50.0, 750.0), -50 },
    					.type = Random<size_t>(0, NumFoods - 1) };
    
    				// 経過時間を減らす
    				timeAccumulator -= foodSpawnTime;
    			}
    		}
    
    		// 食べ物の移動
    		{
    			const double move = (foodSpeed * Scene::DeltaTime());
    
    			for (auto& food : foods)
    			{
    				food.pos.y += move;
    			}
    		}
    
    		// 地面に落ちた食べ物の消去
    		{
    			// 条件を満たす要素を配列から削除(ラムダ式で記述)
    			foods.remove_if([](const Food& food) { return (600 < food.pos.y); });
    		}
    
    		// プレイヤーによる食べ物の獲得
    		{
    			// プレイヤーのあたり判定円
    			const Circle playerCircle{ playerPos, 60 };
    
    			// イテレータで全ての食べ物をたどる
    			for (auto it = foods.begin(); it != foods.end();)
    			{
    				// 食べ物のあたり判定円
    				const Circle foodCircle{ it->pos, 30 };
    
    				if (playerCircle.intersects(foodCircle)) // 交差していたら
    				{
    					score += foodScores[it->type]; // スコアを加算(要素を削除する前に it を使うこと!)
    
    					it = foods.erase(it); // 要素を削除して次の要素へ
    				}
    				else
    				{
    					++it; // 次の要素へ
    				}
    			}
    		}
    
    		// 描画
    		{
    			// 空を描く(グラデーション)
    			Rect{ 0, 0, 800, 520 }
    				.draw(Arg::top = ColorF{ 0.1, 0.3, 0.6 }, Arg::bottom = ColorF{ 0.3, 0.7, 1.0 });
    
    			// 地面を描く
    			Rect{ 0, 520, 800, 80 }.draw(ColorF{ 0.2, 0.6, 0.3 });
    
    			// プレイヤーを描く
    			playerTexture.drawAt(playerPos);
    
    			for (const auto& food : foods)
    			{
    				// 食べ物を描く(サイズは 0.5 倍)
    				foodTextures[food.type].scaled(0.5).drawAt(food.pos);
    			}
    		}
    	}
    }
    
    ▶ 이어서 Day 3 (준비 중)

    좋은 웹페이지 즐겨찾기