当前位置: 首页 > news >正文

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 进阶 — 数据在内存中的存储

相关文章:

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

C 实现植物大战僵尸&#xff08;二&#xff09; 前文链接&#xff0c;C 实现植物大战僵尸&#xff08;一&#xff09; 五 制作启动菜单 启动菜单函数 void startUI() {IMAGE imageBg, imgMenu1, imgMenu2;loadimage(&imageBg, "res/menu.png");loadimage(&am…...

Vivado - TCL 命令(DPU脚本、v++命令、impl策略)

目录 1. 简介 2. TCL 示例 2.1 DPU TCL 脚本 2.1.1 源码-精简 2.1.2 依赖关系 2.1.3 查 v 步骤列表 2.1.4 生成 DPU.XO 2.2 CPU 示例 2.2.1 源码-框架 2.2.2 示例设计详解 2.3 创建运行脚本 2.3.1 Generate scripts 2.3.2 runme.sh 文件 2.3.3 design_1_wrapper…...

【JDBC】数据库连接的艺术:深入解析数据库连接池、Apache-DBUtils与BasicDAO

文章目录 前言&#x1f30d; 一.连接池❄️1. 传统获取Conntion问题分析❄️2. 数据库连接池❄️3.连接池之C3P0技术&#x1f341;3.1关键特性&#x1f341;3.2配置选项&#x1f341;3.3使用示例 ❄️4. 连接池之Druid技术&#x1f341; 4.1主要特性&#x1f341; 4.2 配置选项…...

hadoop-common的下载位置分享

1.GitHub - steveloughran/winutils: Windows binaries for Hadoop versions (built from the git commit ID used for the ASF relase) 2.GitHub - cdarlint/winutils: winutils.exe hadoop.dll and hdfs.dll binaries for hadoop windows 3.winutils: hadoop winutils 镜像...

【机器学习】SVM支持向量机(一)

介绍 支持向量机&#xff08;Support Vector Machine, SVM&#xff09;是一种监督学习模型&#xff0c;广泛应用于分类和回归分析。SVM 的核心思想是通过找到一个最优的超平面来划分不同类别的数据点&#xff0c;并且尽可能地最大化离该超平面最近的数据点&#xff08;支持向量…...

Spring Boot介绍、入门案例、环境准备、POM文件解读

文章目录 1.Spring Boot(脚手架)2.微服务3.环境准备3.1创建SpringBoot项目3.2导入SpringBoot相关依赖3.3编写一个主程序&#xff1b;启动Spring Boot应用3.4编写相关的Controller、Service3.5运行主程序测试3.6简化部署 4.Hello World探究4.1POM文件4.1.1父项目4.1.2父项目的父…...

基于Spring Boot + Vue3实现的在线商品竞拍管理系统源码+文档

前言 基于Spring Boot Vue3实现的在线商品竞拍管理系统是一种现代化的前后端分离架构的应用程序&#xff0c;它结合了Java后端框架Spring Boot和JavaScript前端框架Vue.js的最新版本&#xff08;Vue 3&#xff09;。该系统允许用户在线参与商品竞拍&#xff0c;并提供管理后台…...

LeetCode--排序算法(堆排序、归并排序、快速排序)

排序算法 归并排序算法思路代码时间复杂度 堆排序什么是堆&#xff1f;如何维护堆&#xff1f;如何建堆&#xff1f;堆排序时间复杂度 快速排序算法思想代码时间复杂度 归并排序 算法思路 归并排序算法有两个基本的操作&#xff0c;一个是分&#xff0c;也就是把原数组划分成…...

华诺星空 Java 开发工程师笔试题 - 解析

单选题 1.Math.round(-11.5)等于多少?(B) A.-11.5 B.-11 C.-12 D.11.5 2.下列哪个没有继承自Collection接口。( C ) A.List B.Set C.Map D.全部 3.下列说法正确的有(B) A.在类方法中可用this来调用本类的类方法 B.在类方法中调用本类的类方法时可直接调用 C.在类…...

QT:一个TCP客户端自动连接的测试模型

版本 1:没有取消按钮 测试效果&#xff1a; 缺陷&#xff1a; 无法手动停止 测试代码 CMakeLists.txt cmake_minimum_required(VERSION 3.19) project(AutoConnect LANGUAGES CXX)find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets Network)qt_standard_project_setup(…...

关于启动vue项目,出现:Error [ERR_MODULE_NOT_FOUND]: Cannot find module ‘xxx‘此类错误

