C语言小游戏——飞机大战

目录

引言

开发环境与工具准备

1. 开发环境配置

2. 资源文件准备

游戏设计与架构

1. 游戏核心数据结构

2. 游戏全局变量

游戏核心功能实现

1. 游戏初始化

2. 游戏主循环

3. 游戏渲染

4. 游戏状态更新

关键游戏机制实现

1. 敌机生成系统

2. 碰撞检测系统

3. 敌机销毁逻辑

4. 子弹销毁逻辑

游戏优化与扩展

1. 性能优化技巧

2. 游戏功能扩展

开发经验与学习建议

1. 开发经验总结

2. 学习建议

结语


引言

飞机大战是一款经典的射击游戏,玩家控制一架飞机在屏幕上移动并射击敌机。本文将详细介绍如何使用C语言和EasyX图形库开发一个完整的飞机大战游戏。这个项目不仅适合C语言初学者学习游戏开发的基本概念,也展示了如何将编程基础知识应用到实际项目中。

首先看一下运行效果:

开发环境与工具准备

1. 开发环境配置

开发飞机大战游戏需要以下环境配置:

  1. ​Visual Studio​​:推荐使用VS2019或更高版本,它提供了强大的代码编辑、调试和项目管理功能。
  2. ​EasyX图形库​​:这是一个简单易用的Windows图形编程库,封装了常用图形操作函数,特别适合初学者。
  3. ​Windows SDK​​:安装Visual Studio时需要勾选此项。

安装EasyX图形库非常简单,只需访问EasyX官网下载适配VS版本的安装包,然后按照向导完成安装即可。

2. 资源文件准备

游戏开发需要准备以下素材文件(网上图片随便一搜就有),存放在项目目录的res文件夹中:

  • plane.png - 玩家飞机图片(50x50像素)
  • enemy.png - 敌机图片(50x50像素)
  • bullet.png - 子弹图片(10x20像素)
  • bg.jpg - 游戏背景图片(600x700像素)
  • boom.wav - 爆炸音效文件

游戏设计与架构

1. 游戏核心数据结构

游戏主要使用以下数据结构:

typedef struct pos {
    int x;
    int y;
}POS; // 坐标类型

typedef struct plane {
    POS planePos; // 飞机坐标
    POS planeBullets[BULLET_NUM]; // 子弹数组
    int bulletLen; // 当前子弹数量
    int bulletSpeed; // 子弹移动速度
}PLANE; // 飞机类型

POS结构体用于表示游戏对象的二维坐标,PLANE结构体则包含了飞机的位置、子弹数组及相关属性。

2. 游戏全局变量

游戏使用以下全局变量管理状态:

PLANE myPlane; // 玩家飞机
PLANE enemyPlanes[ENEMY_NUM]; // 敌机数组
int enemyPlaneLen; // 当前敌机数量
time_t startTime, endTime; // 用于控制敌机生成时间
IMAGE img[3]; // 存储游戏图片资源
int score = 0; // 游戏得分

这些变量分别管理玩家飞机、敌机、时间控制和游戏得分等核心游戏状态。

游戏核心功能实现

1. 游戏初始化

initGame()函数负责初始化游戏状态:

void initGame() {
    initgraph(SCREEN_WIDTH, SCREEN_HEIGTH); // 初始化图形窗口
    score = 0; // 重置得分
    
    srand((unsigned)time(NULL)); // 初始化随机数种子
    
    // 初始化玩家飞机
    myPlane.bulletLen = 0;
    myPlane.bulletSpeed = 3;
    myPlane.planePos = {SCREEN_WIDTH/2 - PLANE_SIZE/2, SCREEN_HEIGTH - PLANE_SIZE};
    
    enemyPlaneLen = 0; // 重置敌机数量
    startTime = time(NULL); // 记录开始时间
}

该函数设置了游戏窗口、随机数种子、玩家飞机初始位置和游戏计时器等。

2. 游戏主循环

游戏采用经典的游戏循环结构:

while(1) {
    drawGame();    // 渲染游戏画面
    updateGame();  // 更新游戏状态
    Sleep(1000/60); // 控制帧率约60FPS
}

这个循环确保游戏以大约60帧每秒的速度运行,每次循环都先绘制画面再更新游戏状态。

3. 游戏渲染

drawGame()函数负责绘制游戏画面:

