版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第10章图形与游戏程序设计C语言大学实用教程本章内容EasyX图形库,图形用户界面,鼠标和键盘消息,事件驱动编程计算机动画,双缓存技术10.1动画的设计的基本原理计算机动画动态地产生一系列静止、独立而又存在一定内在联系的画面,然后将其按一定的播放速度显示出来其中,当前帧画面是对前一帧画面的局部修改为什么一系列静止的画面会产生运动的视觉效果呢?计算机动画与电影和电视拍摄的基本原理类似都是利用了人眼的视觉暂留现象10.1动画的设计的基本原理计算机动画程序的基本代码框架while(1)//循环显示每帧画面{
清屏
显示当前帧画面
延时
更新图形}10.1动画的设计的基本原理为什么在显示一帧画面后要有延时处理呢?降低屏幕图形闪烁现象,需要让显示在屏幕上的图形在屏幕上停留几毫秒的时间Sleep(200)表示延时200ms需要包含windows.h10.1动画的设计的基本原理如何解决图形动态显示时产生的屏幕闪烁问题?采用双缓存技术(DoubleBuffering)基本原理是:不直接在屏幕上逐像素绘制,而是先在内存中的“缓冲区”里完成所有绘制操作,最后再将整个缓冲区的内容一次性复制到屏幕上。如何解决图形动态显示时产生的屏幕闪烁问题?采用双缓存技术(DoubleBuffering)基本原理是:不直接在屏幕上逐像素绘制,而是先在内存中的“缓冲区”里完成所有绘制操作,最后再将整个缓冲区的内容一次性复制到屏幕上。10.1动画的设计的基本原理如何解决图形动态显示时产生的屏幕闪烁问题?EasyX中“简化版的双缓存方式”基本逻辑用BeginBatchDraw()开启批量绘制模式,本质是写入后台缓冲区用EndBatchDraw()结束批量绘制模式,本质是把后台缓冲区内容拷贝到前台在实时动态更新画面的场景下,可使用FlushBatchDraw()代替EndBatchDraw()FlushBatchDraw():立即将缓冲区内容刷新到屏幕,但不会结束批量绘制模式,允许后续继续在缓冲区中绘制EndBatchDraw():结束整个批量绘制流程10.1动画的设计的基本原理如何解决图形动态显示时产生的屏幕闪烁问题?EasyX中“简化版的双缓存方式”典型的动画循环流程为:BeginBatchDraw()->清屏(cleardevice())->绘制新帧->FlushBatchDraw()->Sleep()->循环在绘制完成后,调用EndBatchDraw()关闭双缓冲模式使用EasyX库函数编写图形程序一定要将源代码保存为C++文件并在源代码中一定要包含头文件<graphics.h>10.2时钟模拟【例10-1】请用EasyX图形库函数编写一个实时模拟时钟程序。程序的整体流程为:Step1:调用EasyX库函数initgraph()初始化图形窗口Step2:调用EasyX库函数BeginBatchDraw()开启批量绘图(防闪烁)Step3:调用VS的扩展函数_kbhit()检测用户按键操作,若没有按键,则循环执行:10.2时钟模拟Step3:调用VS的扩展函数_kbhit()检测用户按键操作,若没有按键,则循环执行:Step3.1:调用EasyX库函数cleardevice()清空屏幕Step3.2:调用自定义函数DrawClock()绘制完整时钟Step3.3:调用EasyX库函数FlushBatchDraw()刷新画面Step3.4:延时100ms,即每100毫秒重绘一次时钟,实现指针动态走动效果Step4:检测到按键退出循环后,调用EasyX库函数EndBatchDraw()关闭双缓冲模式Step5:调用EasyX库函数closegraph()关闭图形窗口10.2时钟模拟自定义一个时钟绘制函数DrawClock()核心逻辑为:实时获取系统时间→计算指针角度→绘制指针→循环刷新算法如下:Step1:绘制表盘背景画一个大圆作为表盘底色,半径比刻度大20%,让表盘更美观Step2:利用循环绘制表盘1~12的数字采用num×2π/12计算弧度,每个数字间隔30°(π/6弧度)10.2时钟模拟Step3:获取系统当前时间调用C标准库<time.h>中的函数time()获取实时系统时间利用VS的扩展函数localtime_s()将time_t类型的时间戳转换为本地时间:tm_hour(时)、tm_min(分)、tm_sec(秒)Step4~6:绘制时针、分针、秒针弧度计算方法相同,仅长度、粗细、颜色、计算精度不同时针是:2π/12(12小时一圈)分针/秒针:×2π/60(60格一圈)长度规则是:秒针最长>分针>时针最短对时针/分针计算偏移量,模拟真实时钟的连续走动,而非跳变Step7:绘制中心红点Step8:显示数字日期时间。将系统时间格式化为字符串,显示在时钟下方,补充文字时间10.2时钟模拟#include<graphics.h>#include<conio.h>#include<time.h>#include<math.h>#include<stdio.h>#definePI3.14159265358979323846structPoint{floatx;floaty;};//注意:屏幕坐标系是y轴向下//函数功能:计算坐标PointGetPos(Pointcenter,floatrad,floatr){Pointpos;pos.x=(float)(sin(rad)*r+center.x);pos.y=(float)(-cos(rad)*r+center.y);returnpos;}10.2时钟模拟//函数功能:绘制时钟voidDrawClock(void){floatpi2=(float)(PI*2),h,m,s,r=150;time_tt_now;TCHARstr[64];Pointcenter,p;center.x=320,center.y=200;settextstyle(24,0,_T("CourierNew"));setbkmode(TRANSPARENT);//1.绘制表盘背景
setfillcolor(RGB(0xFF,0xFF,0xFF));setlinecolor(RGB(0x40,0x40,0x40));fillcircle((int)center.x,(int)center.y,(int)(r*1.2f));
10.2时钟模拟//函数功能:绘制时钟voidDrawClock(void){……//2.绘制表盘上的数字
settextcolor(BLACK);for(intnum=1;num<=12;++num){p=GetPos(center,(float)(num*pi2/12),r);_stprintf_s(str,_T("%d"),num);outtextxy((int)p.x-10,(int)p.y-12,str);}//3.获取当前时间
time(&t_now);structtmt;localtime_s(&t,&t_now);10.2时钟模拟//函数功能:绘制时钟voidDrawClock(void){……//4.绘制时针
setlinecolor(RGB(0x60,0x60,0x60));setlinestyle(PS_SOLID,10);h=(float)(t.tm_hour%12+t.tm_min/60.0);p=GetPos(center,h*pi2/12,r*0.5f);line((int)p.x,(int)p.y,(int)center.x,(int)center.y);//5.绘制分针
setlinecolor(RGB(0x60,0x60,0x60));setlinestyle(PS_SOLID,5);m=(float)(t.tm_min+t.tm_sec/60.0);p=GetPos(center,m*pi2/60,r*0.8f);line((int)p.x,(int)p.y,(int)center.x,(int)center.y);
10.2时钟模拟//函数功能:绘制时钟voidDrawClock(void){……//6.绘制秒针
setlinecolor(RGB(0x80,0x80,0x80));setlinestyle(PS_SOLID,2);s=(float)t.tm_sec;p=GetPos(center,s*pi2/60,r*1.0f);line((int)p.x,(int)p.y,(int)center.x,(int)center.y);//7.中心装饰点
setfillcolor(RED);solidcircle((int)center.x,(int)center.y,5);
//8.显示日期时间
_stprintf_s(str,_T("%d/%02d/%02d%02d:%02d:%02d"),t.tm_year+1900,t.tm_mon+1,t.tm_mday,t.tm_hour,t.tm_min,t.tm_sec);settextcolor(BLACK);outtextxy(200,420,str);}10.2时钟模拟intmain(void){initgraph(640,480);//初始化图形窗口
setbkcolor(WHITE);// 设置窗口背景色
cleardevice();//清屏
BeginBatchDraw();//开启批量绘图,防止闪烁
while(!_kbhit())//没有按键则继续循环
{cleardevice();//清屏
DrawClock();//绘制时钟
FlushBatchDraw();//刷新缓冲区到屏幕
Sleep(100);//降低CPU占用
}EndBatchDraw();//结束批量绘制
closegraph();//关闭图形窗口
return0;}10.3简单的计算器【例10-2】请用EasyX图形库函数编写一个简单的计算器程序,能够执行+、-、*、/运算,按等号则输出运算结果。程序的整体流程为:Step1:绘制界面;Step2:监听鼠标点击;Step3:拼接表达式字符串(即用字符串存输入的表达式);Step4:若按下'='键,则逐个解析字符串(数字+运算符)中的字符,若遇到数字,则拼接成数,若遇到操作符,则按优先级进行计算;Step5:按“先乘除后加减”计算表达式;Step6:显示计算结果;Step7:选择继续或者退出。10.3简单的计算器【例10-2】请用EasyX图形库函数编写一个简单的计算器程序,能够执行+、-、*、/运算,按等号则输出运算结果。1.表达式的计算本例不使用栈,而使用“双值+双运算符”实现带优先级的四则运算变量an2:记录上一个结果(先算好的左边值)变量an1:记录当前数字(刚解析出来的右边值)变量ls2:记录上一个运算符,使用变量ls1记录当前运算符运算符的优先级规则:(1)乘除*/:优先级=2(2)加减+-:优先级=1判断方法为:当abs(ls1)>abs(ls2)时,先算当前的运算,否则先算前面的运算遇到高优先级,就先算当前运算,遇到低优先级就先算前一运算,最后算一次10.3简单的计算器以计算表达式1+2*3–4为例:Step1:解析1,an2=0an1=1ls1=1(+)Step2:解析2,an2=1an1=2ls1=2(*)Step3:解析3,发现*优先级高于+先算:2*3=6,an1=6,ls1变回+Step4:解析-,优先级低于+先算:1+6=7,an2=7,an1=4,ls1=-1Step5:结束,算最后一次,7-4=310.3简单的计算器2.数字的解析算法需要先解析整数部分,再解析小数部分整数部分的解析算法为:用x=当前字符-'0'将当前字符转换为数字;如果下一个还是数字,则x=x*10+下一个数字小数部分的解析算法为:若遇到小数点,则j=0.1;将每一位数字*j循环累加到x中,然后更新j,即j*=0.1还要对输入进行错误检查数字不能以前导零开头,不能有连续符号1++2,不能除以0,首尾不能是操作符10.3简单的计算器3.鼠标交互的核心逻辑Step1:遍历16个按钮;Step2:判断鼠标坐标是否在按钮矩形内;Step3:如果在,则把按钮字符加入字符串;Step4:实时显示在屏幕上;Step5:若有按键'=',则结束输入。10.3简单的计算器#define_CRT_SECURE_NO_WARNINGS1#include<graphics.h>//包含图形库头文件,用于绘图和窗口显示#include<windows.h>//包含Windows系统API头文件,用于消息框、窗口操作#include<stdio.h>//标准输入输出头文件,用于字符串格式化#include<math.h>//数学库头文件,用于数值计算#include<ctype.h>//字符处理头文件,用于判断数字字符structbutt//按钮结构体:存储每个按钮的坐标和显示字符{intx,y;//按钮左上角的屏幕坐标x、ywchar_tc;//按钮上显示的宽字符(0-9+-*/=.)};10.3简单的计算器structbutts[16]={//定义16个计算器按钮的位置与文字
{25,540,L'0'},//第0行按钮:0{135,540,L'.'},//第0行按钮:.{245,540,L'='},//第0行按钮:={355,540,L'+'},//第0行按钮:+{25,450,L'1'},//第1行按钮:1{135,450,L'2'},//第1行按钮:2{245,450,L'3'},//第1行按钮:3{355,450,L'-'},//第1行按钮:-{25,360,L'4'},//第2行按钮:4{135,360,L'5'},//第2行按钮:5{245,360,L'6'},//第2行按钮:6{355,360,L'*'},//第2行按钮:*
{25,270,L'7'},//第3行按钮:7{135,270,L'8'},//第3行按钮:8{245,270,L'9'},//第3行按钮:9{355,270,L'/'}//第3行按钮:/};//L(前缀)含义是:强制按Unicode(宽字符,wchar_t)存储,每个字符占2字节。需要定义类型为constwchar_t*,它不受编译选项影响,永远是宽字符,适合于明确要宽字符的应用场景。10.3简单的计算器constintpy[6]={43,75,107,139,171,205};//显示屏每行文字的Y坐标#defineMAX_STR_LEN1024//输入表达式字符串的最大长度voidDrawBoard(void);intGetOperatorType(wchar_tc);doubleCalculate(doublex,doubley,inttype);intInputExpression(wchar_tS[],intmaxLen);doubleParseAndCalculate(constwchar_tS[],intstrLen,int*needRestart);voidShowResult(doubleans);10.3简单的计算器//函数功能:界面绘制voidDrawBoard(void){setbkcolor(WHITE);//设置背景颜色为白色
cleardevice();//清空整个绘图窗口
setcolor(BLACK);//设置线条颜色为黑色
setfillcolor(BLACK);//设置填充颜色为黑色
settextcolor(BLACK);//设置文字颜色为黑色
rectangle(10,10,470,630);//绘制计算器最外层大边框
rectangle(25,35,455,250);//绘制显示屏区域边框
settextstyle(37,0,L"CourierNew");//设置文字样式
for(inti=0;i<=15;i++)//循环绘制16个按钮
{rectangle(s[i].x,s[i].y,s[i].x+100,s[i].y+70);
wchar_tch[2]={s[i].c,0};//将按钮字符转为字符串
outtextxy(s[i].x+38,s[i].y+15,ch);}}10.3简单的计算器//函数功能:运算符类型判断intGetOperatorType(wchar_tc){switch(c){caseL'+':return1;//加法→返回1caseL'-':return-1;//减法→返回-1caseL'*':return2;//乘法→返回2caseL'/':return-2;//除法→返回-2default:return0;//非运算符→返回0}}10.3简单的计算器//函数功能:四则运算执行doubleCalculate(doublex,doubley,inttype){switch(type){case1:returnx+y;//加法
case-1:returnx-y;//减法
case2:returnx*y;//乘法
case-2:returnx/y;//除法
default:return0;//默认返回0}}10.3简单的计算器//函数功能:鼠标输入与表达式拼接intInputExpression(wchar_tS[],intmaxLen){intstrLen=0;//记录输入字符串的当前长度
ExMessagemsg;//定义鼠标消息变量
while(1)//循环监听鼠标事件
{if(peekmessage(&msg,EX_MOUSE))//获取鼠标消息
{if(msg.message==WM_LBUTTONDOWN)//判断鼠标左键按下
{……//见下两页代码}}Sleep(10);//延时10ms,降低CPU占用
}returnstrLen;//返回输入的字符串长度}10.3简单的计算器if(msg.message==WM_LBUTTONDOWN)//判断鼠标左键按下{intflag=0;//标记是否按下=号
for(inti=0;i<=15;i++)//遍历16个按钮,判断点击了哪一个
{//判断鼠标坐标是否落在当前按钮区域内
if(msg.x>=s[i].x&&msg.x<=s[i].x+100&&msg.y>=s[i].y&&msg.y<=s[i].y+70){S[strLen++]=s[i].c;//将按钮字符加入字符串
S[strLen]=0;//添加字符串结束符
if(s[i].c==L'=')//如果按下={flag=1;//标记结束输入
break;}
10.3简单的计算器if(strLen>120)//如果输入过长,则弹出错误提示
{MessageBoxW(GetHWnd(),L"输入过长!",L"错误",MB_OK);strLen=0;//清空字符串
S[0]=0;DrawBoard();//重新绘制界面
break;}BeginBatchDraw();//开启批量绘图,防止闪烁
intij=0;//行号
//分行显示输入内容(每行最多20个字符)
for(intj=0;j<strLen;j+=20,ij++){intlen=min(strLen-j,20);//取当前行字符数
wchar_ttemp[256]={0};//临时字符串
wcsncpy(temp,S+j,len);//复制一行内容
outtextxy(41,py[ij],temp);//输出到屏幕
}EndBatchDraw();//结束批量绘图
}//endif}//endforif(flag)break;//按下=号,退出输入循环}10.3简单的计算器//函数功能:表达式解析与计算核心doubleParseAndCalculate(constwchar_tS[],intstrLen,int*needRestart){doublean1=0,an2=0;//an1:当前数字an2:上一个结果
intls1=1,ls2=0;//ls1:当前运算符ls2:上一运算符
intn=strLen;//字符串长度*needRestart=0;//初始化:不需要重启
wchar_ttmp_S[MAX_STR_LEN];//临时字符串,用于修改处理
wcscpy_s(tmp_S,MAX_STR_LEN,S);//复制原字符串
if(!iswdigit(tmp_S[0])&&tmp_S[0]!=L'-')//判首字符是否非法
{MessageBoxW(GetHWnd(),L"输入格式错误!",L"错误",MB_OK);*needRestart=1;//标记需要重启
return0;}if(tmp_S[0]==L'-')//如果以负号开头,前面补0,变成"0-xxx"{for(inti=strLen+1;i>=1;i--)tmp_S[i]=tmp_S[i-1];tmp_S[0]=L'0';n++;}10.3简单的计算器//函数功能:表达式解析与计算核心doubleParseAndCalculate(constwchar_tS[],intstrLen,int*needRestart){……intvalid=1;//标记输入是否合法
for(inti=0;i<n&&valid;i++)//解析数字与运算符
{if(!iswdigit(tmp_S[i]))//如果当前字符不是数字
{//判断是否出现连续符号、小数点错误
if(i==0||!iswdigit(tmp_S[i-1])||tmp_S[i]==L'.'){valid=0;break;}if(i==n-1)break;//最后一位是符号,跳过
ls2=ls1;//保存上一个运算符
ls1=GetOperatorType(tmp_S[i]);//获取当前运算符
}10.3简单的计算器doublex=(tmp_S[i]-L'0');//转为数字
//判断是否有前导0(如09错误)
if(tmp_S[i]==L'0'&&i+1<n&&iswdigit(tmp_S[i+1])){valid=0;break;}while(i+1<n&&iswdigit(tmp_S[i+1])){//解析整数
i++;x=x*10+(tmp_S[i]-L'0');}if(i+1<n&&tmp_S[i+1]==L'.'){//解析小数部分i+=2;if(i>=n||!iswdigit(tmp_S[i])){valid=0;break;}for(doublej=0.1;;j*=0.1,i++){x+=j*(tmp_S[i]-L'0');if(i+1>=n||!iswdigit(tmp_S[i+1]))break;}}10.3简单的计算器if(fabs(x)<1e-16&&ls1==-2)//判断除数为0{MessageBoxW(GetHWnd(),L"错误:除数不能为0!",L"错误",MB_OK);*needRestart=1;return0;}if(ls2==0)//根据运算符优先级计算
{an2=an1;an1=x;}elseif(abs(ls1)>abs(ls2)){an1=Calculate(an1,x,ls1);ls1=ls2;ls2=0;}else{an2=Calculate(an2,an1,ls2);an1=x;ls2=0;}}}10.3简单的计算器if(!valid)//如果格式错误
{MessageBoxW(GetHWnd(),L"输入格式错误!",L"错误",MB_OK);*needRestart=1;return0;}if(ls1!=0)an1=Calculate(an2,an1,ls1);//执行最后一次运算
returnan1;//返回最终结果}//函数功能:显示计算结果voidShowResult(doubleans){DrawBoard();//重新绘制计算器界面
wchar_tres[256];//结果字符串数组
swprintf(res,256,L"%g",ans);//将数字转为宽字符串
outtextxy(41,py[0],res);//在显示屏第一行输出结果}10.3简单的计算器intmain(void){while(1)//无限循环:实现多次计算
{wchar_tS[MAX_STR_LEN]={0};intneedRestart=0;initgraph(480,640);DrawBoard();intlen=InputExpression(S,MAX_STR_LEN);doubleans=ParseAndCalculate(S,len,&needRestart);if(needRestart){closegraph();continue;}ShowResult(ans);intret=MessageBoxW(GetHWnd(),L"是否继续计算?",L"提示",MB_OKCANCEL);closegraph();if(ret!=IDOK)break;}return0;}10.4迷宫游戏【例10-3】采用EasyX图形库函数编写一个人机交互方式走迷宫。从文件读入迷宫的高度和宽度,以及相应尺寸的迷宫地图数据,在屏幕上显示迷宫地图然后由用户用鼠标点击左键指定迷宫的起点与终点,用键盘方向键控制移动玩家的位置到达终点后弹出“恭喜!成功走出迷宫!”的提示框,点击提示框的“确定”按钮,程序结束。按ESC键,也可以直接退出程序。10.4迷宫游戏假设用于保存迷宫地图的文本文件为maze.txt,数据格式:12121111111111111000000000011011111111111010001000111010101010111010101010011010101010011010101011011010101011011010101011011000100011011111111111111代表该位置不可达(即障碍物、墙壁或者边界)0代表该位置可达(即路),2表示玩家位置10.4迷宫游戏使用EasyX的消息结构体ExMessage和库函数peekmessage()循环监听监测并处理鼠标和键盘消息先由用户鼠标确定起点和终点,起点用绿色圆圈显示,终点用红色圆圈显示,玩家所在的位置用蓝色方块显示根据键盘方向键确定玩家的位置,利用迷宫地图重绘方式展示玩家的位置移动,在移动过程中检测是否到达终点,到达终点后,弹出提示框并结束程序。10.4迷宫游戏程序的实现流程:Step1:初始化图形窗口Step2:从文件读取迷宫地图Step3:初始化图形窗口的布局Step4:绘制迷宫Step5:鼠标操作获取起点和终点Step6:根据用户的输入更新迷宫地图,并判断是否到达终点Step6.1:初始化玩家位置Step6.2:绘制初始迷宫Step6.3:开始循环监听,检测并处理消息(非阻塞)若用户按了ESC键,则退出循环若用户按了方向键,则相应地更新玩家的位置,实现上下左右移动更新和重绘迷宫地图(旧位置还原为路,新位置标记为玩家)如果到达了终点,则给出提示,并退出程序10.3简单的计算器#define_CRT_SECURE_NO_WARNINGS1#include<graphics.h>#include<stdio.h>#include<stdlib.h>#include<windows.h>#defineN50//迷宫最大行数#defineM50//迷宫最大列数#defineROUTE0//路#defineWALL1//墙#definePLAYER2//玩家#defineMIN_MAZE_SIZE5//迷宫最小尺寸#defineMAX_MAZE_SIZE40//迷宫最大尺寸#defineWINDOW_WIDTH1000//窗口宽度#defineWINDOW_HEIGHT700//窗口高度#defineCELL_SIZE_MIN10//单元格最小尺寸#defineCELL_SIZE_MAX30//单元格最大尺寸#defineSHOW_DELAY50//动画延迟(ms),值越大越慢intmazeMap[N][M];//迷宫地图inthigh=10,width=20;//迷宫实际尺寸intcellSize=20;//单元格大小intoffsetX=10,offsetY=10;//迷宫绘制偏移intstartX=-1,startY=-1;//起点坐标intendX=-1,endY=-1;//终点坐标10.3简单的计算器//函数功能:从文件读取迷宫地图voidReadMazeFile(intmazeMap[][M],int*high,int*width){FILE*fp=fopen("maze.txt","r");//VSCode下需给出文件完整路径
if(fp==NULL){printf("Cannotopenthefilemaze.txt\n");exit(0);}fscanf(fp,"%d%d",high,width);for(inti=0;i<*high;i++){for(intj=0;j<*width;j++){fscanf(fp,"%d",&mazeMap[i][j]);}}fclose(fp);}10.3简单的计算器//函数功能:根据窗口和迷宫大小计算网格调整迷宫绘制布局voidInitLayout(inthigh,intwidth){intwinW=getwidth();//获取窗口宽度
intwinH=getheight();//获取窗口高度
//计算可用区域时使用迷宫实际尺寸而非最大尺寸
intavailW=(winW<width*CELL_SIZE_MIN)?width*CELL_SIZE_MIN:winW;intavailH=(winH<high*CELL_SIZE_MIN)?high*CELL_SIZE_MIN:winH;intmaxCellW=availW/width;//计算最大单元宽度
intmaxCellH=availH/high;//计算最大单元高度
cellSize=(maxCellW<maxCellH)?maxCellW:maxCellH;
cellSize=(cellSize>CELL_SIZE_MAX)?CELL_SIZE_MAX:cellSize;//避免过大
cellSize=(cellSize<CELL_SIZE_MIN)?CELL_SIZE_MIN:cellSize;//避免过小}10.3简单的计算器//函数功能:绘制迷宫(含起点/终点)voidDrawMaze(intmazeMap[][M],inthigh,intwidth){BeginBatchDraw();//双缓冲,避免闪烁
cleardevice();//清空图形窗口
for(inti=0;i<high;i++){//绘制high行width列迷宫网格
for(intj=0;j<width;j++){intx=offsetX+j*cellSize;//格子左上角x坐标
inty=offsetY+i*cellSize;//格子左上角y坐标
if(mazeMap[i][j]==WALL){//绘制墙
setfillcolor(RGB(60,60,60));//设置墙颜色为灰色
setlinecolor(RGB(50,50,50));//设置墙颜色为深灰色
}elseif(mazeMap[i][j]==ROUTE){//绘制路
setfillcolor(WHITE);//绘制路为白色
setlinecolor(RGB(200,200,200));//设置路颜色为浅灰
}elseif(mazeMap[i][j]==PLAYER){//绘制玩家
setfillcolor(RGB(100,200,255));//绘制玩家为蓝色
setlinecolor(RGB(80,180,235));//绘制玩家为浅蓝色
}fillrectangle(x,y,x+cellSize-1,y+cellSize-1);//填充格子
}}10.3简单的计算器//函数功能:绘制迷宫(含起点/终点)voidDrawMaze(intmazeMap[][M],inthigh,intwidth){
。。。。。。if(startX>=0&&startY>=0){//起点有效
intcx=offsetX+startY*cellSize+cellSize/2;
intcy=offsetY+startX*cellSize+cellSize/2;
setfillcolor(GREEN);//设置起点填充色为绿色
setlinecolor(RGB(0,150,0));//设置边框颜色为深绿色
fillcircle(cx,cy,cellSize/3);//填充圆圈,表示这是起点
}if(endX>=0&&endY>=0){//终点有效
intcx=offsetX+endY*cellSize+cellSize/2;
intcy=offsetY+endX*cellSize+cellSize/2;
setfillcolor(RED);//绘制终点填充色为红色
setlinecolor(RGB(200,0,0));//绘制终点边框颜色为深红色
fillcircle(cx,cy,cellSize/3);//填充圆圈,表示这是终点
}EndBatchDraw();//结束双缓冲绘制}10.3简单的计算器//函数功能:鼠标选择迷宫的起点和终点voidGetStartEnd(void){BeginBatchDraw();//开始批量绘制
setfillcolor(RGB(240,240,240));//设置填充颜色为浅灰色
solidrectangle(0,offsetY+high*cellSize+5,WINDOW_WIDTH,offsetY+high*cellSize+40);//填充矩形
settextcolor(BLACK);//设置文本颜色为黑色
settextstyle(20,0,_T("宋体"));//设置文本样式为20号宋体
outtextxy(offsetX,offsetY+high*cellSize+10,_T("请用鼠标点击选择起点(绿色圆圈)"));//提示选择起点
EndBatchDraw();//结束批量绘制
MOUSEMSGmsg;//鼠标事件消息
10.3简单的计算器while(1){//循环等待鼠标点击
msg=GetMouseMsg();//获取鼠标事件消息
if(msg.uMsg==WM_LBUTTONDOWN){//鼠标左键点击选择起点
intcol=(msg.x-offsetX)/cellSize;//鼠标点击的列
introw=(msg.y-offsetY)/cellSize;//鼠标点击的行
if(row>=0&&row<high&&col>=0&&col<width&&mazeMap[row][col]==ROUTE){startX=row;//设置起点行坐标
startY=col;//设置起点列坐标
DrawMaze(mazeMap,high,width);//绘制迷宫
break;//跳出循环
}}}BeginBatchDraw();//开始批量绘制
setfillcolor(RGB(240,240,240));//设置填充颜色为浅灰色
solidrectangle(0,offsetY+high*cellSize+5,WINDOW_WIDTH,offsetY+high*cellSize+40);//填充矩形
settextcolor(BLACK);//设置文本颜色为黑色
settextstyle(20,0,_T("宋体"));//设置文本样式为20号宋体
outtextxy(offsetX,offsetY+high*cellSize+10,_T("请用鼠标点击选择终点(红色圆圈)"));//提示选择终点
EndBatchDraw();//结束批量绘制10.3简单的计算器while(1){//循环等待鼠标点击
msg=GetMouseMsg();//获取鼠标事件消息
if(msg.uMsg==WM_LBUTTONDOWN){//鼠标左键点击选择终点
intcol=(msg.x-offsetX)/cellSize;//鼠标点击的列
introw=(msg.y-offsetY)/cellSize;//鼠标点击的行
if(row>=0&&row<high&&col>=0&&col<width&&mazeMap[row][col]==ROUTE&&(row!=startX||col!=startY)){//选择终点
endX=row;//设置终点行坐标
endY=col;//设置终点列坐标
DrawMaze(mazeMap,high,width);//绘制迷宫
break;//跳出循环
}}}}10.3简单的计算器//函数功能:根据用户的输入更新迷宫地图,并判断是否到达终点voidUpdateWithInput(intmazeMap[][M],inthigh,intwidth,intplayerX,intplayerY,intendX,intendY){mazeMap[playerX][playerY]=PLAYER;//初始化玩家位置
DrawMaze(mazeMap,high,width);//绘制初始迷宫
ExMessagemsg;//定义消息结构体
while(true){//开始循环监听,检测并处理消息(非阻塞)
if(peekmessage(&msg,EM_KEY,true)){//参数3设为true
if(msg.message==WM_KEYDOWN&&msg.vkcode==VK_ESCAPE){return;//按ESC退出循环
}if(msg.message==WM_KEYDOWN){//处理方向键
intoldX=playerX;//保存当前x坐标(用于还原)
intoldY=playerY;//保存当前y坐标(用于还原)10.3简单的计算器switch(msg.vkcode){//移动逻辑)
caseVK_LEFT://左移(左方向键)
if(playerY-1>=0&&mazeMap[playerX][playerY-1]!=WALL){playerY--;}break;caseVK_RIGHT://右移(右方向键)
if(playerY+1<width&&mazeMap[playerX][playerY+1]!=WALL){playerY++;}break;caseVK_UP://上移(上方向键)
if(playerX-1>=0&&mazeMap[playerX-1][playerY]!=WALL){playerX--;}break;caseVK_DOWN://下移(下方向键)
if(playerX+1<high&&mazeMap[playerX+1][playerY]!=WALL){playerX++;}break;default:break;}10.3简单的计算器if(oldX!=playerX||oldY!=playerY){mazeMap[oldX][oldY]=ROUTE;
mazeMap[playerX][playerY]=PLAYER;
DrawMaze(mazeMap,high,width);//重绘迷宫
}if(playerX==endX&&playerY==endY){
MessageBox(NULL,_T("恭喜!成功走出迷宫!"),_T("胜利"),MB_OK|MB_ICONINFORMATION);return;//退出函数
}}}Sleep(SHOW_DELAY);//轻微延时,避免闪烁
}}10.3简单的计算器intmain(void){initgraph(WINDOW_WIDTH,WINDOW_HEIGHT);//初始化图形窗口
setbkcolor(RGB(240,240,240));//设置窗口背景色为灰色
ReadMazeFile(mazeMap,&high,&width);//从文件读取迷宫地图
InitLayout(high,width);//初始化布局
DrawMaze(mazeMap,high,width);//绘制迷宫
GetStartEnd();//获取起点和终点
UpdateWithInput(mazeMap,high,width,startX,startY,endX,endY);closegraph();//关闭图形窗口
return0;}例10.4迷宫自动寻路两种简单的搜索策略广度优先搜索深度优先搜索暂时不考虑最短路径深搜实现迷宫自动寻路深搜原理:从入口出发沿着某个方向向前探索,若能走通,则继续往前否则沿原路退回(回溯),换方向继续探索,直至走到出口为止如何保证能够沿原路退回(回溯)呢?后进先出的结构(递归)——保存从入口到当前位置的路径暂时不考虑最短路径深搜自动走迷宫122345655678910117891011121310上下岔路口1岔路口2岔路口3岔路口4左右右上下上下构建一个搜索树暂时不考虑最短路径出口迷宫自动寻路基于深度优先搜索算法求迷宫问题的一个解,就是要在状态树中搜索从根节点到目标节点(目标状态)的一条可行路径,算法的基本思想是:(1)若当前位置“可通”(例如未越界、无障碍、未被访问过),则将其标记为已访问并放入“当前路径”,并继续朝“下一位置”探索,即切换“下一位置”为“当前位置”,如此重复直至到达出口;(2)若当前位置“不可通”,则应沿着“来向”回溯到上一步即“前一通道块”,取消当前坐标的访问标记,然后朝着除“来向”之外的其他方向继续探索;(3)若该通道块的四周4个方块均“不可通”,则从“当前路径”上删除该通道块。这里,“当前位置”是指“在搜索过程中的某一时刻所在图中的某个方块位置”,“下一位置”是指当前位置周围4个方向(上下左右)上相邻的方块。新增一个DFS迷宫自动寻路的函数SearchWithDFS(),同时修改函数DrawMaze(),增加回溯标记,并修改主函数#definePATH3//回溯痕迹intflag=0;//DFS寻路终止标记//函数功能:绘制迷宫(含起点/终点/寻路状态)voidDrawMaze(intmazeMap[][M],inthigh,intwidth){BeginBatchDraw();//双缓冲,避免闪烁
cleardevice();//清空图形窗口
for(inti=0;i<high;i++){//绘制high行width列迷宫网格
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 保险行业客户关系管理知识点巩固习题
- 保险消费者权益保护与法律法规习题集
- 食堂餐具卫生管控培训考核试题附答案
- 芜湖市公务员考试(财会专业知识、财会类)试题解析+高频考点-命题趋势深度解析(2026年)
- 2026年甘肃省敦煌市高二生物上册期末考试测试卷【培优B卷】附答案
- 2026年子痫急救处置理论笔试试题(附答案)
- 煤炭质量检测岗笔试真题参考答案
- 2026年中医耳鼻喉科虚火喉痹健康宣教试题及答案
- 2026年护士资格证考试妇产科题库(含答案)
- 2026年家庭安防系统的报警准确性与操作便捷性测试及答案
- 2026中国进出口银行招聘考试(专业知识)历年参考题库含答案详解
- 消防培训防盗、防火安全课件
- 事业编计算机岗2026全真模拟
- 艾灸疗法小讲课
- 超限站工作制度
- 不合格品管理培训课件
- DZ/T 0054-2014定向钻探技术规程
- 腹主动脉瘤的治疗与护理
- 应用型高校教学评价指标体系构建
- 城市高架桥防撞护栏安装方案
- 2024-2025学年人教版物理八年级上册 期中考试物理试卷
评论
0/150
提交评论