C 实现植物大战僵尸(二)

C 实现植物大战僵尸(二)

前文链接,C 实现植物大战僵尸(一)

五 制作启动菜单

启动菜单函数

void startUI()
{IMAGE imageBg, imgMenu1, imgMenu2;loadimage(&imageBg, "res/menu.png");loadimage(&imgMenu1, "res/menu1.png");loadimage(&imgMenu2, "res/menu2.png");bool mouseStatus = false; //0 表示鼠标未移动至开始游戏位置while (1) {BeginBatchDraw();putimage(0, 0, &imageBg);//根据鼠标是否移动至游戏开始位置, 显示不同的图片putimagePNG(UI_LEFT_MARGIN, UI_TOP_MARGIN, mouseStatus ? &imgMenu2 : &imgMenu1);ExMessage msg;if (peekmessage(&msg)) //监听鼠标事件{//当鼠标移动至开始游戏位置, 界面高亮if (msg.x > UI_LEFT_MARGIN && msg.x < UI_LEFT_MARGIN + UI_WIDTH&& msg.y > UI_TOP_MARGIN && msg.y < UI_TOP_MARGIN + UI_HIGHT){putimagePNG(UI_LEFT_MARGIN, UI_TOP_MARGIN, &imgMenu2);//表示鼠标移动至开始游戏位置, 如果一直不移动鼠标则一直高亮mouseStatus = true;//当鼠标点击时, 进入游戏if (msg.message == WM_LBUTTONDOWN)return;}else mouseStatus = false;}EndBatchDraw();}
}

提醒

不能把 startUI 放在 gameInit 前, gameInit 包含了创建游戏图形窗口

int main()
{gameInit(); //不能把 startUI 放在 gameInit 前, gameInit 包含了创建游戏图形窗口startUI();updateWindow(); //窗口视图展示int timer = 0; //用以计时 20 毫秒更新一次while (1){userClick(); //监听窗口鼠标事件timer += getDelay();if (timer > 20){updateWindow(); //更新窗口视图updateGame(); //更新游戏动画帧timer = 0;}}system("pause");return 0;
}

效果展示

鼠标移动至开始冒险模式时,会变成高亮效果,当点击开始开始冒险模式时,进入游戏

image-20241227115451230

六 创建和显示随机阳光

相关数据结构

//阳光球在飘落过程中 X 坐标不变
typedef struct SunShineBall
{int x;              //当前 X 轴坐标int y;              //当前 Y 轴坐标int frameId;        //当前图片帧编号int destination;    //飘落目标位置 Y 坐标bool used;          //是否在使用int timer;          //统计飘落目标位置后的帧次数
}SunShineBall;
#define MAX_BALLS_NUM 10
#define SUM_SHINE_PIC_NUM 29
SunShineBall balls[MAX_BALLS_NUM];
IMAGE imgSunShineBall[SUM_SHINE_PIC_NUM];

在更新游戏数据的函数中,创建阳光球并且更新阳光球数据

void updateGame() 
{for (int i = 0; i < GRASS_GRID_ROW; ++i){for (int j = 0; j < GRASS_GRID_COL; ++j){if (plants[i][j].type >= 0){if (imgPlant[plants[i][j].type][++plants[i][j].frameId] == NULL)plants[i][j].frameId = 0;}}}createSunshine();updateSunshine();
}

核心实现是 createSunshine (创建阳光球) 和 updateSunshine (更新阳光球数据)

