easyx对于初学者来说,可能一些同学觉得并不easyx,其实要用好这个库,需要有一定的win32编程基础,GDI图像处理知识。easyx原生没有对透明通道图片的支持,比如透明格式的png图片。这个扩展库增加了对透明png图片绘制的支持,简单的界面按钮管理和音乐声音播放函数,专门为制作小游戏扩展,使用非常方便。
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(默认)
//标记程序是否正在运行
extern bool running;
//记录鼠标坐标
extern POINT mouse;
//记录鼠标按钮信息
extern int mouse_button;
//是否调试模式
extern bool debug;
//
// _getch() 常用键盘码
//
enum KEYCODE
{
KEY_ENTER = 13,
KEY_ESC = 27,
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,
};
//获取虚拟按键状态
//虚拟按键和_kbhit()冲突,easyx没有提供按键消息的接口。
bool keystate(int key);
//
// 游戏场景状态
//
enum GAME_STATE
{
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,
};
//-----------------------------------------------------------------------------
//
// 界面按钮
//
//-----------------------------------------------------------------------------
//按钮
struct BUTTON
{
int command; //按钮命令
TCHAR text[64]; //按钮文字
RECT bounds; //按钮位置
};
//添加一个按钮
void add_button(GAME_STATE state, int command, PCTSTR text, int x, int y, int width, int height);
//获取按钮数量
int button_count(GAME_STATE state);
//获取按钮信息
BUTTON* get_button(GAME_STATE state, int id);
//设置按钮信息
void set_button_command(GAME_STATE state, int id, int command);
//设置按钮标题
void set_button_text(GAME_STATE state, int id, PCTSTR text);
//判断鼠标点击了哪个按钮,如果没有点击,返回-1
int hit_button(GAME_STATE state);
//-----------------------------------------------------------------------------
//
// 数学函数
//
//-----------------------------------------------------------------------------
//获得n以内的随机数
int random(int n);
//获得iMin到iMax之间的随机数
int random(int iMin, int iMax);
//获得弧度
float radian(float x, float y);
//获得角度
float angle(float x, float y);
//-----------------------------------------------------------------------------
//
// 二维向量
//
//-----------------------------------------------------------------------------
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);
//归一化
vec2 normalize(const vec2& v);
//获取角度
float angle(const vec2& v);
//-----------------------------------------------------------------------------
//
// GDI矩阵操作
//
//-----------------------------------------------------------------------------
typedef XFORM mat2x3;
//设置单位矩阵
void identity(XFORM& m);
//移动矩阵
void translate(XFORM& m, float x, float y);
//旋转矩阵
void rotate(XFORM& m, double angle);
//缩放矩阵
void scale(XFORM& m, float x, float y);
//读取HDC矩阵
XFORM getmat(HDC dc);
//设置HDC的矩阵
void setmat(HDC dc, XFORM& 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 = 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);
}
//绘制旋转的图片
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);
}
//-----------------------------------------------------------------------------
//
// 音乐播放
//
//-----------------------------------------------------------------------------
//播放音乐。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();
//播放声音
int play_sound(PCTSTR filename, bool loop = false);
//停止声音播放
void stop_sound();
源文件(EasyxEx.cpp):
#include "EasyxEx.h"
//窗口大小
int WINX = 1024;
int WINY = 720;
//标记程序是否正在运行
bool running = true;
//记录鼠标坐标
POINT mouse;
//记录鼠标按钮信息
int mouse_button = 0;
//是否调试模式
bool debug = false;
//获取虚拟按键状态
bool keystate(int key)
{
return GetAsyncKeyState(key) & 0x8000;
}
//-----------------------------------------------------------------------------
//
// 数学函数
//
//-----------------------------------------------------------------------------
//获得n以内的随机数
int random(int n)
{
return rand() % n;
}
//获得iMin到iMax之间的随机数
int random(int iMin, int iMax)
{
return iMin + random(iMax - iMin);
}
//获得弧度
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);
}
//-----------------------------------------------------------------------------
//
// 二维向量
//
//-----------------------------------------------------------------------------
//相加
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);
}
//归一化
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(XFORM& 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(XFORM& m, float x, float y)
{
XFORM 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(XFORM& m, double angle)
{
XFORM 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(XFORM& 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矩阵
XFORM getmat(HDC dc)
{
XFORM m;
GetWorldTransform(dc, &m);
return m;
}
//设置HDC的矩阵
void setmat(HDC dc, XFORM& 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);
}
//-----------------------------------------------------------------------------
//
// 界面按钮
//
//-----------------------------------------------------------------------------
//简单的按钮界面系统
//每个游戏界面状态缓存16个按钮,基本够用。
const int MAX_BUTTONS = 16;
BUTTON buttonsArray[GAME_LAST][MAX_BUTTONS] = { 0 };
static bool init_buttons = false;
//添加一个按钮
void add_button(GAME_STATE state, int command, PCTSTR text, int x, int y, int width, int height)
{
//是否初始化按钮
if (!init_buttons) {
for (int i = 0; i < GAME_LAST; ++i) {
for (int j = 0; j < MAX_BUTTONS; ++j) {
//INT_MAX标识按钮没有被使用
buttonsArray[i][j].command = INT_MAX;
}
}
init_buttons = true;
}
BUTTON* buttons = buttonsArray[state];
for (int i = 0; i < MAX_BUTTONS; ++i) {
if (buttons[i].command == INT_MAX) {
buttons[i].command = command;
_tcsncpy(buttons[i].text, text, 64);
buttons[i].bounds.left = x;
buttons[i].bounds.top = y;
buttons[i].bounds.right = x + width;
buttons[i].bounds.bottom = y + height;
break;
}
}
}
//获取按钮数量
int button_count(GAME_STATE state)
{
int n = 0;
BUTTON* buttons = buttonsArray[state];
for (int i = 0; i < MAX_BUTTONS; ++i) {
if (buttons[i].command != INT_MAX) {
++n;
}
}
return n;
}
//获取按钮信息
BUTTON* get_button(GAME_STATE state, int id)
{
return &buttonsArray[state][id];
}
//设置按钮信息
void set_button_command(GAME_STATE state, int id, int command)
{
if (id < MAX_BUTTONS) {
buttonsArray[state][id].command = command;
}
}
//设置按钮标题
void set_button_text(GAME_STATE state, int id, PCTSTR text)
{
if (id < MAX_BUTTONS) {
_tcsncpy(buttonsArray[state][id].text, text, 64);
}
}
//判断鼠标点击了哪个按钮,如果没有点击,返回-1
int hit_button(GAME_STATE state)
{
for (int i = 0; i < button_count(state); ++i) {
//检测鼠标点击了哪个按钮
if (PtInRect(&buttonsArray[state][i].bounds, mouse)) {
return i;
}
}
return -1;
}
//-----------------------------------------------------------------------------
//
// 音频播放
//
//-----------------------------------------------------------------------------
//播放音乐
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);
}