1. 引言
C语言是大学理工科专业的必修课,是编程的入门课程,为学生的编程实践和计算机理论打好基础。在编程语言教学中,项目化教学的重要性越来越被认识到,但是由于C语言是偏底层语言,C语言开发环境不像开发Java程序那样自带图形、界面等一些库,因此用C语言编写应用并不非常方便,这就为在C语言教学中开展项目化教学带来了困难[1]。本文所用案例飞机大战游戏常用于学生课设、实验等实践环节,面向对象通常是计算机或计算机相近专业的高年级学生。对于低年级学生或初学者来说,利用编程实现这类游戏存在着明显的困难[2]。而EasyX是针对C/C++的图形库,可以帮助使用C/C++语言的初学者快速上手图形和游戏编程,而不需要注册窗口类、建消息循环等。对于学生来说,C语言课程可以帮助他们步入编程殿堂的大门,C语言的课程教学工作因此变得至关重要,从传统的理论教学到近年来的项目式教学,C语言的教学在不断地进行改革和创新,项目化教学是通过实施一个完整的项目而进行的教学活动,旨在把学生融入有意义的任务完成的过程中,充分发掘学生的创造潜能,提高学生解决实际问题的综合能力[3]。因此,本文以基于EasyX图形库设计的飞机大战游戏作为项目化教学的案例,以代码模块化的方式开展教学,便可以在C语言项目化教学的过程中带来显著的效果。
在C语言的项目化教学中,建构主义学习理论作为一种重要的教育理论,也能为其教学的开展提供有力的支撑。它强调学习者在原有知识经验的基础上,通过与环境的相互作用,主动建构新的知识意义。对于C语言课程的发展而言,这一理论在课程教学的改革创新中具有深远的指导意义。建构主义学习理论强调学习者在学习过程中的主动性和建构性。它认为,学习不是被动地接受知识,而是学习者在与环境互动中主动建构知识意义的过程,因此,教学过程既要教师的指导,也要学生充分发挥主观能动性,这不仅有助于提升学生的学习效果和学习体验,也有助于培养出更多具有实践能力和创新思维的高素质人才,推动教学质量的全面提升。同时,在游戏项目的驱动下,再加以这种评价方式也有助于更好激发学生的学习兴趣和动力,培养他们的自主学习能力和终身学习的习惯,为他们的未来发展奠定坚实的基础[4]。
2. EasyX图形库
EasyX是一个可以用C、C++语言进行编程的免费绘图库。它能够使开发者在这两种主流编程语言环境下,轻松实现丰富多样的图形绘制与游戏开发功能,极大地拓展了C/C++语言在可视化编程领域的应用边界,满足了从基础图形绘制到复杂游戏逻辑实现等不同层次的编程需求,为编程者提供了一个便捷高效的图形编程工具。在兼容性方面,EasyX表现出色,其支持范围涵盖了从VC6.0到VC2022的众多Visual C++版本。EasyX图形库只需简单掌握绘图函数及基本概念即可开发,低门槛特性推动其在编程爱好者和开发者中普及,吸引更多初学者的学习使用。简言之,EasyX有如下特点:
1) EasyX安装简单,对包含VC6.0之后的VC各版本都支持。
2) EasyX使用简单,利用简单的绘图函数和如颜色、坐标几个绘图相关的基本概念就能编写图形和游戏程序[5]。
本文设计的飞机大战游戏项目需要用到的EasyX功能,涉及的内容主要有:窗口建立,直线、矩形的绘制,鼠标键盘的输入,屏幕内容清空等功能。教学案例飞机大战使用EasyX库下的鼠标键盘参数读取的函数,以及背景音乐播放的函数,给学生提供内容更加丰富的项目学习和人机交互的体验,学生在使用这些功能函数的同时,对EasyX库也有更加深入的理解。通过EasyX图形库设计趣味游戏项目,让项目贯穿在教学过程中,引导学生自主学习,自主分析,让学生可以得到直接的反馈,形成较强的满足感和成就感,也可以进一步提升学生的学习兴趣,增强学生的主观能动性[6]。EasyX图形库主要提供了用于绘制图形的常用函数库及相应的头文件。使用EasyX非常简单,只要下载文件夹,将其lib文件夹和include文件夹内容分别拷贝到Visual C++的include和lib文件中即可[7]。
3. 项目化教学模式
3.1. 项目化教学模式的定义及特点
项目化教学作为课程改革的有效方式,能将书本上的理论知识通过项目化的教学形式带给学生,通过引导学生主动参与项目,实现“教、学、做”的有机结合,学生在项目教学过程中学习相关知识点的同时,也能在潜移默化中培养项目思维,教师也无需按照传统章节的顺序来讲解,即“项目用到什么知识点,教师就讲什么知识点”。知识点的学习具有跳跃性,学生作为主体将理论与实际相结合,在教师的辅助下学生自主完成项目,然后教师针对学生所做的项目进行总结并给出适当建议,找出更优解决问题的路径。项目化教学中,教师布置项目作为学生的一种挑战,在传道解惑时以模块化的呈现形式为主。这种模式下,充分激发学生们对项目的学习兴趣与对知识点的渴望,同时学生自身只有真正掌握了知识点才能顺利完成项目,这样既能充分激发学生的主观能动性,也能达到教学开展的初衷[8]。
3.2. 项目化教学模式的流程
教师课前精心挑选项目,确保项目既围绕知识点又符合培养方案,设计项目时由浅入深、由易到难,循序渐进。由于一下子将项目的整个内容呈现在学生面前可能加大学生的接受难度,因此可以将项目细化成各个功能模块。为了提高学生模块化的编程思路,在学生学完基础的变量初始化、输出等知识点后,可以把函数的知识点放到教学开展的初期来进行学习,在整个项目的设计和编写初期就引入函数,将项目的功能模块用函数来进行分隔,这样就能让学生在接触编程的初期时就建立模块化的编程思维和方法,为以后编程的学习和应用打下良好的基础。之后,随着教学的深入开展,再慢慢将难度更大以及知识点更难的模块呈现,打造适合学生学习“坡度”的项目。
最后,对学生学习效果进行检验,让学生在先前课程的基础上,适当加入自己的想法,将学习的知识点消化吸收,转化为成果进行讲解展示,教师则在适当时机给予肯定与建议。在这样的项目化教学中,学生也能做到弄懂、悟透,对自己动手操作而获得的劳动成果,也会倍加珍惜。本文以飞机大战项目为案例,涵盖了C语言的主要语法,包括选择、循环、数组、函数、指针、结构体等,满足了教学培养方案的要求,上手难度也非常合适,此外,游戏案例也是吸引学生兴趣的重要一环,学生的积极性也会更高,故以飞机大战游戏作为项目化教学的案例。
4. 项目设计
4.1. 软件工具和使用方法
使用Visual Studio 2022作为游戏开发主要软件,使用PhotoShop软件创建游戏背景、玩家战机、敌机、子弹等图片,并将这些图片导出为.png格式文件,并拖入素材文件PlayPlane的image文件中;将.mp3格式的音效素材拖至素材文件PlayPlane的Music文件中,使用Visual Studio 2022编辑C#脚本语言,实现背景音乐的播放控制。
4.2. 游戏的模块设计
教学所用的项目是通过使用C语言编写的一款名为飞机大战的游戏,该游戏功能设计分为3个主要模块。
(1) 初始化模块:此模块扮演着游戏启动前准备工作的核心角色。它涵盖了多个关键步骤,首先是游戏界面的窗口创建,这一过程负责在屏幕上绘制出游戏的初始界面框架,为后续的游戏元素提供展示的舞台。紧接着是游戏数据初始化,这一步确保所有游戏相关的变量、状态及配置被正确设置至初始状态,为游戏逻辑的顺利运行奠定基础。此外,图片的加载与输出也是不可或缺的一环,它负责将游戏所需的图像资源(如背景、角色、道具等)加载到内存中,并在游戏界面上适时地渲染出来,为玩家呈现出生动丰富的视觉体验。
(2) 游戏控制模块:作为游戏交互性的核心,该模块集成了多种控制逻辑。其中,飞机控制判断负责根据玩家的输入(如键盘操作)来动态调整游戏内飞机的飞行状态,如速度、方向等。按键控制则是对玩家按键行为的直接响应,将物理按键的按下、释放等事件转化为游戏内的操作指令。碰撞判断是保障游戏逻辑准确性的重要环节,它实时检测游戏元素(如飞机与敌机、障碍物等)之间的位置关系,一旦判定为碰撞,则触发相应的游戏事件(如扣分、爆炸等)。此外,鼠标按键判断虽然在一些飞行射击游戏中可能不是主要控制手段,但对于支持鼠标操作的版本而言,它同样重要,能够增加游戏的多样性和灵活性。
(3) 统计模块:此模块专注于游戏成绩的记录与展示,是玩家游戏体验反馈的重要组成部分。它主要负责统计玩家的分数和HP值(生命值或健康值),得分数通常反映了玩家在游戏中的表现优劣,如击败敌人的数量、完成任务的质量等;而HP值则直接关联到玩家的生存状态,一旦降至零,则游戏结束。统计模块通过实时更新并显示这些关键数据,让玩家能够清晰地了解自己的游戏进度和表现,从而激发其挑战更高分数的欲望,增强游戏的吸引力和可玩性。
飞机大战游戏功能如图1所示。
Figure 1. Functional module diagram of the Plane War game
图1. 飞机大战游戏功能模块图
4.3. 游戏逻辑设计
玩家按下开始游戏按钮进入游戏界面后,玩家通过通过按住键盘“W”、“A”、“S”、“D”键来控制战机,敌方战机将从游戏界面上边沿随机生成并开始移动,与此同时,玩家可以控制进行移动,以击败场景中的敌方战机,每击中一架敌机+1分。在游戏过程中,增加血量道具有概率掉落,拾取后可以获得生命值+1效果。游戏默认为无尽模式,一旦玩家子弹与敌机发生碰撞时,玩家扣除1点血量,当血量扣除殆尽后失败,将导致玩家被击杀,同时弹出游戏失败界面以宣布游戏结束。通过可视化游戏逻辑的整体构成,在提升教学质量的同时,也提高了教学的效率,游戏逻辑的整体构成如图2所示。
Figure 2. Game logic composition
图2. 游戏逻辑组成
4.4. 游戏互动设计
Figure 3. Game scene
图3. 游戏场景
在Visual Studio 2022将游戏场景搭建好后(见图3),开始对游戏的交互性进行设计。本游戏的交互设计流程为:首先,利用Visual Studio 2022中的EasyX插件来设计开始界面,用鼠标点击开始游戏按钮玩家即可开始游戏。进入游戏后,通过按住键盘“W”、“A”、“S”、“D”键控制主角飞机的上下左右运动,让子弹尽可能多地击中敌机[3]。玩家飞机发射的子弹碰撞到敌方飞机,敌方飞机会消失,左上方的Sorce值增加1;反之,如果敌方飞机碰撞到玩家飞机,左上方的“玩家HP值”减少1。在游戏过程中,玩家战机也可以尽可能地去触碰道具,每触碰一个道具,“玩家HP值”增加1,同时在左上角也会实时显示HP值,及时给玩家反馈生命值。在游戏设计过程中,还添加了长按空格键实现游戏暂停功能,在一定程度上可以帮助玩家减轻游戏难度。此设计过程主要是EasyX图形库的主要核心技术设计,也是教学的重要部分之一。
5. 项目化教学的实现
5.1. 准备图片素材
为了使教学中学生能更加投入地学习,为每一架飞机、道具、子弹以及背景图等这些游戏图片精心准备2张图,分别表示选中和未选中的两种状态。使用掩码图“plane3.png”和彩绘图“plane4.png”通过代码进行堆叠,采用透明贴图技术,最后将主体抠出来显示在窗口上,如图4所示。
Figure 4. Preparing image materials
图4. 准备图片素材
5.2. 创建窗口
根据背景图尺寸绘制默认窗口,以游戏界面的左上角(0, 0)为原点,X轴水平方向向右,逐渐增加,Y轴竖直方向向下,逐渐增加。设计过程考虑玩家的游戏体验,最大化地绘制窗口。基于这些前期准备,方便了后续教学中对代码的深入讲解,同时也能实现从简单到难度层层递进式的模块化教学,界面设计原理如图5所示。
Figure 5. Game window design diagram
图5. 游戏窗口设计图
5.3. 算法设计
飞机大战游戏算法设计基于EasyX库进行开发,EasyX库是基于Windows的图形编程,将Windows下的复杂程序过程进行封装,还将Windows下的编程过程隐藏,给用户提供一个简单熟悉的接口,设计过程中对于图形库中函数的调用,最终都会由Windows的底层API实现。基于此算法设计,能让学生从飞机大战游戏这个项目中学到更多C语言的算法知识,提高编程能力,游戏算法设计流程如图6所示。
Figure 6. Algorithm design process of Plane War game
图6. 飞机大战游戏算法设计流程
该项目基于EasyX图形库展开,同时扩展Windows API的底层函数,涉及的C语言知识包括了变量声明、循环、条件判断、函数设计、二维数组、指针传参等,该项目根据教学进度可分解为六个部分,分别为:
(1) 初始化模块:初始化窗口、控制台、飞机和子弹等数据,载入图片素材,向多媒体接口发送字符串播放背景音乐,实现背景音乐循环播放,这个模块主要用到重定义、结构体、循环以及EasyX图形库播放音乐和加载图片的函数调用,提供一个完整的代码框架,同时也初步地引入EasyX图形库这一概念,主要代码如下:
#define WIN_WIDTH 1055//窗口宽度
#define WIN_HEIGHT 1467//窗口高度
#define num 15//玩家最大子弹数量
#define Num 15//敌机最大数量
struct Image{ //easyx贴图
IMAGE begin;//游戏开始界面
IMAGE bakcGround;//背景图片
IMAGE player[2];//玩家飞机图片,原图和掩码图
IMAGE bullet[2];//子弹图片
IMAGE enemy[2];//敌机1图片
IMAGE boss[2];//敌机2图片
IMAGE daoju[2];//加血道具掉落
IMAGE miss[2];//透明图片,敌机消失
IMAGE end;//结束画面
}image;
srand(GetTickCount());//设置随机数种子
t1 = t2 = t3=t3=GetTickCount();//获取系统开机到当前所经过的毫秒
mciSendString("open ./image/game_music.mp3 alias BGM",0,0,0);//向多媒体设备接口发送字符串
mciSendString("play BGM repeat", 0, 0, 0); //播放音乐
loadimage(&image.bakcGround,"./image/ok.jpg");//把图片加载并保存
loadimage(&image.player[0], "./image/plane3.png"); //加载玩家飞机图片
loadimage(&image.player[1], "./image/plane4.png");
player.x =WIN_WIDTH/2-60; //初始化玩家数据
player.y =WIN_HEIGHT-830;
player.flag = true;//玩家飞机状态
player.HP = 1;//初始生命值
for (int i = 0; i < num; i++) //初始化子弹数据
{
bullet[i].flag = false;
bullet1[i].flag = false;
}
(2) 移动与控制模块:设置键盘“W”、“A”、“S”、“D”键控制主角飞机的上下左右运动,让子弹尽可能多地击中敌机,同时躲避敌方飞机,同时设置敌机、子弹和道具的随机掉落和移动。游戏设定为无尽模式,只要生命值没有耗尽,玩家便需要一直控制战机规避敌机并尽可能多地拾取道具,此模块主要是人机交互功能的代码,运用函数、条件判断和EasyX图形库按键状态读取函数,此模块主要代码如下:
void GameControl(int speed){ //飞机控制
if (GetAsyncKeyState(0x57)&&player.y>=0){ // W
player.y -= speed;
}
if (GetAsyncKeyState(0x53)&&player.y<=800){ // S
player.y += speed;
}
if (GetAsyncKeyState(0x41)&&player.x>=-53){ // A
player.x -= speed;
}
if (GetAsyncKeyState(0x44)&&player.x<990){ // S
player.x += speed;
}
}
void EnemyMove(int speed){ //敌机移动
for (int i = 0; i < Num; i++){
if (enemy[i].flag){
enemy[i].y += speed;
if (enemy[i].y >= WIN_HEIGHT){
enemy[i].flag = false;
}
}
}
(3) 碰撞判断模块:在游戏的核心逻辑中,碰撞判断扮演着至关重要的角色。它不仅限于子弹与敌机之间的交互,还涵盖了敌机与玩家战机之间可能发生的任何碰撞事件。通过精确的物理坐标对比或碰撞检测算法,系统能够即时判断这些元素是否发生了接触。一旦检测到碰撞,系统将自动触发相应的响应机制,比如使敌机或特定道具从游戏场景中消失,同时也可以添加爆炸动画、音效等效果,以增强游戏的沉浸感和动态反馈,模块所涉及的知识点则是对前面C语言基础知识的整合,将函数、循环和条件判断巧妙运用在碰撞检测环节,给后面进阶知识点做铺垫,主要代码如下:
void playertouch(){ //玩家飞机与敌机碰撞判断
for (int i = 0; i < 2; i++){
if (player.x + 90 > enemy[i].x + 30 && player.x+30<enemy[i].x + 90
&& player.y +10<enemy[i].y + 80 && player.y + 80 >enemy[i].y + 10){
if (enemy[i].flag){
player.HP--;//碰撞敌机,血量-1
enemy[i].flag = false;
break;
}
}
}
}
(4) 获取按键和鼠标数据模块:游戏界面与玩家之间的交互,主要依赖于鼠标和键盘信号的输入,需要精准捕捉按键按下与释放的状态,并进行高效处理。玩家通过鼠标点击屏幕上的“开始游戏”按钮来启动游戏,或通过键盘控制战机的移动方向这一流程机制,确保了游戏操作的流畅性和响应速度,为玩家提供了直观且易于上手的控制体验,这一模块是EasyX图形库的函数调用,以项目实例来将指针这一知识难点引入,以项目驱动方式开展教学,更有助于学生理解和掌握,主要代码如下:
GetAsyncKeyState(0x57);//获取按键“W”键
GetAsyncKeyState(0x53);//获取按键“S”键
//鼠标按键获取与判断
ExMessage msg;//参数是消息类型
if (peekmessage(&msg)){//如果消息有值则保持在peekmessage函数中,即为真
if (msg.message == WM_LBUTTONDOWN){
if (msg.message > 507 && msg.x < 947 && msg.y>593 && msg.y < 752){//定义坐标判断点击是否为开始
}
(5) 数值实时统计模块:为了让玩家能够清晰地了解自己的游戏状态和进展,系统需要实时记录并显示玩家的Score值和HP值。得分系统根据玩家的游戏表现(如击败敌机、收集道具等)动态更新,而HP值则反映了玩家的生存状态。这些信息不仅在游戏过程中以直观的界面元素(如数字、进度条等)持续展示给玩家,还在游戏结束时以最终得分的形式出现在游戏结束界面上,为玩家提供全面的游戏反馈。这种实时统计与展示机制,极大地提升了游戏的可玩性和挑战性,激发了玩家的竞争意识和成就感。模块调用Windows API的函数,将字体显示在背景上且不影响背景色,此模块目的是扩展学生知识面,为学生展示了不同函数库的调用,也为学生后续自主开展项目做铺垫,主要代码如下:
void showmessage(){ //显示得分和血量信息
//得分显示
char Grade[100] = { 0 };
sprintf(Grade, "%d", grade.Count);
setbkmode(0);//设置背景透明
settextcolor(YELLOW);
settextstyle(50, 0, "宋体");//字体大小,紧凑程度,字体样式
outtextxy(10, 20, "Sorce:");
outtextxy(150, 20, Grade);
//血量显示
char heart[100] = { 0 };
sprintf(heart, "%d", player.HP);
setbkmode(0);//设置背景透明
settextcolor(RED);
settextstyle(50, 0, "宋体");
outtextxy(10, 70, "HP:");
outtextxy(90, 70, heart);
}
(6) 是否失败结算模块:作为游戏流程的重要一环,失败判定机制确保了游戏的公平性和完整性。当玩家的HP值降至零时,即判定为游戏失败。此时,系统将自动暂停游戏进程,并跳转到游戏结束界面。在这个界面上,除了显示玩家的最终得分外,还可以添加包含一些鼓励性的话语或重玩选项,以引导玩家继续挑战或回顾自己的游戏表现。此模块则是界面切换的代码模块,作为对EasyX图形库函数的巩固强化,主要代码如下:
void showend(){ //结束画面
loadimage(&image.end, "./image/end4.jpg");//加载结束页面
putimage(0,0, &image.end);//输出结束画面
//最终得分显示
char Grade[100] = { 0 };
sprintf(Grade, "%d", grade.Count);
setbkmode(0);//设置背景透明
settextcolor(YELLOW);
settextstyle(50, 0, "宋体");//字体大小,紧凑程度,字体样式
outtextxy(350, 400, "最终分数:");
outtextxy(580, 400, Grade);
}
5.4. 项目实施的感悟
项目化教学案例中,五子棋游戏、俄罗斯方块游戏等均可作为有效的教学工具。在设计这些项目时,我个人深感在选择案例及其实施方案上,往往会顾虑重重,担心项目出错或不够完善。然而,在项目设计的初期阶段,或许我们不必太过拘泥于细节和高级性。逻辑清晰、实现简便的项目更能有效引导学生入门,因为首要任务是激发学生的学习兴趣,引导学生积极主动地去汲取知识,这也是选择飞机大战这一基于EasyX图形库设计开发的项目作为教学案例的初衷之一。当学生掌握基础知识后,他们自然有能力在后续学习中添加自己感兴趣的复杂功能。例如,在“飞机大战”项目中,教师或许希望游戏能实现像现实一样的爆炸画面,但这会增加实现的难度。为了让学生更容易上手,我们不妨适当简化项目,以轻松且充满乐趣贯穿整个教学过程,这样的项目化教学才会有抛砖引玉的效果。
6. 结束语
本文以飞机大战游戏为案例,利用EasyX图形库完成了游戏的制作,包括窗口创建、图片加载、游戏控制和统计等,操作简单,反应速度快,既提高了学生的动手能力以及对编程的兴趣,也很好地打磨了自身的C语言基础。此外,EasyX图形库不仅在项目化教学中具有实用价值,而且它能够巧妙地融入到C语言各章节的教学中,同时也能推进建构主义学习理论,增强学生的主观能动性。通过编写小程序,教师可以直观地展示C语言语法在不同章节的具体应用,从而增强学生的学习体验。更进一步,当教学过渡到C++语言时,EasyX同样能够发挥效用,允许学生利用面向对象的方式进行编程实践。综上所述,EasyX图形库在C/C++的教学中扮演着积极角色,不仅提升了教学的丰富性,还具有较高的性价比,是一个值得推荐的教学辅助工具。
NOTES
*通讯作者。