void createSunshine() 
{static int callCnt = 0;static int randomCallCnt = 400;if (callCnt++ >= randomCallCnt) {randomCallCnt = 200 + rand() % 200;callCnt = 0;//从阳光池中取一个可用阳光for (int i = 0; i < MAX_BALLS_NUM; ++i){//找到一个未使用的阳光, 则进行初始化if (!balls[i].used){//只允许阳光掉落在草地范围内任意位置balls[i].x = GRASS_LEFT_MARGIN + (rand() % (GRASS_GRID_COL * GRASS_GRID_WIDTH));balls[i].y = GRASS_TOP_MARGIN;balls[i].frameId = 0;//目标点在中间三行balls[i].destination = GRASS_TOP_MARGIN + GRASS_GRID_ROW + (rand() % (3 * GRASS_GRID_HIGHT));balls[i].used = true;balls[i].timer = 0;break;}}}
}void updateSunshine()
{for (int i = 0; i < MAX_BALLS_NUM; ++i) {if (balls[i].used){if (balls[i].y < balls[i].destination){balls[i].y += 2; //每次移动两个像素//修改当前图片帧编号, 并在到达 SUM_SHINE_PIC_NUM 时重置图片帧为 0balls[i].frameId = ++balls[i].frameId % SUM_SHINE_PIC_NUM;}else //当阳光下落至目标位置时, 停止移动{if (balls[i].timer < 100) ++balls[i].timer;else balls[i].used = false;}}}
}

在 gameInit 函数中加载阳光图片

void gameInit() 
{//加载背景图片loadimage(&imgBg, "res/map0.jpg");loadimage(&imgBar, "res/bar5.png");//加载植物卡片char name[64];//将二维指针数组内存空间置零memset(imgPlant, 0, sizeof(imgPlant));memset(plants, -1, sizeof(plants));memset(balls, 0, sizeof(balls));for (int i = 0; i < PLANT_CNT; ++i){//获取植物卡片相对路径名称sprintf(name, "res/Cards/card_%d.png", i + 1);loadimage(&imgCards[i], name);for (int j = 0;i < MAX_PICTURE_NUM; ++j){//获取动态植物素材相对路径名称sprintf(name, "res/Plants/%d/%d.png", i, j + 1);if (fileExist(name)) {imgPlant[i][j] = new IMAGE;loadimage(imgPlant[i][j], name);}else break;}}//加载阳光图片for (int i = 0; i < SUM_SHINE_PIC_NUM; ++i){sprintf(name, "res/sunshine/%d.png", i + 1);loadimage(&imgSunShineBall[i], name);}//配置随机种子srand(time(NULL));//创建游戏图形窗口initgraph(WIN_WIDTH, WIN_HIGHT, 1);
}

在 updateWindow 函数中渲染阳光球

void updateWindow() 
{//使用双缓冲, 解决输出窗口闪屏BeginBatchDraw();//渲染背景图至窗口putimage(0, 0, &imgBg);putimagePNG(250, 0, &imgBar);//渲染植物卡牌for (int i = 0;i < PLANT_CNT;++i)putimage(PIC_LEFT_MARGIN + i * PIC_WIDTH, 6, &imgCards[i]);//渲染种植植物for (int i = 0; i < GRASS_GRID_ROW; ++i){for (int j = 0; j < GRASS_GRID_COL; ++j) {if (plants[i][j].type >= 0){putimagePNG(GRASS_LEFT_MARGIN + j * GRASS_GRID_WIDTH + 5, //微调植物种植位置GRASS_TOP_MARGIN + i * GRASS_GRID_HIGHT + 10,imgPlant[plants[i][j].type][plants[i][j].frameId]);}}}//渲染随机阳光for (int i = 0; i < MAX_BALLS_NUM; ++i){if (balls[i].used) putimagePNG(balls[i].x, balls[i].y, &imgSunShineBall[balls[i].frameId]);}//渲染当前拖动的植物if (currIndex >= 0){IMAGE* currImage = imgPlant[currIndex][0];putimagePNG(currX - currImage->getwidth() / 2,currY - currImage->getheight() / 2, currImage);}EndBatchDraw(); //结束双缓冲
}

效果展示

阳光球会在游戏开始的 400 帧后,开始从随机位置(只能是草坪)下落,之后阳光球会在 200 帧加上一个 200 内随机帧的时间内下落

image-20241227115654402

七 收集阳光并显示阳光值

int sunShineVal = 50; //全局变量阳光值

核心函数

#include <mmsystem.h>
#pragma commet(lib, "winmm.lib")
//加上音效头文件, 如果有 mciSendString 外部符号 ERROR 请按下方链接解决void collectSunShine(ExMessage* msg)
{IMAGE* imgSunShine = NULL;for (int i = 0; i < MAX_BALLS_NUM; ++i){//阳光球在使用中if (balls[i].used){//找到对应的阳光球图片imgSunShine = &imgSunShineBall[balls[i].frameId];//判断鼠标移动的位置是否处于当前阳光球的位置if (msg->x > balls[i].x && msg->x < balls[i].x + imgSunShine->getwidth()&& msg->y > balls[i].y && msg->y < balls[i].y + imgSunShine->getheight()) {mciSendString("play res/audio/sunshine.mp3", 0, 0, 0);balls[i].used = false;sunShineVal += 25;}}}
}

在 userClick 函数中调用,收集阳光值

image-20241227121614596

在 gameInit 函数中设置阳光值字体

void gameInit() 
{//加载背景图片loadimage(&imgBg, "res/map0.jpg");loadimage(&imgBar, "res/bar5.png");//加载植物卡片char name[64];//将二维指针数组内存空间置零memset(imgPlant, 0, sizeof(imgPlant));memset(plants, -1, sizeof(plants));memset(balls, 0, sizeof(balls));for (int i = 0; i < PLANT_CNT; ++i){//获取植物卡片相对路径名称sprintf(name, "res/Cards/card_%d.png", i + 1);loadimage(&imgCards[i], name);for (int j = 0;i < MAX_PICTURE_NUM; ++j){//获取动态植物素材相对路径名称sprintf(name, "res/Plants/%d/%d.png", i, j + 1);if (fileExist(name)) {imgPlant[i][j] = new IMAGE;loadimage(imgPlant[i][j], name);}else break;}}//加载阳光图片for (int i = 0; i < SUM_SHINE_PIC_NUM; ++i){sprintf(name, "res/sunshine/%d.png", i + 1);loadimage(&imgSunShineBall[i], name);}//配置随机种子srand(time(NULL));//创建游戏图形窗口initgraph(WIN_WIDTH, WIN_HIGHT, 1);//设置字体LOGFONT f;gettextstyle(&f);f.lfHeight = 30;f.lfWidth = 15;strcpy(f.lfFaceName, "Segoe UI Black");f.lfQuality = ANTIALIASED_QUALITY; //抗锯齿化效果settextstyle(&f);setbkmode(TRANSPARENT); //设置背景透明setcolor(BLACK); //设置字体颜色
}

在 updateWindow 函数中渲染阳光值

void updateWindow() 
{//使用双缓冲, 解决输出窗口闪屏BeginBatchDraw();//渲染背景图至窗口putimage(0, 0, &imgBg);putimagePNG(250, 0, &imgBar);//渲染植物卡牌for (int i = 0;i < PLANT_CNT;++i)putimage(PIC_LEFT_MARGIN + i * PIC_WIDTH, 6, &imgCards[i]);//渲染种植植物for (int i = 0; i < GRASS_GRID_ROW; ++i){for (int j = 0; j < GRASS_GRID_COL; ++j) {if (plants[i][j].type >= 0){putimagePNG(GRASS_LEFT_MARGIN + j * GRASS_GRID_WIDTH + 5, //微调植物种植位置GRASS_TOP_MARGIN + i * GRASS_GRID_HIGHT + 10,imgPlant[plants[i][j].type][plants[i][j].frameId]);}}}//渲染随机阳光for (int i = 0; i < MAX_BALLS_NUM; ++i){if (balls[i].used) putimagePNG(balls[i].x, balls[i].y, &imgSunShineBall[balls[i].frameId]);}//渲染当前拖动的植物if (currIndex >= 0){IMAGE* currImage = imgPlant[currIndex][0];putimagePNG(currX - currImage->getwidth() / 2,currY - currImage->getheight() / 2, currImage);}//渲染阳光值char scoreText[8];sprintf(scoreText, "%d", sunShineVal);outtextxy(277, 67, scoreText);EndBatchDraw(); //结束双缓冲
}

效果展示

阳光球在下落过程中,或到达目标点后停留的 100 帧内。若鼠标移动至对应阳光球的位置,则该阳光被收集(会触发对应的音效和左上角阳光值增加 25)

image-20241227120832551

vs 中 mciSendString 添加音效报错无法找到的外部符号

八 创建僵尸并实现行走

相关数据结构

#define MAX_ZOMBIE_NUM 10
#define MAX_ZOMBIE_PIC_NUM 22
typedef struct Zombie {int x;              //当前 X 轴坐标int y;              //当前 Y 轴坐标int frameId;        //当前图片帧编号int speed;bool used;          //是否在使用
};
Zombie zombies[MAX_ZOMBIE_NUM];
IMAGE imgZombies[MAX_ZOMBIE_PIC_NUM];

在更新游戏数据的函数中,创建僵尸并且更新僵尸数据

void updateGame() 
{for (int i = 0; i < GRASS_GRID_ROW; ++i){for (int j = 0; j < GRASS_GRID_COL; ++j){if (plants[i][j].type >= 0){if (imgPlant[plants[i][j].type][++plants[i][j].frameId] == NULL)plants[i][j].frameId = 0;}}}createSunshine();updateSunshine();createZombie();updateZombie();
}

核心函数

void createZombie()
{//延缓函数调用次数并增加些随机性static int zombieCallCnt = 0;static int randZombieCallCnt = 500;if (zombieCallCnt++ < randZombieCallCnt) return;randZombieCallCnt = 300 + rand() % 200;zombieCallCnt = 0;for (int i = 0; i < MAX_ZOMBIE_NUM; ++i){//找一个未在界面的僵尸初始化if (!zombies[i].used){zombies[i].x = WIN_WIDTH;//出现在草地的任意一格上zombies[i].y = GRASS_TOP_MARGIN +(rand() % GRASS_GRID_ROW) * GRASS_GRID_HIGHT;zombies[i].frameId = 0;//僵尸的移动速度zombies[i].speed = 1;zombies[i].used = 1;break;}}
}void updateZombie() 
{//延缓函数调用次数static int CallCnt = 0;if (++CallCnt < 3) return;CallCnt = 0;for (int i = 0; i < MAX_ZOMBIE_NUM; ++i){if (zombies[i].used){//僵尸行走zombies[i].x -= zombies[i].speed;//僵尸更换图片帧zombies[i].frameId = ++zombies[i].frameId % MAX_ZOMBIE_PIC_NUM;//目前先这样写待优化if (zombies[i].x < 170){printf("GAME OVER !");MessageBox(NULL, "over", "over", 0);exit(0);}}}
}

在 gameInit 中加载图片

//加载僵尸图片
for (int i = 0; i < MAX_ZOMBIE_PIC_NUM; ++i)
{sprintf(name, "res/zm/0/%d.png", i + 1);loadimage(&imgZombies[i], name);
}

在 updateWindow 中渲染僵尸

//渲染僵尸
for (int i = 0; i < MAX_ZOMBIE_NUM; ++i)
{if (zombies[i].used) {IMAGE* img = &imgZombies[zombies[i].frameId];//该位置 + img->getheight(), 因为 zombies[i].y 是草地格子的高度, +5 像素是微调putimagePNG(zombies[i].x, zombies[i].y + img->getheight() + 5,img);}
}

效果展示

僵尸会随机从游戏窗口右边任意草格子产生,并行走至左边房屋处。当僵尸行走至左边房屋处时,游戏将结束,并弹出提示窗口 over ,点击后程序退出

image-20241228105218658

image-20241228105458665

九 实现阳光球飞跃

在阳光球结构体中增加成员

typedef struct SunShineBall
{int x;              //当前 X 轴坐标int y;              //当前 Y 轴坐标int frameId;        //当前图片帧编号int destination;    //飘落目标位置 Y 坐标bool used;          //是否在使用int timer;          //统计飘落目标位置后的帧次数float xOffset;      //阳光球飞跃过程中每次 X 轴偏移量float yOffset;      //阳光球飞跃过程中每次 Y 轴偏移量
}SunShineBall;

在创建阳光球时进行初始化(有进行 memset 其实不初始化也是 0,仅为了规范)

注意更改 createSunshine 的判断条件,if (!balls[i].used && balls[i].xOffset == 0) 在飞跃状态时不能对其进行初始化

void createSunshine() 
{static int sunCallCnt = 0;static int randSunCallCnt = 400;if (++sunCallCnt < randSunCallCnt) return;randSunCallCnt = 200 + rand() % 200;sunCallCnt = 0;//从阳光池中取一个可用阳光for (int i = 0; i < MAX_BALLS_NUM; ++i){//找到一个未使用的阳光, 则进行初始化if (!balls[i].used && balls[i].xOffset == 0){//只允许阳光掉落在草地范围内(不允许左一格)balls[i].x = GRASS_LEFT_MARGIN + GRASS_GRID_WIDTH +(rand() % GRASS_GRID_COL) * GRASS_GRID_WIDTH;balls[i].y = GRASS_TOP_MARGIN;balls[i].frameId = 0;//目标点在中间三行balls[i].destination = GRASS_TOP_MARGIN + GRASS_GRID_HIGHT + (rand() % (3 * GRASS_GRID_HIGHT));balls[i].used = true;balls[i].timer = 0;//对阳光球飞跃过程中的 X, Y 进行初始化balls[i].xOffset = 0;balls[i].yOffset = 0;break;}}
}

在收集阳光球时,计算阳光球飞跃过程中的 X, Y 偏移量

void collectSunShine(ExMessage* msg)
{IMAGE* imgSunShine = NULL;for (int i = 0; i < MAX_BALLS_NUM; ++i){//阳光球在使用中if (balls[i].used){//找到对应的阳光球图片imgSunShine = &imgSunShineBall[balls[i].frameId];//判断鼠标移动的位置是否处于当前阳光球的位置if (msg->x > balls[i].x && msg->x < balls[i].x + imgSunShine->getwidth()&& msg->y > balls[i].y && msg->y < balls[i].y + imgSunShine->getheight()) {mciSendString("play res/audio/sunshine.mp3", NULL, 0, NULL);balls[i].used = false;//计算阳光球飞跃过程中的 X, Y 偏移量const float angle = atan((float)(balls[i].y - 0) / (float)(balls[i].x - 262));balls[i].xOffset = 16 * cos(angle);balls[i].yOffset = 16 * sin(angle);}}}
}

主要内容,是在更新阳光球游戏数据时,else if (balls[i].xOffset) 需要不断调整 balls[i].xballs[i].y 的值(不断调整阳光球的位置坐标)

void updateSunshine()
{for (int i = 0; i < MAX_BALLS_NUM; ++i) {if (balls[i].used){if (balls[i].y < balls[i].destination){balls[i].y += 2; //每次移动两个像素//修改当前图片帧编号, 并在到达 SUM_SHINE_PIC_NUM 时重置图片帧为 0balls[i].frameId = ++balls[i].frameId % SUM_SHINE_PIC_NUM;}else //当阳光下落至目标位置时, 停止移动{if (balls[i].timer < 100) ++balls[i].timer;else balls[i].used = false;}}else if (balls[i].xOffset) //阳光球处于飞跃状态{if (balls[i].y > 0 && balls[i].x > 262){//不断调整阳光球的位置坐标const float angle = atan((float)(balls[i].y - 0) / (float)(balls[i].x - 262));balls[i].xOffset = 16 * cos(angle);balls[i].yOffset = 16 * sin(angle);balls[i].x -= balls[i].xOffset;balls[i].y -= balls[i].yOffset;}else{//阳光球飞至计分器位置, 则将 xOffset 置 0, 且加上 25 积分balls[i].xOffset = 0;balls[i].yOffset = 0;sunShineVal += 25;}}}
}

最后不要忘记飞跃阳光球的渲染条件,修改 updateWindow 函数

image-20241228183659124

优化使用 mciSendString 收集阳光球卡顿

方法一好处在于节省资源,不会有线程的频繁创建和销毁;方法二好处是简便(原理同样是开异步线程)

方法一 : 单独开一个线程死循环

static bool isEnd = false;
/* sunShineMusic 加减也可换为使用 mutex */ 
long sunShineMusic = 0;
HANDLE sunShineThread = NULL;DWORD WINAPI PlayMusic(LPVOID lpParam)
{while (1) {if (sunShineMusic){mciSendString("play res/audio/sunshine.mp3", NULL, 0, NULL);InterlockedDecrement(&sunShineMusic);/* 这里也可使用异步 notify 的方式 */Sleep(100);}if (isEnd) break;}return 0;
}

在收集阳光时把 sunShineMusic InterlockedIncrement

void collectSunShine(ExMessage* msg)
{IMAGE* imgSunShine = NULL;for (int i = 0; i < MAX_BALLS_NUM; ++i){//阳光球在使用中if (balls[i].used){//找到对应的阳光球图片imgSunShine = &imgSunShineBall[balls[i].frameId];//判断鼠标移动的位置是否处于当前阳光球的位置if (msg->x > balls[i].x && msg->x < balls[i].x + imgSunShine->getwidth()&& msg->y > balls[i].y && msg->y < balls[i].y + imgSunShine->getheight()) {InterlockedIncrement(&sunShineMusic);balls[i].used = false;sunShineVal += 25;}}}
}

程序退出时,把线程资源清除

int main()
{gameInit(); //不能把 startUI 放在 gameInit 前, gameInit 包含了创建游戏图形窗口startUI();updateWindow(); //窗口视图展示sunShineThread = CreateThread(NULL, 0,(LPTHREAD_START_ROUTINE)PlayMusic, (LPVOID)NULL, 0, NULL);int timer = 0; //用以计时 20 毫秒更新一次while (1){userClick(); //监听窗口鼠标事件timer += getDelay();if (timer > 20){updateWindow(); //更新窗口视图updateGame(); //更新游戏动画帧timer = 0;}}isEnd = true;//等待线程退出WaitForSingleObject(sunShineThread, INFINITE);if (sunShineThread)//关闭线程CloseHandle(sunShineThread);system("pause");return 0;
}

方法二 : 使用 PlaySound API

注意 SND_ASYNC 参数,可参考 API 之 playsound,同样是以异步线程方式去播放音频

void collectSunShine(ExMessage* msg)
{IMAGE* imgSunShine = NULL;for (int i = 0; i < MAX_BALLS_NUM; ++i){//阳光球在使用中if (balls[i].used){//找到对应的阳光球图片imgSunShine = &imgSunShineBall[balls[i].frameId];//判断鼠标移动的位置是否处于当前阳光球的位置if (msg->x > balls[i].x && msg->x < balls[i].x + imgSunShine->getwidth()&& msg->y > balls[i].y && msg->y < balls[i].y + imgSunShine->getheight()) {PlaySound("res/audio/sunshine.wav", NULL, SND_FILENAME | SND_ASYNC);balls[i].used = false;sunShineVal += 25;}}}
}

解决 VS 告警太多,屏蔽该告警

例告警序号为 8888

一 直接在代码中添加 #pragma warning(disable:8888)

二 进入项目属性,通过 C/C++ -> Advanced -> Disable Specific Warnings 设置,输入8888 来屏蔽

image-20241228122134738

效果展示

在阳光球下落或落至草地目标点未消失之前,将鼠标移至阳光球上时,阳光球将会飞跃至左上角的计分板,然后阳光值积分会增加 25 (GIF 动图如下)
超过 CSDN 图片大小限制了 。。。感兴趣可以访问 如下链接

https://lucky-1331733286.cos.ap-guangzhou.myqcloud.com/images/202412281852663.gif

遇到的小问题

反三角函数 atan 的函数是浮点数类型(之前没用过),千万不要写成这样 const float angle = atan()(balls[i].y - 0) / (balls[i].x - 262)); ,将会导致阳光球在飞跃过程先在 X 轴平移一段再飞跃

在 debug 时,直接把对应的 xOffset,yOffset,x 和 y 打印了出来,如下

image-20241228133351839

才发现了问题的原因,解决方法就是如上代码,直接强转即可 const float angle = atan((float)(balls[i].y - 0) / (float)(balls[i].x - 262));

原因很好理解,跟数值在计算机中如何存储有关,感兴趣的可以去翻 C 进阶 — 数据在内存中的存储

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/65958.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpiderFlow平台v0.5.0之数据库连接

一、寻找lib目录安装方式 在 SpiderFlow 平台中&#xff0c;连接数据库时需要指定数据库的 DriverClassName&#xff0c;并确保正确配置数据库驱动。通常&#xff0c;驱动文件&#xff08;JAR 文件&#xff09;需要放置在指定的文件夹中&#xff0c;以便 SpiderFlow 可以找到并…

【人工智能机器学习基础篇】——深入详解监督学习之模型评估:掌握评估指标(准确率、精确率、召回率、F1分数等)和交叉验证技术

深入详解监督学习之模型评估 在监督学习中&#xff0c;模型评估是衡量模型性能的关键步骤。有效的模型评估不仅能帮助我们理解模型在训练数据上的表现&#xff0c;更重要的是评估其在未见数据上的泛化能力。本文将深入探讨监督学习中的模型评估方法&#xff0c;重点介绍评估指…

单周期CPU电路设计

1.实验目的 本实验旨在让学生通过设计一个简单的单周期 CPU 电路&#xff0c;深入理解 RISC-V 指令集的子集功能实现&#xff0c;掌握数字电路设计与实现的基本流程&#xff0c;包括指令解析、部件组合、电路设计以及功能仿真等环节&#xff0c;同时培养verilog HDL编程能力和…

【AIGC】 ChatGPT实战教程:如何高效撰写学术论文引言

&#x1f4a5; 欢迎来到我的博客&#xff01;很高兴能在这里与您相遇&#xff01; 首页&#xff1a;GPT-千鑫 – 热爱AI、热爱Python的天选打工人&#xff0c;活到老学到老&#xff01;&#xff01;&#xff01;导航 - 人工智能系列&#xff1a;包含 OpenAI API Key教程, 50个…

C语言中的强弱符号

文章目录 一、基本定义二、链接过程中的行为三、应用场景四、强弱符号示例1五、稍有难度示例2 在C语言中&#xff0c;强弱符号是与链接过程相关的重要概念&#xff0c;C中不存在强弱符号&#xff0c;以下是对它们的详细讲解&#xff1a; 一、基本定义 强符号 强符号通常是指在…

【网络协议】开放式最短路径优先协议OSPF详解(一)

OSPF 是为取代 RIP 而开发的一种无类别的链路状态路由协议&#xff0c;它通过使用区域划分以实现更好的可扩展性。 文章目录 链路状态路由协议OSPF 的工作原理OSPF 数据包类型Dijkstra算法、管理距离与度量值OSPF的管理距离OSPF的度量值 链路状态路由协议的优势拓扑结构路由器O…

鸿蒙HarmonyOS开发:拨打电话、短信服务、网络搜索、蜂窝数据、SIM卡管理、observer订阅管理

文章目录 一、call模块&#xff08;拨打电话&#xff09;1、使用makeCall拨打电话2、获取当前通话状态3、判断是否存在通话4、检查当前设备是否具备语音通话能力 二、sms模块&#xff08;短信服务&#xff09;1、创建短信2、发送短信 三、radio模块&#xff08;网络搜索&#x…

Docker学习相关笔记,持续更新

如何推送到Docker Hub仓库 在Docker Hub新建一个仓库&#xff0c;我的用户名是 leilifengxingmw&#xff0c;我建的仓库名是 hello_world。 在本地的仓库构建镜像&#xff0c;注意要加上用户名 docker build -t leilifengxingmw/hello_world:v1 .构建好以后&#xff0c;本地会…

2024年12月 Scratch 图形化(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch图形化等级考试(1~4级)全部真题・点这里 一、单选题(共25题,共50分) 第 1 题 点击下列哪个按钮,可以将红框处的程序放大?( ) A. B. C.

向 SwiftUI 视图注入 managedObjectContext 环境变量导致 Xcode 预览(Preview)崩溃的解决

问题现象 从 SwiftUI 诞生到现在,我们这些秃头码农们早已都习惯了在 Xcode 预览中调试 App 界面了。不过,对于某些场景下向 SwiftUI 视图传递 managedObjectContext 环境变量(environment)总是会导致 Xcode 预览崩溃,这是怎么回事呢? 如上图所示,甚至我们将一个常驻内存…

VMware安装配置

1、官网下载VMware16 &#xff08;1&#xff09;进入VMware官网https://www.vmware.com/cn.html&#xff0c;之后点击下载里的Workstation Pro&#xff1a; &#xff08;2&#xff09;之后选择你要下载的VMware的版本&#xff0c;找到合适的下载&#xff0c;我这里以Windows系…

机器学习之正则化惩罚和K折交叉验证调整逻辑回归模型

机器学习之正则化惩罚和K折交叉验证调整逻辑回归模型 目录 机器学习之正则化惩罚和K折交叉验证调整逻辑回归模型1 过拟合和欠拟合1.1 过拟合1.2 欠拟合 2 正则化惩罚2.1 概念2.2 函数2.3 正则化种类 3 K折交叉验证3.1 概念3.2 图片理解3.3 函数导入3.4 参数理解 4 训练模型K折交…

Qt 下位机串口模拟器

使用 vspd 创建虚拟配对串口&#xff0c;Qt 实现下位机串口模拟器&#xff0c;便于上位机开发及实时调试&#xff0c;适用字符串格式上下位机串口通信&#xff0c;数据包格式需增加自定义解析处理。 通过以下链接下载 vspd 安装包&#xff0c;进行 dll 破解。 链接: https://…

vue 虚拟滚动 vue-virtual-scroller RecycleScroller

vue 3 https://github.com/Akryum/vue-virtual-scroller/blob/master/packages/vue-virtual-scroller/README.md vue 2 https://github.com/Akryum/vue-virtual-scroller/tree/v1/packages/vue-virtual-scroller npm install --save vue-virtual-scrollernextmain.js // 虚拟滚…

密钥登录服务器

1. 生成 SSH 密钥对 如果您还没有生成密钥对&#xff0c;可以使用以下命令生成&#xff1a; ssh-keygen 在 root 用户的家目录中生成了一个 .ssh 的隐藏目录&#xff0c;内含两个密钥文件&#xff1a;id_rsa 为私钥&#xff0c;id_rsa.pub 为公钥。 在提示时&#xff0c;您可…

30天开发操作系统 第 11 天 --制作窗口

前言 在harib07d中鼠标移动到右侧后就不能再往右移了,大家有没有觉得别扭?没错,在Windows 中&#xff0c;鼠标应该可以向右或向下移动到画面之外隐藏起来的&#xff0c;可是我们的操作系统却还不能实现这样的功能&#xff0c;这多少有些遗憾。 这是为什么呢?我们还是先来看一…

物理知识1——电流

说起电流&#xff0c;应该从电荷说起&#xff0c;而说起电荷&#xff0c;应该从原子说起。 1 原子及其结构 常见的物质是由分子构成的&#xff0c;而分子又是由原子构成的&#xff0c;有的分子是由多个原子构成&#xff0c;有的分子只由一个原子构成。而原子的构成如图1所示。…

Android性能分析工具的比较

背景介绍 Systrace、Perfetto和Android Benchmarking Framework都是用于性能分析和优化的工具&#xff0c;但它们各自的用途、功能范围和适用场景有所不同。以下对它们进行详细的比较。 通常一次较完整的性能分析过程需要多工具的配合使用&#xff0c;往复迭代&#xff1a; …

Jupyter在运行上出现错误:ModuleNotFoundError: No module named ‘wordcloud‘

问题分析&#xff1a;显示Jupyter未安装这个模板 解决办法&#xff1a;在单元格内输入&#xff1a;!pip install wordcloud

大模型WebUI:Gradio全解系列8——Additional Features:补充特性(下)

大模型WebUI&#xff1a;Gradio全解系列8——Additional Features&#xff1a;补充特性&#xff08;下&#xff09; 前言本篇摘要8. Additional Features&#xff1a;补充特性8.5 嵌入托管 Spaces8.5.1 使用 Web Components 嵌入1. 嵌入步骤2. 定义嵌入的外观和行为 8.5.2 使用…