← 作品一覧

IMPLEMENTATION NOTES / C++ / SFML

Pac-Man (SFML Remake)

C++とSFMLで制作したPac-Manリメイク。衝突判定、候補マスから目標への二乗距離を比較するゴーストの方向選択、スプライト描画などを学習。

C++SFMLCMake

IMPLEMENTATION / EQUATIONS & SOURCE

入力から出力まで、コードで追う。

各節は「何を計算するか → 数式 → 実際のコード」の順に読めます。コードは動作する実装からの抜粋で、単独実行用ではありません。省略した周辺処理は参照元で確認できます。

01 / ゴーストは経路全体ではなく次の1歩を選ぶ

壁と逆走方向を除いた候補マスごとに、目標までの二乗距離を比較します。平方根は順位を変えないので計算不要です。最短経路を探索するA*ではなく、交差点ごとに局所的な方向を選ぶルールです。同距離ならコードの<=により後の候補で上書きされます。Distanceの引数はintなので、小数座標は整数へ変換した後に比較します。

計算式
D²(q,t) = (qx−tx)² + (qy−ty)²
nextDirection = arg min D²(candidate,target)
PacMan/Ghosts.cpp : 49COMMIT c6a12fa
float Distance(int x, int y, int x1, int y1)
{
	return ((x - x1) * (x - x1)) + ((y - y1) * (y - y1));
}

// the original game didnt use a pathfinding algorithm
// simple distance comparisons are fine enough
Dir GetShortestDir(const std::vector<Dir>& squares, const Ghost& ghost, sf::Vector2f target)
{
	int min_dist = 20000000;
	Dir min_dir = NONE;
	if (squares.size() == 0)
		printf("EMPTY\n");

	for (auto dir : squares) {
		sf::Vector2f square = ghost.pos + dir_addition[dir];
		float dir_dist = Distance(target.x, target.y, square.x, square.y);
		if (dir_dist <= min_dist) {
			min_dir = dir;
			min_dist = dir_dist;
		}
	}

	return min_dir;
}
該当箇所をGitHubで読む ↗

02 / 同じ方向選択でも目標を変えると性格が変わる

赤は現在のプレイヤー位置、桃は進行方向の4マス先、青はプレイヤー2マス先を基準に赤の位置を反転した点を目標にします。橙はプレイヤーとの二乗距離が64未満なら隅(0,31)へ切り替えます。方向選択ロジックを共有しつつ、目標の計算を分けています。

計算式
tred = player
tpink = player + 4·direction
a = player + 2·direction, tblue = 2a − red
torange = corner if D² < 64, otherwise player

この式は公開コードのロジックを表しています。原作の全仕様や、自分独自のアルゴリズム開発を主張するものではありません。

PacMan/Ghosts.cpp : 74COMMIT c6a12fa
sf::Vector2f BlinkyUpdate(Ghost& ghost)
{
	return gState.player->pos;
}
sf::Vector2f PinkyUpdate(Ghost& ghost)
{
	return gState.player->pos + dir_addition[gState.player->cur_dir] * 4;
}
sf::Vector2f InkyUpdate(Ghost& ghost)
{
	sf::Vector2f target = gState.player->pos + dir_addition[gState.player->cur_dir] * 2;
	sf::Vector2f offset = gState.ghosts[RED]->pos - target;
	target = target + offset * -1;
	return target;
}
sf::Vector2f ClydeUpdate(Ghost& ghost)
{
	sf::Vector2f target = gState.player->pos;

	// if clyde is 8 tiles away, target player, else target corner
	if (Distance(ghost.pos.x, ghost.pos.y, gState.player->pos.x, gState.player->pos.y) < 64)
		target = { 0,31 };
	return target;
}
該当箇所をGitHubで読む ↗

SOURCE / SCOPE

実装の読み方。

各コード抜粋はリンク先の固定コミットから取得しています。数式は該当処理を説明するための表記です。性能や品質の数値は、掲載した計算例を除き新しい実測を行っていません。ビルド手順と依存環境は各リポジトリを参照してください。

リポジトリへ ↗