一、说明
1、先说一下为什么用C和要分享
这一款游戏是学校课设要求,因为大学嘛,懂得都懂,要求早就过时代了,于是就要求用C语言手敲一个大鱼吃小鱼(Unity3D:😂,笑嘻了)
手敲归手敲,我还是问了AI帮我提升了代码的专业性,并且进行整合、模块化打包 ,里面很多编写思想和Unity3D引擎所带的模块大差不差,就是是用C语言手敲出来的,所以我认为这些代码有一定的分享价值,当然,会拿主要的说
2、需求分析
需要制作一个大鱼吃小鱼的游戏,是个简单的2D平面游戏,玩家可以通过w、a、s、d按键实现上下左右移动,并且有其他鱼在场景移动(链表结构),并可以被玩家吃掉增加分数(其中type5为敌人,可以吃掉玩家使游戏结束),游戏结束后分数以文件形式保存在本地。
二、值得分享的代码块
1、数组妙用
还记得去年12月左右那个日更的博主吗?日更已经不在了。那些文章主要就是讲解理论的,比如c语言的数组是什么、怎么写什么的(说白了就是应试刷题),但是从4月复活的博主开始,就要介绍它们具体可以怎么用了
我需要不同种类的鱼的生成概率,而且要集成(即一个模块专门处理概率这件事,不要用一堆常量去定义概率)

