iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
📚

[Week 2] Day 3: Block Placement and Destruction (Fixed Stage)

に公開

Day 3: Block Placement + Destruction (Fixed 1-Stage)

Day 3 advances the project to a state where "blocks break and the game starts to feel like a game."

  • Add the Block class

  • Give GameScene a block array and place them in a fixed single stage

  • Implement collision detection between the ball's AABB and the block's AABB

  • Destroy (remove) the collided block

  • Add scoring (a placeholder score is fine for Day 3)

  • Clear the stage when all blocks are destroyed (provisionally move to ResultScene for Day 3)

Durability (HP) will be added on Day 5, and the multi-stage system on Day 6.


Today's Goal (Done)

  • Add Block (AABB + rendering)
  • Place blocks (rows x columns)
  • Destroy blocks upon ball collision
  • Provisional clear transition when all blocks are destroyed

List of Modified Files

Added (Full content)

  • src/Game/Block.h
  • src/Game/Block.cpp

Modified (Full content as of today)

  • src/Game/Ball.h
  • src/Game/Ball.cpp
  • src/Scene/GameScene.h
  • src/Scene/GameScene.cpp

Implementation Strategy (Day 3)

Collision Handling (Keep it simple for Day 3)

Since we will improve reflection quality on Day 4, the priority for Day 3 is just to "make them break."

  • Reflect on the Y-axis (provisional) upon collision
  • Refine overlap correction on Day 4

Block Management

  • Use std::vector<Block>
  • Manage survival status using Block::alive
  • Render and perform collision detection only for alive blocks

Added Files (Full Content)

src/Game/Block.h

#pragma once
#include "Aabb.h"

enum class QualityLevel;

class Block
{
public:
	Block() = default;
	Block(float l, float t, float r, float b);

	void Draw(QualityLevel q, int ox, int oy) const;
	Aabb GetAabb() const { return m_aabb; }

	bool IsAlive() const { return m_alive; }
	void Kill() { m_alive = false; }

private:
	Aabb m_aabb{};
	bool m_alive = true;
};

src/Game/Block.cpp

#include "Block.h"
#include "../Core/Quality.h"

#include "DxLib.h"

Block::Block(float l, float t, float r, float b)
{
	m_aabb.l = l;
	m_aabb.t = t;
	m_aabb.r = r;
	m_aabb.b = b;
	m_alive = true;
}

void Block::Draw(QualityLevel q, int ox, int oy) const
{
	if (!m_alive) return;

	const int x0 = (int)m_aabb.l + ox;
	const int y0 = (int)m_aabb.t + oy;
	const int x1 = (int)m_aabb.r + ox;
	const int y1 = (int)m_aabb.b + oy;

	// Change the look slightly based on Quality (without being heavy)
	const int fill = (q == QualityLevel::High)
		? GetColor(80, 170, 240)
		: GetColor(60, 120, 170);

	DrawBox(x0, y0, x1, y1, fill, TRUE);
	DrawBox(x0, y0, x1, y1, GetColor(20, 20, 30), FALSE);
}

Modification: GameScene.h (Diff: Adding Block array and functions)

#pragma once
#include "IScene.h"
#include <vector>

class SceneManager;
class Camera2D;

class Paddle;
class Ball;
class Block;

class GameScene : public IScene
{
public:
	explicit GameScene(SceneManager* mgr);
	~GameScene();

	void Enter() override;
	void Update(float dt, const class Input& input) override;
	void Draw() override;

private:
	SceneManager* m_mgr = nullptr;

	Camera2D* m_camera = nullptr;
	Paddle* m_paddle = nullptr;
	Ball* m_ball = nullptr;

	std::vector<Block> m_blocks;

	int m_lives = 3;
	int m_score = 0;
	float m_time = 0.0f;

	void BuildStage_();
	void CheckBallVsBlocks_();
	int AliveBlocks_() const;
};

Modification: GameScene.cpp (Diff: Stage generation + Collision processing)

Enter(): BuildStage_()

void GameScene::Enter()
{
	m_time = 0.0f;
	m_lives = 3;
	m_score = 0;

	m_camera->Reset();
	m_paddle->Reset();
	m_ball->ResetOnPaddle(*m_paddle);

	BuildStage_();
}

BuildStage_(): emplace_back Block with rows/cols

void GameScene::BuildStage_()
{
	m_blocks.clear();

	// Fixed 1-stage (Stage system to be added on Day 6)
	const int rows = 5;
	const int cols = 10;

	const float pad = 6.0f;
	const float bw = 100.0f;
	const float bh = 28.0f;

	const float startX = 80.0f;
	const float startY = 120.0f;

	for (int r = 0; r < rows; r++)
	{
		for (int c = 0; c < cols; c++)
		{
			const float x0 = startX + c * (bw + pad);
			const float y0 = startY + r * (bh + pad);
			const float x1 = x0 + bw;
			const float y1 = y0 + bh;

			m_blocks.emplace_back(x0, y0, x1, y1);
		}
	}
}

CheckBallVsBlocks_():

void GameScene::CheckBallVsBlocks_()
{
	const Aabb ball = m_ball->GetAabb();

	for (auto& blk : m_blocks)
	{
		if (!blk.IsAlive()) continue;

		const Aabb a = blk.GetAabb();
		if (!IntersectAabb(a, ball)) continue;

		blk.Kill();
		m_score += 100; // Placeholder for Day 3

		Audio::Instance().PlaySe("hit");

		// Provisional for Day 3: Only Y-axis reflection
		m_ball->BounceY();
		break; // Destroy only one block per frame
	}
}

AliveBlocks_():

Count surviving blocks

int GameScene::AliveBlocks_() const
{
	int n = 0;
	for (const auto& b : m_blocks)
		if (b.IsAlive()) n++;
	return n;
}

Operation Check Checklist

  • Blocks are displayed
  • Blocks disappear when hit by the ball
  • Only one block is destroyed per frame (controlled by break)
  • Score is increased
  • Transition to ResultScene when all blocks are cleared (provisional clear)

Things to do on Day 4 (Preview)

Day 4 is the day to improve reflection quality.

  • Correctly reflect X/Y based on "which side was hit"
  • Overlap correction (push-back)
  • Replace reflection on block collision from BounceY()

Day 3 focuses on reaching the "destruction" functionality as quickly as possible, and Day 4 will focus on refining the quality.

Discussion