EasyX插件下载地址:www.easyx.cn,点击右上角下载
实例来自:https://www.easyx.cn/samples/
1.贪吃蛇
///
// 程序名称:贪吃蛇
// 编译环境:Visual C++ 6.0 / 2008,EasyX 2013冬至版
// 最后修改:2014-5-20
// 操作方式:以蛇为第一视角,左 左转;右 右转;下 暂停;上 快进。
// 状态: 黄色 蛇头;红色 蛇身;绿色 食物。
#include <graphics.h>
#include <string.h>
#include <time.h>
#define NUM_R 10 //半径
#define NUM_X 25 //横向个数
#define NUM_Y 25 //纵向个数
#define NUM 30 //所需节点个数
void exe(int x,int y,int f);
int GetCommand();
void eat(int x,int y);
void clear();
void set();
void flush();
void over(bool a);
struct pos //建立链表储存每个关节的位置
{
int x;
int y;
struct pos*next;
};
struct pos*head=(pos*)malloc(sizeof(pos)); //建立头指针
int n=0; //记录节点个数
void main() //初始化游戏
{
int x,y,f; //储存初始化点的位置方向
srand((unsigned) time(NULL)); //初始化随机库
do
{
x=rand()%NUM_X*NUM_R*2+NUM_R;
y=rand()%NUM_Y*NUM_R*2+NUM_R;
} while(x<4*NUM_R || y<4*NUM_R || 2*NUM_R*(NUM_X-2)<x || 2*NUM_R*(NUM_Y-2)<y); //产生不在矩形边缘的初始点
f=rand()%4; //随机方向
struct pos*a=(pos*)malloc(sizeof(pos)),*p=head; //建立链表第一个节点
a->x=x; //指针a储存第一个点数据
a->y=y;
head->next=a; //接链
a->next=NULL; //结尾
initgraph(2*NUM_R*NUM_X,2*NUM_R*NUM_Y+50); //初始绘图窗口
setcolor(WHITE);
line(0,2*NUM_R*NUM_Y+1,2*NUM_R*NUM_X,2*NUM_R*NUM_Y+1);
setcolor(getbkcolor()); //取消圆的边缘
setfillcolor(YELLOW); //设置填充颜色
fillcircle(x,y,NUM_R); //绘出初始点
set(); //产生食物
exe(x,y,f); //进入控制函数
}
void exe(int x,int y,int f) //操作游戏
{
int xf,yf,c,i;
while(1) //进入循环
{
c=0; //初始化方向
for(i=0;i<5;i++) //循环5次获取命令
{
Sleep(100-50*n/NUM); //等待
if(c==0) //若没获取到命令就进行获取
{
c=GetCommand();
if(c==4) //返回4时退出循环等待
break;
}
}
f=f+c; //改变方向
if(f>3) //溢出处理
f=f-4;
xf=yf=0; //初始化方向参数
switch(f)
{
case 0:xf=1;break; //方向向右时 x坐标增加
case 1:yf=1;break; //方向向右时 y坐标增加
case 2:xf=-1;break; //方向向右时 x坐标减少
case 3:yf=-1;break; //方向向右时 y坐标减少
}
x=x+2*NUM_R*xf; //x坐标变化
y=y+2*NUM_R*yf; //y坐标变化
if(getpixel(x,y)==RED || x<0 || y<0 || 2*NUM_X*NUM_R<x || 2*NUM_Y*NUM_R<y) //判断是否遇到自身或碰到边界
over(0); //结束游戏
else //不结束进行下步运算
{
if(getpixel(x,y)==GREEN) //判断前方是否为食物
set(); //产生新食物
else
clear(); //清除尾结点
eat(x,y); //在前方生成新结点
if(n>NUM-1) //判断胜利条件
over(1); //结束游戏
}
}
}
int GetCommand() //获取方向
{
int c=0; //初始化方向变量
if(GetAsyncKeyState(VK_RIGHT) & 0x8000) c = 1; //右转为1
if(GetAsyncKeyState(VK_LEFT) & 0x8000) c = 3; //左转为3
if(GetAsyncKeyState(VK_UP) & 0x8000) c = 4; //按上为4 快进
if(GetAsyncKeyState(VK_DOWN) & 0x8000) system("pause"); //按下则暂停
return c;
}
void eat(int x,int y) //增加新结点
{
struct pos*a=(pos*)malloc(sizeof(pos)),*p=head; //声明指针变量
while(p->next!=NULL) //寻找链表尾节点
p=p->next;
a->x=x; //把数据储存到结点
a->y=y;
p->next=a; //指针a接到尾节点后
a->next=NULL; //结尾
setcolor(getbkcolor()); //取消圆的边缘
setfillcolor(RED); //设置填充颜色
fillcircle(p->x,p->y,NUM_R); //绘制新结点
setfillcolor(YELLOW); //设置填充颜色
fillcircle(x,y,NUM_R); //绘制新结点
}
void clear() //清除尾结点
{
setcolor(getbkcolor()); //取消圆的边缘
setfillcolor(getbkcolor()); //设置填充颜色
fillcircle(head->next->x,head->next->y,NUM_R); //擦除结点
head->next=head->next->next; //删除节点数据
}
void set() //产生食物和胜利判断
{
flush();
int x,y; //声明变量
do
{
x=rand()%NUM_X*NUM_R*2+NUM_R;
y=rand()%NUM_Y*NUM_R*2+NUM_R;
} while (getpixel(x,y)==RED); //随机产生食物在非蛇的位置
setcolor(getbkcolor());
setfillcolor(GREEN); //设置填充颜色
fillcircle(x,y,NUM_R); //产生食物
}
void flush()
{
n++; //节点计数累加
char strnum[20],string[10]="进度:";
itoa(n,strnum,10); //转换
strcat(string,strnum); //链接
strcpy(strnum,"/"); //赋值
strcat(string,strnum); //连接
itoa(NUM,strnum,10);
strcat(string,strnum);
setcolor(WHITE);
settextstyle(32,0,_T("宋体")); //设置字体类型
outtextxy(20,2*NUM_R*NUM_Y+2," ");
outtextxy(20,2*NUM_R*NUM_Y+2,string);
}
void over(bool a) //结束游戏
{
setcolor(WHITE); //设置字体颜色
settextstyle(48,0,_T("宋体")); //设置字体类型
if(a) //判断条件
outtextxy(NUM_X*NUM_R-20,NUM_Y*NUM_R-20,"胜利"); //输出结果
else
outtextxy(NUM_X*NUM_R-20,NUM_Y*NUM_R-20,"失败"); //输出结果
Sleep(2000);
system("pause");
exit(0);
}
2.矿井逃生
///
// 程序名称:矿井逃生
// 编译环境:Visual C++ 6.0 / 2010,EasyX 2013霜降版
// 最后修改:2013-11-15
//
#include <easyx.h>
#include <time.h>
#include <math.h>
#include <stdio.h>
// 定义常量
#define PI 3.141592653589 // 圆周率
#define UNIT_GROUND 0 // 表示地面
#define UNIT_WALL 1 // 表示墙
#define LIGHT_A PI / 3 // 灯光的角度范围
#define LIGHT_R 120 // 灯光的照射距离
#define WIDTH 480 // 矿井的宽度
#define HEIGHT 480 // 矿井的高度
#define SCREENWIDTH 640 // 屏幕宽度
#define SCREENHEIGHT 480 // 屏幕高度
#define UNIT 20 // 每个墙壁单位的大小
#define PLAYER_R 5 // 游戏者的半径
// 定义常量
const SIZE g_utMap = {23, 23}; // 矿井地图的尺寸(基于 UNIT 单位)
const POINT g_utPlayer = {1, 1}; // 游戏者的位置(基于 UNIT 单位)
const POINT g_utExit = {21, 22}; // 出口位置(基于 UNIT 单位)
const POINT g_ptOffset = {10, 10}; // 矿井显示在屏幕上的偏移量
//
// 定义全局变量
//
POINT g_ptPlayer; // 游戏者的位置
POINT g_ptMouse; // 鼠标位置
IMAGE g_imgMap(WIDTH, HEIGHT); // 矿井平面图
DWORD* g_bufMap; // 矿井平面图的显存指针
IMAGE g_imgRender(WIDTH, HEIGHT); // 渲染
DWORD* g_bufRender; // 渲染的显存指针
DWORD* g_bufScreen; // 屏幕的显存指针
// 枚举用户的控制命令
enum CMD { CMD_QUIT = 1, CMD_UP = 2, CMD_DOWN = 4, CMD_LEFT = 8, CMD_RIGHT = 16, CMD_RESTART = 32 };
//
// 函数声明
//
// 初始化
void Welcome(); // 绘制游戏界面
void ReadyGo(); // 准备开始游戏
void InitGame(); // 初始化游戏数据
// 矿井生成
void MakeMaze(int width, int height); // 初始化(注:宽高必须是奇数)
void TravelMaze(int x, int y, BYTE** aryMap); // 遍历 (x, y) 四周
void DrawWall(int x, int y, bool left, bool top, bool right, bool bottom);
// 画一面墙
// 绘制
void Paint(); // 绘制视野范围内的矿井
void Lighting(int _x, int _y, double _a); // 在指定位置和角度“照明”
void DrawPlayer(); // 绘制游戏者
void DrawExit(); // 绘制出口
// 处理用户控制
int GetCmd(); // 获取用户输入的命令
void OnUp(); // 向上移动
void OnLeft(); // 向左移动
void OnRight(); // 向右移动
void OnDown(); // 向下移动
bool CheckWin(); // 检查是否到出口
//
// 函数定义
//
// 主程序
void main()
{
// 初始化
initgraph(SCREENWIDTH, SCREENHEIGHT); // 创建绘图窗口
srand((unsigned)time(NULL)); // 设置随机种子
// 显示主界面
Welcome();
// 游戏过程
int c;
do
{
ReadyGo();
while(true)
{
// 获得用户输入
c = GetCmd();
// 处理用户输入
if (c & CMD_UP) OnUp();
if (c & CMD_DOWN) OnDown();
if (c & CMD_LEFT) OnLeft();
if (c & CMD_RIGHT) OnRight();
if (c & CMD_RESTART)
{
if (MessageBox(GetHWnd(), _T("您要重来一局吗?"), _T("询问"), MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
break;
}
if (c & CMD_QUIT)
{
if (MessageBox(GetHWnd(), _T("您确定要退出游戏吗?"), _T("询问"), MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
break;
}
// 绘制场景
Paint();
// 判断是否走出矿井
if (CheckWin())
{
// 是否再来一局
HWND hwnd = GetHWnd();
if (MessageBox(hwnd, _T("恭喜你走出来了!\n您想再来一局吗?"), _T("恭喜"), MB_YESNO | MB_ICONQUESTION) != IDYES)
c = CMD_QUIT;
break;
}
// 延时
Sleep(16);
}
}
while(!(c & CMD_QUIT));
// 关闭图形模式
closegraph();
}
// 准备开始游戏
void ReadyGo()
{
// 初始化
InitGame();
// 停电前兆
int time[7] = {1000, 50, 500, 50, 50, 50, 50};
int i, x, y;
for (i = 0; i < 7; i++)
{
if (i % 2 == 0)
{
putimage(0, 0, &g_imgMap);
DrawPlayer();
DrawExit();
}
else
clearrectangle(0, 0, WIDTH - 1, HEIGHT - 1);
Sleep(time[i]);
}
// 电力缓慢中断
for (i = 255; i >= 0; i -= 5)
{
for (y = (HEIGHT - 1) * SCREENWIDTH; y >= 0; y -= SCREENWIDTH)
for (x = 0; x < WIDTH; x++)
if (g_bufScreen[y + x] != 0)
g_bufScreen[y + x] = g_bufScreen[y + x] - 0x050505;
FlushBatchDraw();
DrawPlayer();
DrawExit();
Sleep(50);
}
// 绘制游戏区
Paint();
}
// 绘制游戏界面
void Welcome()
{
setfillcolor(DARKGRAY);
solidrectangle(WIDTH, 0, SCREENWIDTH - 1, SCREENHEIGHT - 1);
// 设置字体样式
settextcolor(WHITE);
setbkmode(TRANSPARENT);
// 绘制标题
settextstyle(24, 0, _T("宋体"));
outtextxy(512, 40, _T("矿井逃生"));
// 绘制操作说明
RECT r = {488, 100, 632, 470};
settextstyle(12, 0, _T("宋体"));
drawtext(_T("[游戏说明]\n 矿井里的电路又出问题了。迅速借助你的头灯,在漆黑的矿井里\
找到出口逃出去吧。\n\n[控制说明]\n方向键: 移动\nA/S/D/W:移动\n鼠标: 控制照射\
方向\nF2: 重来一局\nESC: 退出游戏"), &r, DT_WORDBREAK);
outtextxy(495, 465, _T("Powered by yw80@qq.com"));
}
// 初始化游戏数据
void InitGame()
{
// 获得窗口显存指针
g_bufRender = GetImageBuffer(&g_imgRender);
g_bufMap = GetImageBuffer(&g_imgMap);
g_bufScreen = GetImageBuffer(NULL);
// 设置 Render 环境
SetWorkingImage(&g_imgRender);
setbkmode(TRANSPARENT);
SetWorkingImage(NULL);
// 创建矿井
MakeMaze(g_utMap.cx, g_utMap.cy);
// 设置游戏者位置
g_ptPlayer.x = g_utPlayer.x * UNIT + UNIT / 2 + g_ptOffset.x;
g_ptPlayer.y = g_utPlayer.y * UNIT + UNIT / 2 + g_ptOffset.y;
}
// 生成矿井:初始化(注:宽高必须是奇数)
void MakeMaze(int width, int height)
{
if (width % 2 != 1 || height % 2 != 1)
return;
int x, y;
// 定义矿井二维数组,并初始化全部为墙壁
// 宽高比实际多 2,是因为两端各有一个“哨兵”,用于方便处理数据
BYTE** aryMap = new BYTE*[width + 2];
for(x = 0; x < width + 2; x++)
{
aryMap[x] = new BYTE[height + 2];
memset(aryMap[x], UNIT_WALL, height + 2);
}
// 定义边界(哨兵功能)
for (x = 0; x <= width + 1; x++)
aryMap[x][0] = aryMap[x][height + 1] = UNIT_GROUND;
for (y = 1; y <= height; y++)
aryMap[0][y] = aryMap[width + 1][y] = UNIT_GROUND;
// 从任意点开始遍历生成矿井
TravelMaze(((rand() % (width - 1)) & 0xfffe) + 2, ((rand() % (height - 1)) & 0xfffe) + 2, aryMap);
// 设置出口
aryMap[g_utExit.x + 1][g_utExit.y + 1] = UNIT_GROUND;
// 将矿井绘制在 IMAGE 对象上
SetWorkingImage(&g_imgMap);
cleardevice();
for (y = 1; y <= height; y++)
for (x = 1; x <= width; x++)
if (aryMap[x][y] == UNIT_WALL)
DrawWall(x, y, aryMap[x - 1][y] == UNIT_WALL,
aryMap[x][y - 1] == UNIT_WALL,
aryMap[x + 1][y] == UNIT_WALL,
aryMap[x][y + 1] == UNIT_WALL);
SetWorkingImage(NULL);
}
// 生成矿井:遍历 (x, y) 四周
void TravelMaze(int x, int y, BYTE** aryMap)
{
// 定义遍历方向
int d[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
// 将遍历方向乱序
int n, t, i;
for(i = 0; i < 4; i++)
{
n = rand() % 4;
t = d[i][0], d[i][0] = d[n][0], d[n][0] = t;
t = d[i][1], d[i][1] = d[n][1], d[n][1] = t;
}
// 尝试周围四个方向
aryMap[x][y] = UNIT_GROUND;
for(i = 0; i < 4; i++)
if (aryMap[x + 2 * d[i][0]][y + 2 * d[i][1]] == UNIT_WALL)
{
aryMap[x + d[i][0]][y + d[i][1]] = UNIT_GROUND;
TravelMaze(x + d[i][0] * 2, y + d[i][1] * 2, aryMap); // 递归
}
}
// 生成矿井:画一面墙
// 参数:left/top/right/bottom 表示墙壁是否与旁边连接
void DrawWall(int x, int y, bool left, bool top, bool right, bool bottom)
{
// 墙壁厚 4 pixel
int cx, cy;
cx = x * UNIT - UNIT / 2 - 2 + 10;
cy = y * UNIT - UNIT / 2 - 2 + 10;
if (left) solidrectangle(x * UNIT - UNIT + 10, cy, cx + 4, cy + 4);
if (top) solidrectangle(cx, y * UNIT - UNIT + 10, cx + 4, cy + 4);
if (right) solidrectangle(cx, cy, x * UNIT + 9, cy + 4);
if (bottom) solidrectangle(cx, cy, cx + 4, y * UNIT + 9);
}
// 绘制视野范围内的矿井
void Paint()
{
// 设置绘图目标为 Render 对象
SetWorkingImage(&g_imgRender);
// 清空 Render 对象
cleardevice();
// 计算视野角度
double dx, dy, a;
dx = g_ptMouse.x - g_ptPlayer.x;
dy = g_ptMouse.y - g_ptPlayer.y;
if (dx == 0 && dy == 0)
a = 0;
else if (dx != 0 && dy != 0)
a = atan(dy / dx);
else if (dx == 0)
a = (dy > 0) ? PI / 2 : PI * 3 / 2;
else
a = 0;
if (dx < 0) a += PI;
if (a < 0) a += PI * 2;
// 绘制灯光
Lighting(g_ptPlayer.x, g_ptPlayer.y, a);
// 画游戏者
DrawPlayer();
// 画出口
DrawExit();
// 设置绘图目标为窗口
SetWorkingImage(NULL);
// 显示到窗口上
putimage(0, 0, &g_imgRender);
}
// 在指定位置和角度“照明”
void Lighting(int _x, int _y, double _a)
{
int i; // 定义循环变量
int x, y; // 定义临时坐标
double a; // 定义临时角度
// 计算灯光照亮的角度区域
double a1 = _a - LIGHT_A / 2;
double a2 = _a + LIGHT_A / 2;
for(a = a1; a < a2; a += PI / 360) // 扇形循环
{
for(int r = 0; r < LIGHT_R; r++) // 半径循环
{
// 计算照射到的位置
x = (int)(_x + cos(a) * r);
y = (int)(_y + sin(a) * r);
// 光线超出屏幕范围,终止
// (为了简化全凭模糊运算,不处理最上和最下一行)
if (x < 0 || x >= WIDTH || y <= 0 || y >= HEIGHT - 1)
break;
// 光线碰到建筑物,终止
if(g_bufMap[y * WIDTH + x])
break;
// 光线叠加
g_bufRender[y * WIDTH + x] += 0x202000; // 0x202000 是很淡的黄色
}
}
// 计算光照扇形区域的最小包围矩形
// 方法:获得 7 个点的最值:圆心、圆弧两端、圆与 xy 轴的 4 个交点
// 第一步:初始化 7 个点
POINT pt[7];
pt[0].x = _x; pt[0].y = _y;
pt[1].x = int(_x + LIGHT_R * cos(a1) + 0.5); pt[1].y = int(_y + LIGHT_R * sin(a1) + 0.5);
pt[2].x = int(_x + LIGHT_R * cos(a2) + 0.5); pt[2].y = int(_y + LIGHT_R * sin(a2) + 0.5);
for (a = ceil(a1 * 4 / (2 * PI)) * (PI / 2), i = 3; a < a2; a += PI / 2, i++)
{
pt[i].x = int(_x + LIGHT_R * cos(a) + 0.5);
pt[i].y = int(_y + LIGHT_R * sin(a) + 0.5);
}
// 第二步:获取 7 个点的最大最小值,得到最小包围矩形
i--;
RECT r = {pt[i].x, pt[i].y, pt[i].x, pt[i].y};
for (--i; i >= 0; i--)
{
if (pt[i].x < r.left) r.left = pt[i].x;
if (pt[i].x > r.right) r.right = pt[i].x;
if (pt[i].y < r.top) r.top = pt[i].y;
if (pt[i].y > r.bottom) r.bottom = pt[i].y;
}
// 调整矩形范围
if (r.left < 0) r.left = 0;
if (r.top < 1) r.top = 1;
if (r.right >= WIDTH) r.right = WIDTH - 1;
if (r.bottom >= HEIGHT - 1) r.bottom = HEIGHT - 2;
// 修正曝光过度的点
for (y = r.top; y <= r.bottom; y++)
for (x = r.left; x <= r.right; x++)
{
i = y * WIDTH + x;
if (g_bufRender[i] > 0xffff00)
g_bufRender[i] = 0xffff00;
}
// 将光线模糊处理(避开建筑物)
for (y = r.top; y <= r.bottom; y++)
for (x = r.left; x <= r.right; x++)
{
i = y * WIDTH + x;
if (!g_bufMap[i])
g_bufRender[i] = RGB(
(GetRValue(g_bufRender[i - WIDTH]) + GetRValue(g_bufRender[i - 1]) + GetRValue(g_bufRender[i])
+ GetRValue(g_bufRender[i + 1]) + GetRValue(g_bufRender[i + WIDTH])) / 5,
(GetGValue(g_bufRender[i - WIDTH]) + GetGValue(g_bufRender[i - 1]) + GetGValue(g_bufRender[i])
+ GetGValue(g_bufRender[i + 1]) + GetGValue(g_bufRender[i + WIDTH])) / 5,
(GetBValue(g_bufRender[i - WIDTH]) + GetBValue(g_bufRender[i - 1]) + GetBValue(g_bufRender[i])
+ GetBValue(g_bufRender[i + 1]) + GetBValue(g_bufRender[i + WIDTH])) / 5);
}
}
// 绘制游戏者
void DrawPlayer()
{
// 画安全帽
setlinecolor(DARKGRAY);
circle(g_ptPlayer.x, g_ptPlayer.y, 5);
}
// 绘制出口
void DrawExit()
{
settextstyle(12, 0, _T("宋体"));
outtextxy(g_utExit.x * UNIT + g_ptOffset.x, g_utExit.y * UNIT + g_ptOffset.y + 8, _T("出口"));
}
// 获取用户输入的命令
int GetCmd()
{
int c = 0;
if ((GetAsyncKeyState(VK_LEFT) & 0x8000) ||
(GetAsyncKeyState('A') & 0x8000)) c |= CMD_LEFT;
if ((GetAsyncKeyState(VK_RIGHT) & 0x8000) ||
(GetAsyncKeyState('D') & 0x8000)) c |= CMD_RIGHT;
if ((GetAsyncKeyState(VK_UP) & 0x8000) ||
(GetAsyncKeyState('W') & 0x8000)) c |= CMD_UP;
if ((GetAsyncKeyState(VK_DOWN) & 0x8000) ||
(GetAsyncKeyState('S') & 0x8000)) c |= CMD_DOWN;
if (GetAsyncKeyState(VK_F2) & 0x8000) c |= CMD_RESTART;
if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) c |= CMD_QUIT;
MOUSEMSG m;
while(MouseHit())
{
m = GetMouseMsg();
g_ptMouse.x = m.x;
g_ptMouse.y = m.y;
}
return c;
}
// 向上移动
void OnUp()
{
int i = (g_ptPlayer.y - 6) * WIDTH + (g_ptPlayer.x - 5) + 1;
int j;
for (j = 0; j < 5; j++, i += 2)
if (g_bufMap[i])
break;
if (j == 5)
g_ptPlayer.y--;
}
// 向左移动
void OnLeft()
{
int i = (g_ptPlayer.y - 5) * WIDTH + (g_ptPlayer.x - 5);
int j;
for (j = 0; j < 5; j++, i += WIDTH)
if (g_bufMap[i])
break;
if (j == 5)
g_ptPlayer.x--;
}
// 向右移动
void OnRight()
{
int i = (g_ptPlayer.y - 5) * WIDTH + (g_ptPlayer.x + 5) + 1;
int j;
for (j = 0; j < 5; j++, i += WIDTH)
if (g_bufMap[i])
break;
if (j == 5)
g_ptPlayer.x++;
}
// 向下移动
void OnDown()
{
int i = (g_ptPlayer.y + 5) * WIDTH + (g_ptPlayer.x - 5) + 1;
int j;
for (j = 0; j < 5; j++, i += 2)
if (g_bufMap[i])
break;
if (j == 5)
g_ptPlayer.y++;
}
// 检查是否到出口
bool CheckWin()
{
return (g_ptPlayer.y >= g_utExit.y * UNIT + UNIT / 2 + g_ptOffset.y);
}
3.带一盏油灯去巡视仓库
/
// 程序名称:带一盏油灯去巡视仓库
// 编译环境:Visual C++ 6.0 / 2010,EasyX 2013霜降版
// 最后修改:2013-11-11
//
#include <graphics.h>
#include <conio.h>
#include <math.h>
#define RANGE 80 // 灯光照射半径
#define WIDTH 320 // 场景宽度
#define HEIGHT 240 // 场景高度
#define ZOOM 2 // 显示时的缩放倍数
#define PI 3.1415926536 // 圆周率
DWORD* g_bufMask; // 指向“建筑物”IMAGE 的指针
DWORD* g_bufRender; // 指向渲染 IMAGE 的指针
// 创建“仓库”
void MakeMask()
{
// 创建“仓库” IMAGE 对象
static IMAGE g_imgMask(WIDTH, HEIGHT);
g_bufMask = GetImageBuffer(&g_imgMask);
// 设置绘图目标
SetWorkingImage(&g_imgMask);
// 绘制“仓库”
settextstyle(100, 0, _T("Courier"));
outtextxy(8, 60, _T("EasyX"));
// 恢复绘图目标为默认窗口
SetWorkingImage(NULL);
}
// 在指定位置“照明”
void Lighting(int _x, int _y)
{
int i; // 定义循环变量
// 清空 Render 对象
memset(g_bufRender, 0, WIDTH * HEIGHT * 4);
// 计算灯光照亮的区域
for(double a = 0; a < 2 * PI; a += PI / 180) // 圆周循环
{
for(int r = 0; r < RANGE; r++) // 半径循环
{
// 计算照射到的位置
int x = (int)(_x + cos(a) * r);
int y = (int)(_y + sin(a) * r);
// 光线超出屏幕范围,终止
// (为了简化全凭模糊运算,不处理最上和最下一行)
if (x < 0 || x >= WIDTH || y <= 0 || y >= HEIGHT - 1)
break;
// 光线碰到建筑物,终止
if(g_bufMask[y * WIDTH + x])
break;
// 光线叠加
g_bufRender[y * WIDTH + x] += 0x101000; // 0x101000 是很淡的黄色
}
}
// 修正曝光过度的点
for (i = WIDTH * HEIGHT - 1; i >= 0; i--)
if (g_bufRender[i] > 0xffff00)
g_bufRender[i] = 0xffff00;
// 将光线模糊处理(避开建筑物)
for(i = WIDTH; i < WIDTH * (HEIGHT-1); i++)
if (!g_bufMask[i])
for (int j = 0; j < 2; j++)
{
g_bufRender[i] = RGB(
(GetRValue(g_bufRender[i - WIDTH]) + GetRValue(g_bufRender[i - 1]) + GetRValue(g_bufRender[i])
+ GetRValue(g_bufRender[i + 1]) + GetRValue(g_bufRender[i + WIDTH])) / 5,
(GetGValue(g_bufRender[i - WIDTH]) + GetGValue(g_bufRender[i - 1]) + GetGValue(g_bufRender[i])
+ GetGValue(g_bufRender[i + 1]) + GetGValue(g_bufRender[i + WIDTH])) / 5,
(GetBValue(g_bufRender[i - WIDTH]) + GetBValue(g_bufRender[i - 1]) + GetBValue(g_bufRender[i])
+ GetBValue(g_bufRender[i + 1]) + GetBValue(g_bufRender[i + WIDTH])) / 5);
}
}
// 主函数
void main()
{
// 初始化绘图窗口
initgraph(WIDTH * ZOOM, HEIGHT * ZOOM);
BeginBatchDraw();
DWORD* bufScreen = GetImageBuffer(NULL);
// 制作建筑
MakeMask();
// 创建渲染对象
IMAGE imgRender(WIDTH, HEIGHT);
g_bufRender = GetImageBuffer(&imgRender);
// 定义鼠标消息变量
MOUSEMSG msg;
while(true)
{
// 获取一条鼠标移动的消息
do
{
msg = GetMouseMsg();
}
while((msg.uMsg != WM_MOUSEMOVE) || MouseHit());
// 在鼠标位置模拟灯光
Lighting(msg.x / ZOOM, msg.y / ZOOM);
// 将渲染的内容拉伸后显示在绘图窗口中
int ps = 0, pr = 0;
for (int y = 0; y < HEIGHT; y++)
for (int x = 0; x < WIDTH; x++, pr++)
{
ps = y* ZOOM * WIDTH * ZOOM + x * ZOOM;
for (int zy = 0; zy < ZOOM; zy++)
{
for (int zx = 0; zx < ZOOM; zx++)
bufScreen[ps++] = g_bufRender[pr];
ps += ZOOM * (WIDTH - 1);
}
}
// 刷新显示,并延时
FlushBatchDraw();
Sleep(20);
// 按任意键退出
if (_kbhit())
{
EndBatchDraw();
closegraph();
}
}
}