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);
}