EasyxEx扩展库
easyx对于初学者来说,可能一些同学觉得并不easyx,其实要用好这个库,需要有一定的win32编程基础,GDI图像处理知识。easyx原生没有对透明通道图片的支持,比如透明格式的png图片。这个扩展库增加了对透明png图片绘制的支持,简单的界面按钮管理和音乐声音播放函数,专门为制作小游戏扩展,使用非常方便。
//绘制带alpha通道的图片 void draw_image( IMAGE& image, //绘制的图片 int x, int y, //绘制的位置 int w, int h, //绘制的大小。如果是负值,则会对图片进行反转 int srcX, int srcY, //源图片起始位置 int srcW, int srcH, //源图片大小 BYTE alpha = 255 //图片透明度 );
这个函数可以使用透明通道的png图片绘制,可以缩放、设置透明度,并支持镜像(w或者h设置成负值)。函数的实现用的是GDI的AlphaBlend()函数,这函数在Windows 2D软件图像处理方面,性能是相当优秀的。png格式的图片加载,直接用easyx的loadimage就可以,加载之后,不要再进行其他(比如缩放)之类的操作,会破坏png的alpha通道。loadimage加载的时候,可以选择缩放大小,这个不会破坏png的alpha通道。
//绘制旋转的图片 void rotate_draw( IMAGE& image, //图片 int ox, int oy, //旋转中心点 int x, int y, //绘制的位置 int w, int h, //绘制的大小。如果是负值,则会对图片进行反转 int srcX, int srcY, //源图片起始位置 int srcW, int srcH, //源图片大小 float angle //旋转角度 );
然后就是这个比较强大的函数,支持图片的缩放、透明度、镜像,最主要的是支持旋转,直接的旋转绘制。实现原理是使用GDI的矩阵操作,图像处理的基本变换,平移缩放旋转,都封装进去了。有源码,有兴趣的同学,可以借鉴。
GDI的2D图像处理,止步于此,效果更好点的是Windows的Gdiplus。这是软件图像处理方面,再好的效果就是用硬件加速了,这是题外话。
头文件(EasyxEx.h):
#pragma once #define _CRT_SECURE_NO_WARNINGS //easyx 扩展库 #include <conio.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define _USE_MATH_DEFINES #include <math.h> #include <graphics.h> //easyx #include <mmsystem.h> //多媒体库 //弧度角度转换常量 #ifndef M_RD #define M_RD 0.01745329251994329576923690768489 #define M_INV_RD 57.295779513082320876798154814092 #endif #pragma comment (lib, "msimg32.lib") #pragma comment (lib, "winmm.lib") //----------------------------------------------------------------------------- // // 全局常量、变量 // //----------------------------------------------------------------------------- // 保存窗口大小的变量 extern int WINX; //1024(默认) extern int WINY; //720(默认) // 标记程序是否正在运行,设置 false 以结束程序主循环 extern bool running; // 记录鼠标坐标 extern POINT mouse; // 记录鼠标按钮信息 extern int mouse_button; // 是否开启调试模式 extern bool debug; //----------------------------------------------------------------------------- // // 键盘输入 // //----------------------------------------------------------------------------- // _getch() 常用键盘码 enum KEYCODE { KEY_ENTER = 13, //回车 KEY_ESC = 27, //ESC KEY_SPACE = 32, //空格 KEY_F1 = 59, KEY_F2 = 60, KEY_F3 = 61, KEY_F4 = 62, KEY_F5 = 63, KEY_F6 = 64, KEY_F7 = 65, KEY_F8 = 66, KEY_F9 = 67, KEY_F10 = 68, KEY_UP = 72, //上 KEY_LEFT = 75, //左 KEY_RIGHT = 77, //右 KEY_DOWN = 80, //下 KEY_F11 = 133, KEY_F12 = 134, }; // 获取虚拟按键状态 // key 虚拟键 VK 值,比如 VK_SPACE,VK_ESCAPE 等。 // 返回值: bool。true 代表这个按键处于按下状态。 // // 虚拟按键和 _kbhit() 冲突,easyx 没有提供按键消息的接口获取按键, // 但大部分情况下,我们只使用一种方案就可以了。 bool keystate(int key); //----------------------------------------------------------------------------- // // 游戏场景状态 // //----------------------------------------------------------------------------- // 常用的几个游戏场景状态,可以用来标记各个游戏场景、界面方案。 // 在游戏进程处理过程中,通过 switch 开关,对不同的场景、界面进行管理操作。 enum GAME_STATE { GAME_NULL, //空值 GAME_MENU, //主菜单 GAME_LEVELS, //关卡选择 GAME_LOADING, //加载 GAME_PLAY, //运行 GAME_PAUSE, //暂停 GAME_WIN, //胜利 GAME_LOST, //失败 GAME_END, //结束 GAME_SETTING, //设置 GAME_HELP, //帮助 GAME_HISTORY, //历史 GAME_EXIT, //退出 GAME_LAST, //标记最后一个状态 }; //----------------------------------------------------------------------------- // // 界面按钮 // //----------------------------------------------------------------------------- // 添加一个按钮 // state 游戏场景 // command 可以是任何自定义的值,标识按钮点击的命令 // text 按钮文字 // x, y 按钮位置 // width, height 按钮大小 // 返回值: 按钮 ID int add_button(GAME_STATE state, int command, PCTSTR text, int x, int y, int width, int height); // 获取游戏场景按钮数量 // state 当前游戏场景 // 返回值: 当前场景按钮数量 int button_count(GAME_STATE state); // 获取游戏场景按钮 ID,这个函数和 button_count() 用来遍历场景按钮 // state 当前游戏场景 // index 当前游戏场景按钮索引 // 返回值 按钮 ID int button_index(GAME_STATE state, int index); // 获取按钮命令 // id 按钮 ID // 返回值: 按钮命令值 int button_command(int id); // 获取按钮标题 // id 按钮 ID // 返回值: 按钮标题 PCTSTR button_text(int id); // 获取按钮显示范围 // id 按钮 ID // 返回值: RECT矩形 RECT button_bounds(int id); // 显示(隐藏)按钮 // id 按钮 ID // value true:显示;false:隐藏。 void show_button(int id, bool value); // 按钮是否可见 // id 按钮 ID // 返回值: true:显示;false:隐藏。 bool button_visible(int id); // 检测鼠标点击了哪个按钮 // state 当前游戏场景 // 返回值 返回鼠标点击的按钮 ID // // 当在鼠标按下或弹起的事件中,使用 hit_button(state) 函数的时候, // 会根据 mouse 当前的位置,判断鼠标点击了哪个按钮,并返回按钮的 ID。 int hit_button(GAME_STATE state); // 按钮的绘制例子 // 因为是轻量级按钮管理,绘制就不集成了,功能都集成进去代码太多 // 根据需要单独写个函数进行绘制就行。 /* void draw_buttons(GAME_STATE state) { int id; //按钮 ID RECT bounds; //按钮显示范围 int w; //按钮宽度 int h; //按钮高度 int cx; //字体显示宽度 int cy; //字体显示高度 //设置按钮字体 settextstyle(24, 0, TEXT("微软雅黑"), 0, 0, 600, false, false, false); //遍历场景按钮 for (int i = 0; i < button_count(state); ++i) { //获取按钮ID id = button_index(state, i); //获取按钮显示范围 bounds = button_bounds(id); w = bounds.right - bounds.left; h = bounds.bottom - bounds.top; //鼠标位于按钮内部 if (PtInRect(&bounds, mouse)) { if (mouse_button == MK_LBUTTON) { //按钮按下,可以替换成绘制图片 setlinecolor(RGB(0, 0, 0)); setfillcolor(RGB(160, 81, 22)); settextcolor(RGB(211, 211, 211)); } else { //按钮激活,可以替换成绘制图片 setlinecolor(RGB(0, 0, 0)); setfillcolor(RGB(238, 118, 33)); settextcolor(WHITE); } } else { //按钮普通样式,可以替换成绘制图片 setlinecolor(RGB(0, 0, 0)); setfillcolor(RGB(205, 102, 29)); settextcolor(BLACK); } //绘制按钮轮廓,如果绘制图片按钮,这里就不用绘制 fillrectangle(bounds.left, bounds.top, bounds.right, bounds.bottom); rectangle(bounds.left, bounds.top, bounds.right, bounds.bottom); //居中显示字体 cx = textwidth(button_text(id)); cy = textheight(button_text(id)); outtextxy(bounds.left + (w - cx) / 2, bounds.top + (h - cy) / 2, button_text(id)); } } */ //----------------------------------------------------------------------------- // // 数学函数 // //----------------------------------------------------------------------------- // 获得 n 以内的随机数,值的范围(0 ~ n - 1) int random(int n); // 获得 a 到 b - 1 之间的随机数 int random(int a, int b); // 获取弧度 float radian(float x, float y); // 获取角度 float angle(float x, float y); // 获取两点的距离 float distance(float x1, float y1, float x2, float y2); //----------------------------------------------------------------------------- // // 二维向量 // //----------------------------------------------------------------------------- struct vec2 { float x, y; vec2() : x(), y() {} vec2(float vx, float vy) : x(vx), y(vy){} }; // 相加 vec2 add(const vec2& v1, const vec2& v2); // 相减 vec2 sub(const vec2& v1, const vec2& v2); // 相乘 vec2 mul(const vec2& v1, const vec2& v2); // 旋转 vec2 rotate(const vec2& v, float angle); // 距离原点的距离 float length(const vec2& v); // 获取两个向量的距离 float distance(const vec2& v1, const vec2& v2); // 归一化 vec2 normalize(const vec2& v); // 获取角度 float angle(const vec2& v); //----------------------------------------------------------------------------- // // GDI矩阵操作 // //----------------------------------------------------------------------------- typedef XFORM mat2; // 初始化单位矩阵 void identity(mat2& m); //移动矩阵 void translate(mat2& m, float x, float y); //旋转矩阵 void rotate(mat2& m, double angle); //缩放矩阵 void scale(mat2& m, float x, float y); //读取 HDC 矩阵 mat2 getmat(HDC dc); //设置 HDC 的矩阵 void setmat(HDC dc, mat2& m); //----------------------------------------------------------------------------- // // easyx扩展绘图函数 // //----------------------------------------------------------------------------- // 这个函数可以使用透明通道的 png 图片绘制,可以缩放、设置透明度,并支持镜像(w 或者 h 设置成负值) // 加载 png 格式的图片,直接用 easyx 的 loadimage() 就可以。加载之后不要再用 rotateimage() // 进行缩放之类的操作,会破坏 png 的 alpha 通道。loadimage() 加载的时候,设置图片大小不会破坏 // png 的 alpha 通道。 // 绘制带 alpha 通道的图片 // image 绘制的图片 // x, y 目标绘制的位置 // w, h 目标绘制的大小。如果是负值,则会对图片进行镜像反转 // srcX, srcY 源图片起始位置 // srcW, srcH 源图片大小 // alpha 图片透明度 void draw_image(IMAGE& image, int x, int y, int w, int h, int srcX, int srcY, int srcW, int srcH, BYTE alpha = 255); inline void draw_image(IMAGE& image, int x, int y, int w, int h, BYTE alpha = 255) { return draw_image(image, x, y, w, h, 0, 0, image.getwidth(), image.getheight(), alpha); } inline void draw_image(IMAGE& image, int x, int y, BYTE alpha = 255) { draw_image(image, x, y, image.getwidth(), image.getheight(), alpha); } // 绘制旋转的图片 // image 绘制的图片 // ox, oy 源图片旋转中心点 // x, y 目标绘制的位置 // w, h 目标绘制的大小。如果是负值,则会对图片进行镜像反转 // srcX, srcY 源图片起始位置 // srcW, srcH 源图片大小 // alpha 图片透明度 void rotate_draw(IMAGE& image, int ox, int oy, int x, int y, int w, int h, int srcX, int srcY, int srcW, int srcH, float angle); inline void rotate_draw(IMAGE& image, int ox, int oy, int x, int y, int w, int h, float angle) { return rotate_draw(image, ox, oy, x, y, w, h, 0, 0, image.getwidth(), image.getheight(), angle); } // 以图片中心为旋转中心点绘制 inline void point_image(IMAGE& image, int x, int y, int w, int h, float angle) { return rotate_draw(image, image.getwidth() / 2, image.getheight() / 2, x, y, w, h, angle); } inline void point_image(IMAGE& image, int x, int y, float angle) { point_image(image, x, y, image.getwidth(), image.getheight(), angle); } //----------------------------------------------------------------------------- // // 音乐播放 // //----------------------------------------------------------------------------- // 播放音乐 // filename 播放的音乐文件 // repeat 是否重复 void play_music(PCTSTR filename, bool repeat = true); // 暂停播放音乐 void pause_music(); // 继续播放音乐 void resume_music(); // 停止播放音乐 void stop_music(); // 判断音乐是否播放中 bool music_is_playing(); // 判断音乐是否暂停 bool music_is_pause(); // 判断音乐是否停止 bool music_is_stop(); // 获取音乐当前播放位置 int music_position(); // 获取音乐总长度 int music_length(); // 播放声音 // filename 播放的音频文件 // loop 是否循环播放 int play_sound(PCTSTR filename, bool loop = false); // 停止声音播放 void stop_sound();
源文件(EasyxEx.cpp):
#include "EasyxEx.h" // 保存窗口大小的变量 int WINX = 1024; int WINY = 720; // 标记程序是否正在运行,设置 false 以结束程序主循环 bool running = true; // 记录鼠标坐标 POINT mouse; // 记录鼠标按钮信息 int mouse_button = 0; // 是否开启调试模式 bool debug = false; // 获取虚拟按键状态 bool keystate(int key) { return GetAsyncKeyState(key) & 0x8000; } //----------------------------------------------------------------------------- // // 界面按钮 // //----------------------------------------------------------------------------- // 简单的按钮界面系统 // 按钮结构体 struct BUTTON { GAME_STATE state; //游戏场景 int command; //按钮命令 TCHAR text[64]; //按钮文字 RECT bounds; //按钮位置 bool visible; //是否可见 }; const int MAX_BUTTONS = 1024; BUTTON buttonData[MAX_BUTTONS] = { GAME_NULL }; // 添加一个按钮 // 维护的按钮 id 范围是 1 ~ MAX_BUTTONS,0 是无效 id int add_button(GAME_STATE state, int command, PCTSTR text, int x, int y, int width, int height) { if (state <= GAME_NULL && state >= GAME_LAST) { return 0; } BUTTON* button; for (int i = 0; i < MAX_BUTTONS; ++i) { button = buttonData + i; if (button->state == GAME_NULL) { button->state = state; button->command = command; _tcsncpy(button->text, text, 64); button->bounds.left = x; button->bounds.top = y; button->bounds.right = x + width; button->bounds.bottom = y + height; button->visible = true; return i + 1; } } return 0; } //获取按钮数量 int button_count(GAME_STATE state) { int n = 0; for (int i = 0; i < MAX_BUTTONS; ++i) { if (buttonData[i].state == state) { ++n; } } return n; } // 获取游戏场景按钮ID int button_index(GAME_STATE state, int index) { int n = 0; for (int i = 0; i < MAX_BUTTONS; ++i) { if (buttonData[i].state == state) { if (n == index) { return i + 1; } ++n; } } return 0; } // 获取按钮信息 BUTTON* button_info(int id) { if (id > 0 && id <= MAX_BUTTONS) { return &buttonData[id - 1]; } else { return NULL; } } // 获取按钮命令 int button_command(int id) { if (id > 0 && id <= MAX_BUTTONS) { return buttonData[id - 1].command; } else { return 0; } } // 获取按钮标题 PCTSTR button_text(int id) { if (id > 0 && id <= MAX_BUTTONS) { return buttonData[id - 1].text; } else { return TEXT(""); } } // 获取按钮显示范围 RECT button_bounds(int id) { if (id > 0 && id <= MAX_BUTTONS) { return buttonData[id - 1].bounds; } else { return RECT(); } } // 显示(隐藏)按钮 void show_button(int id, bool value) { if (id > 0 && id <= MAX_BUTTONS) { buttonData[id - 1].visible = value; } } // 按钮是否可见 bool button_visible(int id) { if (id > 0 && id <= MAX_BUTTONS) { return buttonData[id - 1].visible; } else { return false; } } // 判断鼠标点击了哪个按钮 int hit_button(GAME_STATE state) { for (int i = 0; i < MAX_BUTTONS; ++i) { //查找场景按钮,并检查按钮是否可见 if (buttonData[i].state == state && buttonData[i].visible == true) { //检测鼠标点击了哪个按钮 if (PtInRect(&buttonData[i].bounds, mouse)) { return i + 1; } } } return 0; } //----------------------------------------------------------------------------- // // 数学函数 // //----------------------------------------------------------------------------- // 获得 n 以内的随机数,值的范围(0 ~ n - 1) int random(int n) { return rand() % n; } // 获得 a 到 b - 1 之间的随机数 int random(int a, int b) { return a + random(b - a); } // 获取弧度 float radian(float x, float y) { double n = double(); if (y != 0) { n = M_PI_2 - atan(x / y);//根据斜率求弧度,反向 if (y < 0.0f)n += M_PI; } return float(n); } // 获取角度 float angle(float x, float y) { return float(radian(x, y) * M_INV_RD); } // 获取两点的距离 float distance(float x1, float y1, float x2, float y2) { float w = x2 - x1; float h = y2 - y1; return (float)sqrt(w * w + h * h); } //----------------------------------------------------------------------------- // // 二维向量 // //----------------------------------------------------------------------------- // 相加 vec2 add(const vec2& v1, const vec2& v2) { return vec2(v1.x + v2.x, v1.y + v2.y); } // 相减 vec2 sub(const vec2& v1, const vec2& v2) { return vec2(v1.x - v2.x, v1.y - v2.y); } // 相乘 vec2 mul(const vec2& v1, const vec2& v2) { return vec2(v1.x * v2.x, v1.y * v2.y); } // 旋转 vec2 rotate(const vec2& v, float angle) { angle *= (float)M_RD; float sine = (float)sin(angle); float cosine = (float)cos(angle); return vec2(v.x * cosine - v.y * sine, v.y * cosine + v.x * sine); } // 距离原点的距离 float length(const vec2& v) { return (float)sqrt(v.x * v.x + v.y * v.y); } // 获取两个向量的距离 float distance(const vec2& v1, const vec2& v2) { return distance(v1.x, v1.y, v2.x, v2.y); } // 归一化 vec2 normalize(const vec2& v) { float n = length(v); if (n == 0) { return v; } n = 1.0f / n; return vec2(v.x * n, v.y * n); } // 获取角度 float angle(const vec2& v) { return angle(v.x, v.y); } //----------------------------------------------------------------------------- // // GDI矩阵操作 // //----------------------------------------------------------------------------- // 初始化单位矩阵 void identity(mat2& m) { m.eM11 = 1.0f; m.eM12 = 0.0f; m.eM21 = 0.0f; m.eM22 = 1.0f; m.eDx = 0.0f; m.eDy = 0.0f; } // 移动矩阵 void translate(mat2& m, float x, float y) { mat2 mat; mat.eM11 = 1.0f; mat.eM12 = 0.0f; mat.eM21 = 0.0f; mat.eM22 = 1.0f; mat.eDx = x; mat.eDy = y; CombineTransform(&m, &m, &mat); } // 旋转矩阵 void rotate(mat2& m, double angle) { mat2 mat; angle *= M_RD; double cosin = cos(angle); double sine = sin(angle); mat.eM11 = (FLOAT)cosin; mat.eM12 = (FLOAT)sine; mat.eM21 = (FLOAT)-sine; mat.eM22 = (FLOAT)cosin; mat.eDx = 0.0f; mat.eDy = 0.0f; CombineTransform(&m, &m, &mat); } // 缩放矩阵 void scale(mat2& m, float x, float y) { XFORM mat; mat.eM11 = x; mat.eM12 = 0.0f; mat.eM21 = 0.0f; mat.eM22 = y; mat.eDx = 0.0f; mat.eDy = 0.0f; CombineTransform(&m, &m, &mat); } // 读取 HDC 矩阵 mat2 getmat(HDC dc) { mat2 m; GetWorldTransform(dc, &m); return m; } // 设置 HDC 的矩阵 void setmat(HDC dc, mat2& m) { SetWorldTransform(dc, &m); } //----------------------------------------------------------------------------- // // easyx扩展绘图函数,绘制带alpha通道的png图片 // //----------------------------------------------------------------------------- // 绘制带 alpha 通道的图片 void draw_image(IMAGE& image, int x, int y, int w, int h, int srcX, int srcY, int srcW, int srcH, BYTE alpha) { BLENDFUNCTION blendfunc = { AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA }; //获取当前绘图dc HDC dc = GetImageHDC(); //判断是否需要反转 if (w < 0 || h < 0) { //计算缩放反转 float scale_x = 1.0f; float scale_y = 1.0f; if (w < 0) { scale_x = -1.0f; w = -w; //x += w; } if (h < 0) { scale_y = -1.0f; h = -h; //y += h; } //备份当前矩阵 XFORM backup = getmat(dc); //矩阵操作 XFORM m; identity(m); //重置矩阵 scale(m, scale_x, scale_y); //缩放 translate(m, (float)x, (float)y); //移动 setmat(dc, m); //应用矩阵 //绘制图片 AlphaBlend(dc, 0, 0, w, h, GetImageHDC(&image), srcX, srcY, srcW, srcH, blendfunc); //恢复dc变换矩阵 setmat(dc, backup); } else { //正常绘制图片 AlphaBlend(dc, x, y, w, h, GetImageHDC(&image), srcX, srcY, srcW, srcH, blendfunc); } } // 绘制旋转的图片 void rotate_draw(IMAGE& image, int ox, int oy, int x, int y, int w, int h, int srcX, int srcY, int srcW, int srcH, float angle) { //获取当前绘图dc HDC dc = GetImageHDC(); //备份当前矩阵 XFORM backup = getmat(dc); //计算缩放反转 float scale_x = 1.0f; float scale_y = 1.0f; if (w < 0) { w = -w; scale_x = -1.0f; } if (h < 0) { h = -h; scale_y = -1.0f; } //矩阵操作 XFORM m; identity(m); //重置矩阵 scale(m, scale_x, scale_y); //缩放(反转图片) rotate(m, angle); //旋转 translate(m, (float)x, (float)y); //平移 setmat(dc, m); //应用矩阵 //绘制图片 BLENDFUNCTION blendfunc = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; AlphaBlend(dc, -ox, -oy, w, h, GetImageHDC(&image), srcX, srcY, srcW, srcH, blendfunc); //恢复dc变换矩阵 setmat(dc, backup); } //----------------------------------------------------------------------------- // // 音频播放 // //----------------------------------------------------------------------------- // 播放音乐 void play_music(PCTSTR filename, bool repeat) { TCHAR command[256] = { 0 }; _tcscpy(command, TEXT("open ")); _tcscat(command, filename); _tcscat(command, TEXT(" alias bgm")); //关闭之前的音乐 mciSendString(TEXT("close bgm"), NULL, 0, NULL); //打开新音乐 mciSendString(command, NULL, 0, NULL); //循环播放 if (repeat) { mciSendString(TEXT("play bgm repeat"), NULL, 0, NULL); } else { mciSendString(TEXT("play bgm"), NULL, 0, NULL); } } // 暂停播放音乐 void pause_music() { mciSendString(TEXT("pause bgm"), NULL, 0, NULL); } // 继续播放音乐 void resume_music() { mciSendString(TEXT("resume bgm"), NULL, 0, NULL); } // 停止播放音乐 void stop_music() { mciSendString(TEXT("stop bgm"), NULL, 0, NULL); } // 判断音乐是否播放中 bool music_is_playing() { TCHAR buf[32] = { 0 }; mciSendString(TEXT("status bgm mode"), buf, 32, 0); return _tcsncmp(buf, TEXT("playing"), 32) == 0; } // 判断音乐是否暂停 bool music_is_pause() { TCHAR buf[32] = { 0 }; mciSendString(TEXT("status bgm mode"), buf, 32, 0); return _tcsncmp(buf, TEXT("paused"), 32) == 0; } // 判断音乐是否停止 bool music_is_stop() { TCHAR buf[32] = { 0 }; mciSendString(TEXT("status bgm mode"), buf, 32, 0); return _tcsncmp(buf, TEXT("stopped"), 32) == 0; } // 获取音乐当前播放位置 int music_position() { TCHAR buf[32] = { 0 }; mciSendString(TEXT("status bgm position"), buf, 32, 0); return _ttoi(buf); } // 获取音乐总长度 int music_length() { TCHAR buf[32] = { 0 }; mciSendString(TEXT("status bgm length"), buf, 32, 0); return _ttoi(buf); } // 播放声音 int play_sound(PCTSTR filename, bool loop) { DWORD fdwSound = SND_FILENAME | SND_ASYNC; if (loop)fdwSound |= SND_LOOP; return PlaySound(filename, 0, fdwSound); } // 停止声音播放 void stop_sound() { PlaySound(NULL, NULL, SND_FILENAME); }
sdragonx https://github.com/sdragonx