总而言之,如果是有一堆数字的需求,那么需要考虑数组对其模块化盛放,避免导致混乱 ,当然,对于用游戏引擎开发来说这件事的影响微乎其微(因为unity3D用的类Java的C#语法而且本身模块够多)
2、结构体妙用
其实这里之前提过一嘴,可以参考这篇文章
总之就是不管是玩家还是NPC,他们的数据需要集成化、模块化处理,就使用结构体对数据进行存储(下面就是鱼的结构体,因为有鱼群算法需求所以写的比较多)
cpp
typedef struct FFish {
float x;
float y;
float speed;
int type;
int FaceDirection;
int size;
int width;
int height;
float mouthRatio;
float mouthWidth;
float mouthHeight;
float mouthLeft, mouthRight, mouthTop, mouthBottom;
bool isDashing; // 新增:冲刺状态标记
float originalSpeed; // 新增:原始速度存储
float gravity; // 重力加速度
float verticalSpeed; // 垂直速度
float lastInputTime; // 最后操作时间戳
float dashTimer; // 冲刺剩余时间
float dashDuration; // 冲刺总时长
float dashCooldown;
//以下是npc才会调用的
FishState state; // 当前状态
float stateTimer; // 状态计时器
float reactionDist; // 反应距离
float chaseTimeout; // 新增:type5专用计时器
float verticalOffset; // 新增:垂直浮动偏移量
float scatterAngle; // 解散角度
float scatterSpeed; // 解散速度
Vector2 localGroupCenter;
float currentSpeed;
bool yAxisLocked; //y锁定,避免状态机之间冲突
Vector2 velocity; // 新增:当前速度向量
float maxForce; // 新增:最大转向力
float viewAngle; // 新增:视野角度(弧度)
float panicTimer; // 新增:恐慌状态计时器
Vector2 prevVelocity; // 需在结构体中添加字段
int validNeighbors;
float neighborRadius;
float moveAngle; // 当前运动角度(弧度)
float previousAngle; // 新增:上一帧的角度,用于插值
} FFish;
这个思想在游戏引擎中也是有体现的

在对游戏引擎使用时,要把其中一个元素的相关代码、状态机、3D模型等人为整合在一个文件夹里面就是这种结构体思想的体现(不整合进一个文件夹里面虽说可以运行,但是你自己看着不乱吗?小组合作的时候别人又怎么看呢?)
3、循环妙用
其实一个游戏和一个简单程序的最大区别就在于:游戏是一个循环,里面的所有元素都是在一帧一帧进行更新的,如果你的游戏模块化足够高,最后main函数应该结构如下就是一个循环和跳出逻辑的组合
cpp
int main() {
srand(time(NULL));
initgraph(BG_WEIGHT, BG_HEIGHT);
InitializeGame();
BeginBatchDraw();
DWORD lastTime = GetTickCount();
static bool scoreSaved = false;
//游戏帧更新
while (1) {
DWORD currentTime = GetTickCount();
float deltaTime = (currentTime - lastTime) / 1000.0f;
lastTime = currentTime;
if (!gameover) {
UpdateGame(deltaTime);
UpdateTimer(deltaTime); // 更新计时器
DrawGame();
scoreSaved = false;
}
else {
if (!scoreSaved) {
SaveScoreToFile(score);
scoreSaved = true;
}
DrawGameOver();
if (GetAsyncKeyState(VK_ESCAPE)) break;
Sleep(10);
}
FlushBatchDraw();
}
EndBatchDraw();
closegraph();
return 0;
}
在游戏制作领域,这种循环其实就叫做帧更新------动画进行每帧更新让元素动起来
还有就是如果有一些东西不需要帧更新只需要第一次启动时加载,那么就叫初始化(上面代码的InitializeGame()函数模块)
Unity3D的脚本也是自带初始化与帧更新的(下图的start和update)

4、碰撞检测
这一块Unity3D自带的物理效果什么的比手搓效果要好,而且支持3D与2D,所以就先不介绍手搓了,主要展示一下在unity中这个模块在什么位置

需要挂在一个游戏object的下面
5、动画集成
游戏其实是每一段的动画组成的,这些东西也需要集成应用,就需要引入一个概念:
状态机------根据特定触发条件应用动画集合里面的相应动画
手搓如下
闲逛、逃离、追逐、群体行为、解散行为是五个每条鱼自带的状态,达到哪个状态的阈值就会有所切换,然后通过判断语句对该状态下的行为有所反馈
cpp
// 在FFish结构体中添加状态相关字段
typedef enum {
FSM_WANDER, // 闲逛
FSM_FLEE, // 逃离
FSM_CHASE, // 追逐
FSM_GROUP, // 群体行为
FSM_SCATTER // 解散行为
} FishState;
void UpdateAIFish(float deltaTime) {
AIfishNode** current = &AIfishHead;
while (*current) {
FFish* fish = &(*current)->fish;
fish->x += (fish->FaceDirection ? fish->speed : -fish->speed);
if (group && (fish->type >= 1 && fish->type <= 3)) {
GroupIntegratedBehavior(fish, deltaTime);
}
else {
// === 第一步:状态处理(仅计算方向/速度) ===
switch (fish->state) {
case FSM_CHASE:
ChaseBehavior(fish, deltaTime);
break;
default:
WanderBehavior(fish, deltaTime);
break;
}
}
if (!(group && (fish->type >= 1 && fish->type <= 3))) {
float effectiveSpeed = (fish->state == FSM_FLEE) ?
(fish->originalSpeed * FLEE_SPEED_MULT) : fish->speed;
fish->x += (fish->FaceDirection ? 1 : -1) * effectiveSpeed * deltaTime;
}
// 边界检测
if ((fish->FaceDirection && fish->x > BG_WEIGHT) ||
(!fish->FaceDirection && fish->x + fish->width < 0)) {
AIfishNode* temp = *current;
*current = (*current)->next;
free(temp);
}
else {
current = &(*current)->next;
}
}
}
//状态机状态反馈举例
void ChaseBehavior(FFish* fish, float deltaTime) {
if (fish->type != 5) return;
// 行为参数
const float BASE_SPEED = 3.5f; // 基础移动速度
const float WAVE_AMP = 25.0f; // 波浪幅度(像素)
const float WAVE_FREQ = 2.0f; // 波浪频率(每秒周期数)
const float VERTICAL_TRACKING = 0.6f; // 垂直追踪强度系数
// 计算玩家相对位置
float targetX = Player.x + Player.width / 2;
float targetY = Player.y + Player.height / 2;
float fishCenterX = fish->x + fish->width / 2;
float fishCenterY = fish->y + fish->height / 2;
// 水平方向移动(带预判)
float dx = targetX - fishCenterX;
fish->FaceDirection = (dx > 0) ? 1 : 0;
// 垂直方向跟踪(渐进式接近)
float dy = targetY - fishCenterY;
float verticalSpeed = dy * VERTICAL_TRACKING;
// 波浪运动参数
static float wavePhase = 0.0f; // 保持波浪相位连续
wavePhase += deltaTime * WAVE_FREQ * 2 * 3.14159f;
float waveOffset = sinf(wavePhase) * WAVE_AMP;
// 合成移动(基础速度 + 垂直跟踪 + 波浪运动)
fish->x += (fish->FaceDirection ? 1 : -1) * BASE_SPEED * deltaTime;
fish->y += (verticalSpeed * deltaTime) + (waveOffset * deltaTime);
// 动态难度:玩家体型越大,波浪幅度越小(更容易命中)
if (Player.size > 3) {
fish->y -= waveOffset * deltaTime * (Player.size - 3) * 0.2f;
}
// 边界限制
fish->y = clamp(fish->y, 0, BG_HEIGHT - fish->height);
// 超时检测(保持原有追逐时间逻辑)
fish->chaseTimeout -= deltaTime;
if (fish->chaseTimeout <= 0) {
fish->state = FSM_FLEE;
fish->speed = TYPE5_ESCAPE_SPEED;
}
}
在Unity3D中如下(是不是比手搓直观、可视)

三、总结
条件允许的话,开发游戏首选相关游戏引擎进行开发,更重要的是其实游戏引擎里面每一个模块也是由最基本的代码构成的,只不过开发人员为了便利用户进行了可视化处理,如果好奇背后原理可以进入下方Unity3D的
以上均是本人理解,如有不对欢迎各位大佬评论区指出~