添加bgm
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user