void drawGame() {
    BeginBatchDraw(); // 开始批量绘制
    
    // 绘制背景
    putimage(0, 0, &img[0]);
    
    // 绘制玩家飞机
    putimage(myPlane.planePos.x - PLANE_SIZE/2, 
             myPlane.planePos.y - PLANE_SIZE/2, 
             &img[2], SRCAND);
    
    // 绘制敌机
    for(int i = 0; i < enemyPlaneLen; i++) {
        putimage(enemyPlanes[i].planePos.x - PLANE_SIZE/2, 
                 enemyPlanes[i].planePos.y - PLANE_SIZE/2, 
                 &img[1], SRCAND);
    }
    
    // 绘制子弹
    for(int i = 0; i < myPlane.bulletLen; i++) {
        solidcircle(myPlane.planeBullets[i].x, 
                    myPlane.planeBullets[i].y, 
                    PLANE_SIZE/4);
    }
    
    // 绘制分数
    RECT rect = {0, PLANE_SIZE, SCREEN_WIDTH, SCREEN_HEIGTH};
    setbkmode(TRANSPARENT);
    char str[30] = {0};
    sprintf(str, "score:%d", score);
    drawtext(str, &rect, DT_TOP | DT_CENTER);
    
    EndBatchDraw(); // 结束批量绘制
}

该函数使用EasyX的批量绘制功能高效地绘制游戏画面,包括背景、玩家飞机、敌机、子弹和分数显示。

4. 游戏状态更新

updateGame()函数处理游戏逻辑更新:

void updateGame() {
    // 处理玩家输入
    if(GetAsyncKeyState('W') & 0x8000) myPlane.planePos.y -= 4;
    if(GetAsyncKeyState('S') & 0x8000) myPlane.planePos.y += 4;
    if(GetAsyncKeyState('A') & 0x8000) myPlane.planePos.x -= 4;
    if(GetAsyncKeyState('D') & 0x8000) myPlane.planePos.x += 4;
    
    // 发射子弹
    if(_kbhit()) {
        if(_getch() == ' ') {
            if(myPlane.bulletLen < BULLET_NUM) {
                PlaySound("img/bullet.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NOWAIT);
                myPlane.planeBullets[myPlane.bulletLen] = myPlane.planePos;
                myPlane.bulletLen++;
            }
        }
    }
    
    // 更新敌机位置
    for(int i = 0; i < enemyPlaneLen; i++) {
        enemyPlanes[i].planePos.y += 2;
    }
    
    // 更新子弹位置
    for(int i = 0; i < myPlane.bulletLen; i++) {
        myPlane.planeBullets[i].y -= myPlane.bulletSpeed;
    }
    
    // 调用其他更新函数
    initEnemyPlane();
    destroyEnemyPlane();
    destroyBullet();
}

该函数处理玩家输入、更新游戏对象位置,并调用其他辅助函数完成敌机生成和碰撞检测等逻辑。

关键游戏机制实现

1. 敌机生成系统

initEnemyPlane()函数控制敌机的生成:

void initEnemyPlane() {
    endTime = time(NULL);
    double elapsedTime = difftime(endTime, startTime);
    
    if(elapsedTime >= ENEMY_SPEED) {
        if(enemyPlaneLen < ENEMY_NUM) {
            int x = (rand() % (SCREEN_WIDTH - 2*PLANE_SIZE)) + PLANE_SIZE;
            int y = -PLANE_SIZE;
            
            enemyPlanes[enemyPlaneLen].planePos.x = x;
            enemyPlanes[enemyPlaneLen].planePos.y = y;
            
            enemyPlaneLen++;
        }
        startTime = endTime;
    }
}

该函数每隔ENEMY_SPEED秒在屏幕顶部随机位置生成一架新敌机,确保敌机数量不超过最大限制。

2. 碰撞检测系统

游戏使用简单的矩形碰撞检测:

int areInierSecting(POS c1, POS c2, int radius) {
    return abs(c1.x - c2.x) <= radius && abs(c1.y - c2.y) <= radius;
}

该函数检查两个坐标点是否在指定半径范围内相交,用于检测子弹与敌机、玩家与敌机的碰撞。

3. 敌机销毁逻辑

destroyEnemyPlane()处理敌机销毁:

void destroyEnemyPlane() {
    for(int i = 0; i < enemyPlaneLen; i++) {
        // 检测玩家与敌机碰撞
        if(areInierSecting(myPlane.planePos, enemyPlanes[i].planePos, PLANE_SIZE)) {
            if(IDYES == MessageBox(GetHWnd(), "游戏结束,是否重新开始?", "提示", MB_YESNO)) {
                initGame();
            } else {
                exit(0);
            }
        }
        
        // 敌机飞出屏幕
        if(enemyPlanes[i].planePos.y > SCREEN_HEIGTH) {
            for(int j = i; j < enemyPlaneLen; j++) {
                enemyPlanes[j] = enemyPlanes[j+1];
            }
            enemyPlaneLen--;
            i--;
        }
    }
}

该函数检测玩家与敌机的碰撞(游戏结束)以及敌机飞出屏幕的情况(移除敌机)。

4. 子弹销毁逻辑

destroyBullet()处理子弹销毁:

void destroyBullet() {
    for(int i = 0; i < myPlane.bulletLen; i++) {
        // 子弹与敌机碰撞
        for(int j = 0; j < enemyPlaneLen; j++) {
            if(areInierSecting(myPlane.planeBullets[i], enemyPlanes[j].planePos, 
                              PLANE_SIZE/4 + PLANE_SIZE/2)) {
                // 移除子弹和敌机
                for(int x = i; x < myPlane.bulletLen; x++) {
                    myPlane.planeBullets[x] = myPlane.planeBullets[x+1];
                }
                for(int x = j; x < enemyPlaneLen; x++) {
                    enemyPlanes[x] = enemyPlanes[x+1];
                }
                enemyPlaneLen--;
                myPlane.bulletLen--;
                j--;
                score += 100;
                break;
            }
        }
        
        // 子弹飞出屏幕
        if(myPlane.planeBullets[i].y < 0) {
            for(int x = i; x < myPlane.bulletLen; x++) {
                myPlane.planeBullets[x] = myPlane.planeBullets[x+1];
            }
            myPlane.bulletLen--;
            i--;
        }
    }
}

该函数处理子弹与敌机的碰撞(得分并移除双方)以及子弹飞出屏幕的情况(移除子弹)。

游戏优化与扩展

1. 性能优化技巧

  1. ​批量绘制​​:使用BeginBatchDraw()EndBatchDraw()减少屏幕刷新次数。
  2. ​帧率控制​​:通过Sleep(1000/60)将游戏帧率控制在约60FPS。
  3. ​对象池技术​​:预分配游戏对象(如子弹、敌机)避免频繁内存分配。

2. 游戏功能扩展

这个基础版本可以进一步扩展:

  1. ​多种敌机类型​​:添加不同大小、生命值和移动模式的敌机。
  2. ​关卡系统​​:设计多个关卡,逐渐增加难度。
  3. ​道具系统​​:实现火力增强、生命恢复等道具。
  4. ​背景音乐​​:添加游戏背景音乐和更多音效。
  5. ​高分记录​​:保存玩家最高分数。
  6. ​难度递增​​:随着游戏进行逐渐提高敌机速度和生成频率。

开发经验与学习建议

1. 开发经验总结

  1. ​模块化设计​​:将游戏功能划分为初始化、渲染、更新等模块,便于维护。
  2. ​逐步实现​​:先实现核心功能(移动、射击),再添加额外特性。
  3. ​调试技巧​​:使用Visual Studio的调试工具检查游戏状态和变量值。

2. 学习建议

  1. ​掌握C语言基础​​:熟悉指针、结构体、数组等核心概念。
  2. ​学习EasyX图形库​​:从简单图形绘制开始,逐步学习动画和交互。
  3. ​分析开源项目​​:研究其他游戏项目的代码结构和设计思路。
  4. ​动手实践​​:通过修改和扩展现有项目来巩固学习成果。

结语

通过这个飞机大战游戏项目,我们展示了如何使用C语言和EasyX图形库开发一个完整的游戏。从游戏设计、数据结构到核心功能实现,这个项目涵盖了游戏开发的关键概念和技术。

这个项目不仅适合C语言学习者巩固基础知识,也为有志于游戏开发的初学者提供了一个良好的起点。通过扩展和完善这个基础框架,你可以进一步探索游戏开发的更多可能性。

希望本文能帮助你理解游戏开发的基本原理,并激发你创造更多有趣项目的热情!完整的源代码已在文中展示,你可以直接运行或基于此进行二次开发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值