目录 一、问题报错 二、原因分析 三、解决方法 一、问题报错 node环境变量配置有问题&#xff1a; (base) xxxM73H-15:~/VueProject/pproject-vue$ npm run dev /usr/bin/env: “node”: 没有那个文件或目录vue项目启动有问题&#xff1a; (base) xxx:~/VueProject/pproj…...

电路元件与电路基本定理

电流、电压和电功率 电流 1 定义&#xff1a; 带电质点的有序运动形成电流 。 单位时间内通过导体横截面的电量定义为电流强度&#xff0c; 简称电流&#xff0c;用符号 i 表示&#xff0c;其数学表达式为&#xff1a;&#xff08;i单位&#xff1a;安培&#xff08;A&#x…...

指针之矢:C 语言内存幽境的精准飞梭

一、内存和编码 指针理解的2个要点&#xff1a; 指针是内存中一个最小单元的编号&#xff0c;也就是地址平时口语中说的指针&#xff0c;通常指的是指针变量&#xff0c;是用来存放内存地址的变量 总结&#xff1a;指针就是地址&#xff0c;口语中说的指针通常指的是指针变量。…...

uniapp下载打开实现方案,支持安卓ios和h5,下载文件到指定目录,安卓文件管理内可查看到

uniapp下载&打开实现方案&#xff0c;支持安卓ios和h5 Android&#xff1a; 1、申请本地存储读写权限 2、创建文件夹&#xff08;文件夹不存在即创建&#xff09; 3、下载文件 ios&#xff1a; 1、下载文件 2、保存到本地&#xff0c;需要打开文件点击储存 使用方法&…...

免费干净!付费软件的平替款!

今天给大家介绍一个非常好用的电脑录屏软件&#xff0c;完全没有广告界面&#xff0c;非常的干净简洁。 电脑录屏 无广告的录屏软件 这个软件不需要安装&#xff0c;打开就能看到界面直接使用了。 软件可以全屏录制&#xff0c;也可以自定义尺寸进行录制。 录制的声音选择也非…...

软路由系统 iStoreOS 中部署 Minecraft 服务器

商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。协议(License): 知识共享署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)作者(Author): lhDream链接(URL): https://blog.luhua.site/archives/1734968846131 软路由系统 iStoreOS 中部署 Minecraft…...

第 29 章 - ES 源码篇 - 网络 IO 模型及其实现概述

前言 本文介绍了 ES 使用的网络模型&#xff0c;并介绍 transport&#xff0c;http 接收、响应请求的代码入口。 网络 IO 模型 Node 在初始化的时候&#xff0c;会创建网络模块。网络模块会加载 Netty4Plugin plugin。 而后由 Netty4Plugin 创建对应的 transports&#xff0…...

细说STM32F407单片机IIC总线基础知识

目录 一、 I2C总线结构 1、I2C总线的特点 2、I2C总线通信协议 3、 STM32F407的I2C接口 二、 I2C的HAL驱动程序 1、 I2C接口的初始化 2、阻塞式数据传输 &#xff08;1&#xff09;函数HAL_I2C_IsDeviceReady() &#xff08;2&#xff09;主设备发送和接收数据 &#…...

从头开始学MyBatis—04缓存、逆向工程、分页插件

介绍了MyBatis的缓存、逆向工程和分页插件的使用 目录 1.Mybatis的缓存 1.1MyBatis的一级缓存 1.2MyBatis的二级缓存 1.3二级缓存的相关配置 1.4MyBatis缓存查询的顺序 1.5整合第三方缓存EHCache 1.5.1添加依赖 1.5.2各jar包功能 1.5.3创建EHCache的配置文件ehcache.x…...

Artec Space Spider助力剑桥研究团队解码古代社会合作【沪敖3D】

挑战&#xff1a;考古学家需要一种安全的方法来呈现新出土的陶瓷容器&#xff0c;对比文物形状。 解决方案&#xff1a;Artec Space Spider, Artec Studio 效果&#xff1a;本项目是REVERSEACTION项目的一部分&#xff0c;旨在研究无国家社会中复杂的古代技术。研究团队在考古地…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

Bean 作用域有哪些?如何答出技术深度?

导语&#xff1a; Spring 面试绕不开 Bean 的作用域问题&#xff0c;这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开&#xff0c;结合典型面试题及实战场景&#xff0c;帮你厘清重点&#xff0c;打破模板式回答&#xff0c…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

论文阅读:Matting by Generation

今天介绍一篇关于 matting 抠图的文章&#xff0c;抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法&#xff0c;已经有很多的工作和这个任务相关。这两年 diffusion 模型很火&#xff0c;大家又开始用 diffusion 模型做各种 CV 任务了&am…...