添加bgm

This commit is contained in:
2026-04-26 11:39:50 +08:00
parent e80dd15ebf
commit 295dba8bd7
6 changed files with 295 additions and 0 deletions
+244
View File
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Tetris.h"
#include <string>
#define MAX_LOADSTRING 100
#define GAME_TIMER_ID 1
@@ -8,6 +9,11 @@
HINSTANCE hInst;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];
bool bgmEnabled = true;
static bool bgmPlaying = false;
static bool bgmUsingMci = false;
static constexpr const wchar_t* kBgmAlias = L"TereisBgm";
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
@@ -20,6 +26,229 @@ static void ResetGameTimer(HWND hWnd)
SetTimer(hWnd, GAME_TIMER_ID, currentFallInterval > 0 ? currentFallInterval : GAME_TIMER_INTERVAL, nullptr);
}
static std::wstring BuildAssetPath(const wchar_t* relativePath)
{
wchar_t modulePath[MAX_PATH] = {};
GetModuleFileNameW(nullptr, modulePath, MAX_PATH);
std::wstring basePath(modulePath);
size_t lastSlash = basePath.find_last_of(L"\\/");
if (lastSlash != std::wstring::npos)
{
basePath.resize(lastSlash);
}
std::wstring projectRelative = basePath + L"\\..\\..\\" + relativePath;
wchar_t fullPath[MAX_PATH] = {};
DWORD result = GetFullPathNameW(projectRelative.c_str(), MAX_PATH, fullPath, nullptr);
if (result > 0 && result < MAX_PATH)
{
return fullPath;
}
return projectRelative;
}
static std::wstring BuildWorkingDirAssetPath(const wchar_t* relativePath)
{
wchar_t currentDirectory[MAX_PATH] = {};
DWORD length = GetCurrentDirectoryW(MAX_PATH, currentDirectory);
if (length == 0 || length >= MAX_PATH)
{
return L"";
}
std::wstring candidate = std::wstring(currentDirectory) + L"\\" + relativePath;
wchar_t fullPath[MAX_PATH] = {};
DWORD result = GetFullPathNameW(candidate.c_str(), MAX_PATH, fullPath, nullptr);
if (result > 0 && result < MAX_PATH)
{
return fullPath;
}
return candidate;
}
static bool FileExists(const std::wstring& path)
{
DWORD attributes = GetFileAttributesW(path.c_str());
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
}
static RECT GetMusicButtonRect(HWND hWnd)
{
RECT clientRect;
GetClientRect(hWnd, &clientRect);
int clientWidth = clientRect.right - clientRect.left;
int clientHeight = clientRect.bottom - clientRect.top;
int scaleX = MulDiv(clientWidth, 1000, WINDOW_CLIENT_WIDTH);
int scaleY = MulDiv(clientHeight, 1000, WINDOW_CLIENT_HEIGHT);
int scale = (scaleX < scaleY) ? scaleX : scaleY;
if (scale < 500)
{
scale = 500;
}
int layoutWidth = MulDiv(WINDOW_CLIENT_WIDTH, scale, 1000);
int offsetX = (clientWidth - layoutWidth) / 2;
int offsetY = 0;
int size = MulDiv(28, scale, 1000);
if (size < 22)
{
size = 22;
}
int marginRight = MulDiv(12, scale, 1000);
if (marginRight < 6)
{
marginRight = 6;
}
int marginBottom = MulDiv(12, scale, 1000);
if (marginBottom < 6)
{
marginBottom = 6;
}
RECT buttonRect =
{
offsetX + layoutWidth - marginRight - size,
offsetY + MulDiv(WINDOW_CLIENT_HEIGHT, scale, 1000) - marginBottom - size,
offsetX + layoutWidth - marginRight,
offsetY + MulDiv(WINDOW_CLIENT_HEIGHT, scale, 1000) - marginBottom
};
return buttonRect;
}
static bool IsPointInRect(const RECT& rect, int x, int y)
{
return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;
}
static bool TryPlayMciLoop(const std::wstring& path, bool forceMpegVideo)
{
if (!FileExists(path))
{
return false;
}
mciSendStringW((std::wstring(L"close ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
std::wstring openCommand = L"open \"" + path + L"\" ";
if (forceMpegVideo)
{
openCommand += L"type mpegvideo ";
}
openCommand += L"alias ";
openCommand += kBgmAlias;
if (mciSendStringW(openCommand.c_str(), nullptr, 0, nullptr) != 0)
{
return false;
}
std::wstring playCommand = std::wstring(L"play ") + kBgmAlias + L" repeat";
if (mciSendStringW(playCommand.c_str(), nullptr, 0, nullptr) != 0)
{
mciSendStringW((std::wstring(L"close ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
return false;
}
bgmPlaying = true;
bgmUsingMci = true;
return true;
}
static void StopBackgroundMusic()
{
if (bgmUsingMci)
{
mciSendStringW((std::wstring(L"stop ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
mciSendStringW((std::wstring(L"close ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
}
else
{
PlaySoundW(nullptr, nullptr, 0);
}
bgmPlaying = false;
bgmUsingMci = false;
}
static void StartBackgroundMusic()
{
if (!bgmEnabled || bgmPlaying)
{
return;
}
const wchar_t* bgmWavRelativePath = L"assets\\audio\\bgm.wav";
const std::wstring bgmWavCandidates[] =
{
BuildAssetPath(bgmWavRelativePath),
BuildWorkingDirAssetPath(bgmWavRelativePath)
};
for (const std::wstring& candidate : bgmWavCandidates)
{
if (FileExists(candidate) &&
PlaySoundW(candidate.c_str(), nullptr, SND_FILENAME | SND_ASYNC | SND_LOOP))
{
bgmPlaying = true;
bgmUsingMci = false;
return;
}
}
const wchar_t* oggRelativePath = L"assets\\audio\\bgm.ogg";
const std::wstring oggCandidates[] =
{
BuildAssetPath(oggRelativePath),
BuildWorkingDirAssetPath(oggRelativePath)
};
for (const std::wstring& candidate : oggCandidates)
{
if (TryPlayMciLoop(candidate, false) || TryPlayMciLoop(candidate, true))
{
return;
}
}
const wchar_t* fallbackWavRelativePath = L"assets\\audio\\background.wav";
const std::wstring fallbackWavCandidates[] =
{
BuildAssetPath(fallbackWavRelativePath),
BuildWorkingDirAssetPath(fallbackWavRelativePath)
};
for (const std::wstring& candidate : fallbackWavCandidates)
{
if (FileExists(candidate) &&
PlaySoundW(candidate.c_str(), nullptr, SND_FILENAME | SND_ASYNC | SND_LOOP))
{
bgmPlaying = true;
bgmUsingMci = false;
return;
}
}
bgmEnabled = false;
}
static void ToggleBackgroundMusic(HWND hWnd)
{
bgmEnabled = !bgmEnabled;
if (bgmEnabled)
{
StartBackgroundMusic();
}
else
{
StopBackgroundMusic();
}
InvalidateRect(hWnd, nullptr, FALSE);
}
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
@@ -124,6 +353,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_CREATE:
srand((unsigned int)time(nullptr));
ReturnToMainMenu();
StartBackgroundMusic();
ResetGameTimer(hWnd);
InvalidateRect(hWnd, nullptr, FALSE);
break;
@@ -295,6 +525,19 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_SIZE:
InvalidateRect(hWnd, nullptr, FALSE);
break;
case WM_LBUTTONUP:
{
int mouseX = static_cast<short>(LOWORD(lParam));
int mouseY = static_cast<short>(HIWORD(lParam));
RECT musicButtonRect = GetMusicButtonRect(hWnd);
if (IsPointInRect(musicButtonRect, mouseX, mouseY))
{
ToggleBackgroundMusic(hWnd);
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
case WM_KEYDOWN:
if (currentScreen == SCREEN_MENU)
{
@@ -580,6 +823,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
break;
case WM_DESTROY:
KillTimer(hWnd, GAME_TIMER_ID);
StopBackgroundMusic();
PostQuitMessage(0);
break;
default: