C语言课程设计实战:基于Funcode的大鱼吃小鱼游戏开发全解析
发布时间:2026/9/8 8:10:14 锦皓数字建站

简介这是基于C/C语言实现的大鱼吃小鱼游戏完整源代码常用于高校编程课程设计funcode课设特别适合刚学完C/C基础语法的初学者作为综合性练手项目。代码涵盖了游戏主循环、碰撞检测、键盘事件响应、鱼群对象管理结构体数组动态内存等核心模块可帮助理解面向过程编程中模块化设计和数据结构应用。资源包为rar格式大小3.22MB。该资源目前已有2871人学习下载侧面印证了代码的完整性与可靠性。通过学习这份源码读者可以快速掌握SDL或ncurses图形库的简单集成方式、二维坐标碰撞判定逻辑以及如何在数组中动态增删游戏对象从而独立完成一个可游玩的图形化小游戏为后续图形学或游戏开发方向打下良好基础。 游戏课设这东西选对题目就已经成功一半。我当时做“大鱼吃小鱼”这个 C 语言课程设计时目标很明确既要能完整跑出个像样的游戏又不想把时间全耗在底层图形和平台适配的泥潭里。最终选定了 Funcode 平台底子里还是个经典到不行的老游戏——玩家控制一条小鱼在水里游吃比自己小的鱼来变大躲开比自己大的鱼画面简单但逻辑链条完整。这篇文章把我从零到一做完这个课设的全过程、代码思路和踩坑记录都摊开讲适合正在做 C 语言课程设计或者刚接触游戏逻辑开发的朋友直接抄作业。1. 项目整体设计与游戏机制拆解1.1 游戏规则与核心玩法先把需求定清楚不然写代码容易变成无头苍蝇。我这版的规则设计如下玩家通过键盘方向键控制一条鱼在固定尺寸的池塘场景里移动场景中会持续刷新大小不一的 AI 鱼AI 鱼的行为有两种目标小于自己时就追击目标大于自己时就逃跑玩家鱼吃到比自己小的鱼会获得分数并且体型缓慢增大玩家鱼碰到比自己大的鱼游戏结束随着分数提高AI 鱼的密度和种类逐步提升难度动态增加这套规则听上去简单但要落成代码涉及的模块可不少鱼的坐标移动、随机生成、碰撞检测、分数判断、视觉反馈、结束条件……每一个都是一道独立的编程小题。反而对课设来说这种麻雀虽小五脏俱全的项目最容易体现编程基本功。1.2 为什么选了 Funcode 平台在开始动手前我其实纠结过几个方案直接上 Win32 API、用 EasyX、或者用 pygame。后来发现课程要求是 C/C 语言并且学院统一推荐 Funcode 作为课设平台所以我最终把精力集中在 Funcode 上。Funcode 是一个面向教学的 2D 游戏开发框架它对 C 语言的支持很直接。它帮你把底层窗口创建、图片加载、键盘输入这些繁琐的事情都封装掉了你只需要调用函数去加载图片、画图、读按键就能快速搭建一个可视化程序。换句话说它把实现一个游戏的成本降低到实现游戏逻辑这一步特别适合课程设计这种时间紧、任务重的场景。优点不依赖复杂的图形编程基础、API 好上手、运行效果直观。缺点也明显国内相关资料相比主流的 EasyX 少很多遇到冷门问题基本只能自己翻文档。所以网上的现成代码虽然多但多数模糊且缺少注释真要自己调试还是会踩不少坑。1.3 技术选型的核心思路在真正写代码前我从技术角度做了一次拆解把整个项目分成四层数据层鱼的位置、速度、大小、存活状态、分数等逻辑层移动、碰撞检测、吃与被吃的判定、新鱼生成表现层绘制背景、画鱼、写分数、游戏结束提示控制层键盘输入读取、游戏主循环这四层不用刻意做复杂的 MVC 架构但脑子里有分层意识写出来的代码会顺手很多。比如逻辑层不碰任何绘图 API表现层不写任何移动计算后期调试的时候每类问题都有明确查找方向。2. 核心数据结构与算法细节2.1 鱼的属性建模鱼是游戏中最核心的对象我一开始直接用零散的变量硬写比如 fish1x、fish1y、fish1size……后果是代码爆炸且没法扩展。后来老老实实定义一个结构体把鱼的属性都包进去typedef struct { float x, y; // 当前坐标 float vx, vy; // 速度分量 int size; // 鱼的大小用半径近似 int alive; // 1存活 0死亡 int colorId; // 用于区分不同外观 } Fish;结构体建模的好处很直白当你需要创建 20 条鱼时只用一个数组就能管理遍历更新也用同一套代码。鱼的大小用半径近似是因为后续碰撞检测可以简化为圆心距离判断计算量和复杂度都大幅下降。我这里用颜色来区分鱼的类型实际项目中完全可以换成不同图片。2.2 鱼群管理与动态生成鱼群我用一个固定大小的数组来管理设定最大鱼数量 30。为什么不用链表因为课设场景内鱼的数量完全可控固定数组就够用而且遍历时可以通过下标直接访问效率高、代码简单。链表还要考虑节点申请释放课设阶段没必要增加这种复杂度。生成新鱼的逻辑放在主循环中定时执行。为了避免所有鱼滋滋地突然出现在同一个角落我加入了简单的位置约束和间距判断void generate_fish(PlayScene* scene) { Fish newFish; newFish.alive 1; newFish.size rand() % 40 20; // 半径 20~59 newFish.x rand() % (SCENE_WIDTH - 80) 40; newFish.y rand() % (SCENE_HEIGHT - 80) 40; newFish.vx (rand() % 100 - 50) / 100.0f; newFish.vy (rand() % 100 - 50) / 100.0f; if (abs(newFish.x - player.x) 120 abs(newFish.y - player.y) 120) { scene-fish[scene-count] newFish; } }生成时做一次距离判断避免新鱼直接怼在玩家脸上这个细节当年没做导致好几次出现玩家刚开局就被突然刷出的鱼撞死的局面体验非常糟糕。2.3 碰撞检测与吃鱼判定逻辑大鱼吃小鱼的核心逻辑本质是两个圆是否相交的问题。两个鱼的中心距离小于两者半径之和时就会碰撞碰撞后再比较大小决定谁吃谁float dx fish1.x - fish2.x; float dy fish1.y - fish2.y; float dist2 dx * dx dy * dy; float rsum fish1.size fish2.size; if (dist2 rsum * rsum) { if (fish1.size fish2.size) { fish2.alive 0; // 小鱼死亡 fish1.size 2; // 大鱼变大 score 10; } else if (fish1.size fish2.size) { // 玩家失败逻辑 } }注意这里我用的是距离的平方做比较而不是先开根号再比较。因为开根号涉及浮点运算性能虽然不至于有多大影响但这是一个很好的编码习惯——能用平方比较就避免多余的 sqrt 调用尤其是场景中鱼数量多、每一帧都要做两两判断时效益更明显。3. 实操过程与核心功能实现3.1 Funcode 环境搭建与工程创建Funcode 的安装包不大但从下载到能跑起第一个程序还是有点门槛的。我的建议是先去官网装最新版然后跟着自带示例跑一遍。安装完成后打开工程模板你会发现程序框架已经搭好了里面有几个固定函数资源加载、主循环、键盘处理等。不要一上来就把这些都删掉先在原模板基础上改动更稳妥。创建工程时我遇到过编译器版本不匹配的问题。Funcode 不同版本默认的编译器路径可能不同如果你IDE里一直提示找不到编译器去工具-选项里把编译器路径重新指定到安装目录下的编译器即可。类似的坑我后面放在问题清单里详细说。3.2 主循环、移动与键盘控制游戏主循环是游戏的心跳。每帧都做这几件事读取键盘状态、更新所有鱼的位置、做碰撞检测、重绘画面。主循环的逻辑我是这样组织的while (funcode_is_running()) { handle_player_input(); // 读取方向键更新玩家鱼速度 update_fish_positions(); // 所有鱼的位置 速度 check_collisions(); // 两两碰撞检测与处理 generate_new_fish(); // 间隔时间到达则补充新鱼 draw_scene(); // 绘制背景、鱼群、进度 }这里最需要注意的其实是更新频率的稳定性。Funcode 框架自带 Delay 机制但不建议直接使用死循环Deply 方式控制帧率那样会导致程序在不同电脑上速度差异很大。我后来采用固定时间步的思路记录上一帧的时间如果两次刷新之间计时器差值超过设定阈值比如 30ms就做一次逻辑更新否则跳过逻辑只做界面重绘既避免卡顿也保证游戏速度稳定。键盘控制上Funcode 提供按键检测函数按下方向键改变玩家鱼的速度分量。为了手感好一点我给玩家鱼设置了最大速度值并且让方向键控制的是目标速度而不是绝对速度这样松开按键后鱼不会瞬间停下来有一点惯性手感更自然。void handle_player_input() { if (key_down(KEY_UP)) player.vy -PLAYER_SPEED; if (key_down(KEY_DOWN)) player.vy PLAYER_SPEED; if (key_down(KEY_LEFT)) player.vx -PLAYER_SPEED; if (key_down(KEY_RIGHT)) player.vx PLAYER_SPEED; player.x player.vx; player.y player.vy; // 限制玩家鱼不越出边界 if (player.x player.size) player.x player.size; if (player.y player.size) player.y player.size; if (player.x SCENE_WIDTH - player.size) player.x SCENE_WIDTH - player.size; if (player.y SCENE_HEIGHT - player.size) player.y SCENE_HEIGHT - player.size; }边界约束也建议加上否则玩家控制鱼跑到屏幕外面找不回来很影响体验。3.3 AI 鱼游动行为实现AI 鱼的游动如果只是随机飘游戏会显得很假一点没有大鱼吃小鱼的紧张感。所以我给 AI 鱼设计了两种行为模式追击和逃逸。核心就是一个比较比较自己与玩家鱼的大小关系再决定是朝玩家游过去还是逃开void update_ai_fish(Fish* self, Fish* player) { float dx player-x - self-x; float dy player-y - self-y; float dist sqrt(dx * dx dy * dy); if (dist 0.01f) return; // 决定方向自己小就逃自己大就追 float decision (self-size player-size) ? -1.0f : 1.0f; float speed AI_BASE_SPEED (player-size - self-size) * 0.05f; if (speed 0) speed -speed; self-vx (dx / dist) * speed * decision; self-vy (dy / dist) * speed * decision; }这个逻辑虽然简单却能让 AI 鱼表现出趋利避害的行为小鱼躲着你走大鱼追着你跑游戏瞬间有了考验反应力的味道。实际测试下来AI 鱼偶尔会卡在场景边缘反复横跳我临时加了一个偏移角度随机扰动每帧给 AI 鱼的速度方向加个轻微随机偏转效果改善明显。3.4 图片资源加载与界面绘制Funcode 加载图片相对简单在资源加载阶段调用对应函数把图片读入内存绘制时再按坐标画出即可。如果学习时没找到合适的素材可以用不同颜色的圆形色块替代概念图先用色块把逻辑跑通再替换成真实图片也是一个很优雅的迭代路径。绘制顺序也有讲究先画背景图再画普通生存的 AI 鱼然后画玩家鱼最后画文字分数、提示。这个顺序能保证文字永远在最上层不会被鱼盖住。当年我一开始把分数画在最底层结果一条大鱼游过来直接把分数盖掉了还以为是程序显示 bug。游戏结束后需要切换到结算界面我这里的做法是设置一个 gameOver 标志在 draw_scene 里根据标志来决定画游戏画面还是画结算提示文字。状态机的思想哪怕只有两个状态也会让代码清晰很多。4. 常见问题与调试技巧实录课设阶段大家遇到的问题往往就那么几种不管是哪所学校的同学踩的坑高度相似。我把最容易中招的几个问题整理成清单按出现概率从高到低排列问题现象原因分析解决方案游戏画面闪烁严重缺少双缓冲或绘图逻辑和延迟机制冲突优先用 Funcode 自带的缓冲刷新函数如果自定义绘制先绘制在缓冲区再一次性刷新鱼群越界后不动了边界条件把速度直接清零导致鱼卡墙角越界时改修正位置而不是清速度必要时反向并保留原速碰撞时有时吃不到判定半径用图片矩形长宽的一半鱼头尾有空白用鱼身有效半径比如原图片半径的 70%-80%判定更贴近视觉游戏运行太快或太慢主循环帧率没有固定不同电脑速度不同使用固定时间步或利用框架自带的延时函数稳定刷新间隔点击关闭窗口后程序还挂着没有在主循环里处理窗口关闭事件主循环条件判断要包含框架的运行状态4.1 画面闪烁问题的完整排查这是最容易遇到也最劝退的问题。当初我第一版画完运行起来整个界面闪得像迪厅灯球。排查后发现问题出在绘图顺序上我先调用了清屏函数然后才画背景中间隔了一条画鱼逻辑导致屏幕刷新过程中露底。解决方式并不复杂先画背景再画所有动态对象最后统一刷新到屏幕。如果 Funcode 自身没有提供现成的双缓冲函数就参考这个思路把画面先绘制到一块内存缓存上一次性输出。这是所有 2D 游戏通用的避闪方案。4.2 生成鱼群的节奏控制鱼生成得太多游戏卡顿且玩家没有喘息空间生成得太少几分钟后画面空旷索然无味。我调试后的经验值是初始场景 8 条 AI 鱼每秒补充 1-2 条上限 30 条。分数每增加 100 分上限提高 5 条让游戏难度呈阶梯上升。这个节奏完全靠反复试玩调整得来。建议你像我一样做个小工具函数专门显示当前存活鱼数量和最近一次帧率。把这两个数字打到屏幕上调校参数时心里就有数不用靠感觉去猜。4.3 内存与异常退出的隐患C 语言课设最常见的内存问题就是数组越界。当我把鱼的数量上限设置为 30但在生成函数里忘记检查 count 是否已满时一旦超出 30就会访问数组下标 30 之后的非法内存程序表现时好时坏有时候跑 5 分钟才崩排查起来非常折磨人。建议在生成鱼之前先写一个简单的判断if (scene-count MAX_FISH) return;这一个条件就能避免绝大多数越界问题。并且养成每次写数组遍历都用边界条件判断的习惯不是课设阶段才临时突击以后写任何 C 代码都受益。5. 课设答辩时常见问题与扩展方向5.1 老师爱问的三个点课设最终要交报告、做演示老师几乎必问的问题无非这几个项目的难点在哪里你怎么解决的答AI 鱼行为逻辑的平衡设计、碰撞检测的精度和性能取舍。为什么使用结构体而不是多个数组答结构体把鱼的所有属性内聚到一起提高可读性和可维护性新增属性时不用改所有函数签名。如果鱼的数量从 30 变成 3000你的程序还能流畅跑吗答不能直接流畅因为两两碰撞检测的时间复杂度是 O(n²)。优化方向是先粗筛把场景网格化只检查相邻格子里的鱼再做精确检测。提前储备这几个答案答辩时就不会卡壳。5.2 这些扩展方向会让项目加分如果学有余力在基础版本上做这几个扩展会很容易引起老师的兴趣增加玩家鱼在吃掉特定数量小鱼后的进化动画视觉效果更好增加血量条和道具加速、无敌、缩小丰富玩法把碰撞检测从 O(n²) 优化为网格空间筛然后对比优化前后性能写成报告加入简单的音效反馈吃鱼音效、失败音效哪怕只完成其中两个项目在答辩中的技术含量一下就提上去了。5.3 关于 Funcode 源代码的一些课设经验网上能搜到的 Funcode 大鱼吃小鱼源码我见过几个版本总体质量参差不齐。参考时我的建议是一定不要直接拿来当自己的程序提交。一是代码风格和课程要求相差太大容易被查出二是别人的代码你很难在几分钟内讲清楚答辩时老师随便问两句就露馅。更合理的做法是把网上代码当需求说明书来读看懂它的模块划分、碰撞检测的细节、鱼群生成逻辑然后关掉它自己重新写一遍。这样你交上去的代码就是真正属于自己的作品从调试报告到答辩提问都能坦然应对。我个人当时就是参考了思路后重新设计了结构体和 AI 行为才把整体代码质量和体验拉到能满意交差的程度。做这个课设下来最大的感受是一个看似简单的游戏要把它做得没毛病需要处理的细节比想象中多得多。但恰恰是这些细节——生成位置的随机性、碰撞判定的大小调整、手感优化、帧率控制——真正训练了编程思维。如果你现在也在做这个课设别只盯着源码看自己动手把代码敲一遍把那个让鱼差点碰到却没碰到的手感调好这个过程本身才是最有价值的收获。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。