当前位置: 首页 > 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…...

基于Spring Boot的宠物领养系统的设计与实现(代码+数据库+LW)

摘 要 如今社会上各行各业&#xff0c;都在用属于自己专用的软件来进行工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。互联网的发展&#xff0c;离不开一些新的技术&#xff0c;而新技术的产生往往是为了解决现有问题而产生的。针对于宠物领…...

大数据存储Nosql

大数据存储Nosql复习篇 一、理论题、论述题 第1章 绪论 1、NoSQL数据库和关系数据库在设计目标上有何主要区别&#xff1f; (1)关系数据库 优势&#xff1a;以完善的关系代数理论作为基础&#xff0c;具有数据模型、完整性约束和事务的强一致性等特点&#xff0c;借助索引机…...

Git常用指令

集中式管理工具 SVN 分布式管理工具 git 优点&#xff1a;速度&#xff0c;简单设计&#xff0c;允许高并发的分支&#xff0c;完全分布式 vi ~.txt //编写文件&#xff08;分支&#xff09; 工作区 git add ~.txt …...

2024 年发布的 Android AI 手机都有什么功能?

大家好&#xff0c;我是拭心。 2024 年是 AI 快速发展的一年&#xff0c;这一年 AI 再获诺贝尔奖&#xff0c;微软/苹果/谷歌等巨头纷纷拥抱 AI&#xff0c;多款强大的 AI 手机进入我们的生活。 今年全球 16% 的智能手机出货量为 AI 手机&#xff0c;到 2028 年&#xff0c;这…...

k8s基础(2)—Kubernetes-Namespace

一、Namespace概述 名字空间 在 Kubernetes 中&#xff0c;名字空间&#xff08;Namespace&#xff09; 提供一种机制&#xff0c;将同一集群中的资源划分为相互隔离的组。 同一名字空间内的资源名称要唯一&#xff0c;但跨名字空间时没有这个要求。 名字空间作用域仅针对带有…...

车路云网图安全风险复杂交织

随着网联汽车与智能交通深度融合&#xff0c;车云路一体协同发展&#xff0c;车联网迎来全面发展的战略机遇期&#xff0c;但在车联网高速发展的同时&#xff0c;网络安全威胁也不断敲响警钟&#xff0c;网络暴露面扩大&#xff0c;车联网网络安全风险快速升高。车路云一体化也…...

Markdown如何添加任务列表-复选框的添加

Markdown如何添加任务列表-复选框的添加 前言语法讲解使用场景及应用实例代码整和渲染结果小结其他文章快来试试吧☺️ Markdown如何添加任务列表-复选框的添加&#x1f448;点击这里也可查看 前言 To-do任务列表是一种很常见的时间管理工具&#xff0c;它适用于工作计划&…...

Android 旋转盘导航栏

1.直接上源码&#xff1a; package com.you.arc;import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.RectF; import android.support…...

matlab-数字滤波器设计与实战

文章目录 数字滤波器设计FIR 滤波器设计IIR 滤波器设计巴特沃斯滤波器切比雪夫 I 型滤波器切比雪夫II型椭圆滤波器线性相位与非线性相位零相位响应数字滤波器实战数字滤波器产生延迟的主要原因补偿滤波引入的延迟补偿常量滤波器延迟补偿与频率有关的延迟从信号中除去不需要的频…...

虚拟机中的时统卡功能和性能调优

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力&#xff0c;聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域&#xff0c;包含了应用使能套件、软件仓库、软件支持、软件适…...

[算法] [leetcode-20] 有效的括号

20 有效的括号 给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘]’ 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合…...

TDengine 如何进行高效数据建模

1.背景 数据建模对于数据库建立后整体高效运行非常关键&#xff0c;不同建模方式&#xff0c;可能会产生相差几倍的性能差别 2. 建库 建模在建库阶段应考虑几下几点&#xff1a; 建多少库 根据业务情况确定建库个数&#xff0c;TDengine 不支持跨库查询&#xff0c;如果业…...

2024.12.30(多点通信)

作业&#xff1a; 1、将广播发送和接收端实现一遍&#xff0c;完成一个发送端发送信息&#xff0c;对应多个接收端接收信息实验。 发送端 #include <myhead.h>#define PORT 8888 #define IP "192.168.124.255"int main(int argc, const char *argv[]) {//1、…...

前端路由layout布局处理以及菜单交互(三)

上篇介绍了前端项目部署以及基本依赖的应用&#xff0c;这次主要对于路由以及布局进行模块化处理 一、 创建layout模块 1、新建src/layout/index.vue <template><el-container class"common-layout"><!-- <el-aside class"aside">&l…...

Zynq PS端外设之GPIO

1. GPIO&#xff08;通用输入/输出&#xff09; GPIO外设有4个Bank&#xff0c;Bank0/1通过MIO连接到PS的引脚上&#xff1b;Bank2/3通过EMIO连接到PL的引脚上。 注意&#xff1a;Bank1的电平要改成LVCOMS 1.8 GPIO寄存器 寄存器&#xff1a; DATA_RO&#xff1a; 读取GPIO的输…...

Java 操作 PDF:从零开始创建功能丰富的PDF文档

Java 操作 PDF&#xff1a;从零开始创建功能丰富的PDF文档 引言环境准备依赖引入 创建PDF文档一键生成空白PDF添加表单字段&#xff08;选项&#xff09;添加电子签名添加公章图像 结论 引言 随着数字化办公的普及&#xff0c;PDF格式因其跨平台兼容性和安全性而被广泛应用于各…...

12.30-1-5学习周报

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 文章链接摘要Abstract一、方法介绍1.HAT-CIR2.Horde3.DWGRNet 二、实验总结 文章链接 https://arxiv.org/pdf/2405.04101 摘要 本博客介绍了论文《Continual lea…...

H3C Tftp 备份与恢复配置文件

pc 运行 tftp 软件与路由器相连。打开 tftp。 在交换机用户试图下&#xff0c;dir /all 命令查看设备的配置文件。 备份命令&#xff1a;将配置文件备份到地址为 10.10.10.2 的 tftp 服务器 <H3C>backup startup-configuration to 10.10.10.2 恢复命令&#xff1a;恢复配…...

【Matlab算法】基于改进人工势场法的移动机器人路径规划研究(附MATLAB完整代码)

基于改进人工势场法的移动机器人路径规划研究 结果图摘要1. 引言2. 方法说明2.1 基本原理2.2 改进策略3. 核心函数解释3.1 改进的斥力计算函数3.2 路径规划主函数4. 实验设计4.1 实验环境设置4.2 关键参数选择5. 结果分析5.1 实验结果5.2 性能分析附录:完整代码参考文献结果图…...

基于Springboot校园失物招领系统【附源码】

基于Springboot校园失物招领系统 效果如下&#xff1a; 系统登陆页面 物品页面 系统首页面 失物招领管理页面 失物认领页面 宣传视频页面 物品挂失留言管理页面 宣传视频类型管理页面 研究背景 在校园环境中&#xff0c;失物招领是一个常见的问题。传统的失物招领方式主要依…...

Vue 自定义指令

首先&#xff0c;我们知道vue中有很多自带指令&#xff0c;v-bind、v-on、v-model等。但在业务开发中&#xff0c;我们常见一些自定义指令如&#xff1a;v-copy、v-longpress等。那么如何定义自己所需的指令呢&#xff1f; 接下来我们分别从指令注册、指令的钩子函数、指令的参…...

JAVA-制作小游戏期末实训

源码 import game.frame.Frame;public class App {public static void main(String[] args) {System.out.println("starting......");new Frame();} } package game.controller;import game.model.Enemy;public class EnemyController implements Runnable{private…...

【Vue教程】使用Vite快速搭建前端工程化项目 | Vue3 | Vite | Node.js

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f6a9;今天毛毛张分享的是关于如何快速&#x1f3c3;‍♂️搭建一个前端工程化的项目的环境搭建以及流程&#x1f320; 文章目录 1.前端工程化环境搭建&#…...

4.CSS文本属性

4.1文本颜色 div { color:red; } 属性值预定义的颜色值red、green、blue、pink十六进制#FF0000,#FF6600,#29D794RGB代码rgb(255,0,0)或rgb(100%,0%,0%) 4.2对齐文本 text-align 属性用于设置元素内文本内容的水平对齐方式。 div{ text-align:center; } 属性值解释left左对齐ri…...

【工具整理】WIN换MAC机器使用工具整理

最近公司电脑升级&#xff0c;研发同学统一更换了 Mac Book Pro 笔记版电脑&#xff0c;整理一下安装了那些软件以及出处&#xff0c;分享记录下&#xff5e; 知识库工具 1、语雀 网址&#xff1a;语雀&#xff0c;为每一个人提供优秀的文档和知识库工具 语雀 个人花园&…...

Elasticsearch向量检索需要的数据集以及768维向量生成

Elasticsearch8.17.0在mac上的安装 Kibana8.17.0在mac上的安装 Elasticsearch检索方案之一&#xff1a;使用fromsize实现分页 快速掌握Elasticsearch检索之二&#xff1a;滚动查询(scrool)获取全量数据(golang) Elasticsearch检索之三&#xff1a;官方推荐方案search_after…...

《小型支付商城系统》项目(一)DDD架构入门

目录 1.DDD架构 1.1充血模型 1.2领域模型 1.2.1实体 1.2.2值对象 1.2.3聚合 1.2.4领域服务 1.2.5工厂 1.2.6仓储&#xff08;Repository&#xff09; 2.DDD建模 3.DDD工程模型 项目介绍&#xff1a;知识星球 | 深度连接铁杆粉丝&#xff0c;运营高品质社群&#xff…...

web课程设计--酷鲨商城-springboot和vue

文章目录 页面截图技术分析数据库代码地址 页面截图 登陆页面: 分类列表 添加分类 轮播图列表 添加轮播图 商品列表 添加商品信息 技术分析 前端使用 html页面的 vue.js&#xff08;vue2&#xff09;和element-ui绘制前端界面 后台使用Springbootmybatis来实现crud。还有一…...

解决virtualbox克隆ubuntu虚拟机之后IP重复的问题

找遍了国内论坛&#xff0c;没一个能解决该问题的&#xff0c;所以我自己写个文章吧&#xff0c;真讨厌那些只会搬运的&#xff0c;污染国内论坛环境&#xff0c;搜一个问题&#xff0c;千篇一律。 问题 操作系统版本为"Ubuntu 24.04 LTS" lennytest1:~$ cat /etc…...

活动预告 |【Part1】Microsoft Azure 在线技术公开课:使用 Azure DevOps 和 GitHub 加速开发

课程介绍 通过 Microsoft Learn 免费参加 Microsoft Azure 在线技术公开课&#xff0c;掌握创造新机遇所需的技能&#xff0c;加快对 Microsoft Cloud 技术的了解。参加我们举办的“使用 Azure DevOps 和 GitHub 加速开发”活动&#xff0c;了解迁移到 DevOps 所需的合适工具和…...

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

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

springboot集成阿里云短信服务

springboot集成阿里云短信服务 一.阿里云账号准备 流程:注册阿里云账号>短信服务>新增资质>新建签名>新建模版>申请秘钥>用代码测试 1.注册阿里云账号 2、登录成功后&#xff0c; ① 在首页搜索短信服务 ② 打开第一个搜索结果 ③ 免费开通 ④ 可以根据…...

Redis 实战篇 ——《黑马点评》(上)

《引言》 在进行了前面关于 Redis 基础篇及其客户端的学习之后&#xff0c;开始着手进行实战篇的学习。因内容很多&#xff0c;所以将会分为【 上 中 下 】三篇记录学习的内容与在学习的过程中解决问题的方法。Redis 实战篇的内容我写的很详细&#xff0c;为了能写的更好也付出…...

Redis的生态系统和社区支持

Redis的生态系统和社区支持 1. Redis 生态系统 1.1 Redis核心 Redis 是一个高性能的内存存储系统,支持丰富的数据结构(如字符串、列表、集合、哈希和有序集合)。它的核心提供了: 高性能数据存储:单线程模型支持每秒数百万级别的操作。多种数据结构:适用于多样化场景,如…...

基于C语言从0开始手撸MQTT协议代码连接标准的MQTT服务器,完成数据上传和命令下发响应(华为云IOT服务器)

文章目录 一、前言二、搭建开发环境三、网络编程基础概念科普3.1 什么是网络编程3.2 TCP 和 UDP协议介绍3.3 TCP通信的实现过程 四、Windows下的网络编程相关API介绍4.1 常用的函数介绍4.2 函数参数介绍4.3 编写代码体验网络编程 五、访问华为云IOT服务器创建一个产品和设备5.2…...

什么是 GPT?Transformer 工作原理的动画展示

大家读完觉得有意义记得关注和点赞&#xff01;&#xff01;&#xff01; 目录 1 图解 “Generative Pre-trained Transformer”&#xff08;GPT&#xff09; 1.1 Generative&#xff1a;生成式 1.1.1 可视化 1.1.2 生成式 vs. 判别式&#xff08;译注&#xff09; 1.2 Pr…...

IDEA 编辑器自动识别 Dockerfile 类型高亮和语法提示

在 IDEA 中&#xff0c;如果项目里面的只有一个 Dockerfile文件时&#xff0c;那么此时使用打开这个文件都是 ok 的&#xff0c;支持语法高亮和关键词提示。 如果我们有多个 Dockerfile 时&#xff0c; 比如 A_Dockerfile , B_Dockerfile , C_Dockerfile, 这个时候你会发现 IDE…...

AI知识库与用户行为分析:优化用户体验的深度洞察

在当今数字化时代&#xff0c;用户体验&#xff08;UX&#xff09;已成为衡量产品成功与否的关键指标之一。AI知识库作为智能客服系统的重要组成部分&#xff0c;不仅为用户提供快速、准确的信息检索服务&#xff0c;还通过用户行为分析&#xff0c;为产品优化提供了深度洞察。…...

什么是Redis哨兵机制?

大家好&#xff0c;我是锋哥。今天分享关于【什么是Redis哨兵机制&#xff1f;】面试题。希望对大家有帮助&#xff1b; 什么是Redis哨兵机制&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Redis 哨兵&#xff08;Sentinel&#xff09;机制是 Redis 提…...

JavaScript中如何创建对象

在JavaScript中&#xff0c;创建对象有多种方法。以下是几种常见的方式&#xff1a; 1. 对象字面量 这是最直接和常用的创建对象的方法。使用花括号 {} 包围一组键值对来定义一个对象。 let person {name: "John",age: 30,greet: function() {console.log("…...

2025:OpenAI的“七十二变”?

朋友们&#xff0c;准备好迎接AI的狂欢了吗&#xff1f;&#x1f680; 是不是跟我一样&#xff0c;每天醒来的第一件事就是看看AI领域又有什么新动向&#xff1f; 尤其是那个名字如雷贯耳的 OpenAI&#xff0c;简直就是AI界的弄潮儿&#xff0c;一举一动都牵动着我们这些“AI发…...

Mysql(MGR)和ProxySQL搭建部署-Kubernetes版本

一、Mysql(MGR) 1.1 statefulSet.yaml apiVersion: apps/v1 kind: StatefulSet metadata:labels:app: mysqlname: mysqlnamespace: yihuazt spec:replicas: 3serviceName: mysql-headlessselector:matchLabels:app: mysqltemplate:metadata:labels:app: mysqlspec:affinity:p…...

uni-app 多平台分享实现指南

uni-app 多平台分享实现指南 在移动应用开发中&#xff0c;分享功能是一个非常常见的需求&#xff0c;尤其是在社交媒体、营销活动等场景中。使用 uni-app 进行多平台开发时&#xff0c;可以通过一套代码实现跨平台的分享功能&#xff0c;涵盖微信小程序、H5、App 等多个平台。…...

Windows系统下载、部署Node.js与npm环境的方法

本文介绍在Windows电脑中&#xff0c;下载、安装并配置Node.js环境与npm包管理工具的方法。 Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;其允许开发者使用JavaScript编写命令行工具和服务器端脚本。而npm&#xff08;Node Package Manager&#xff09;则…...

Typora 最新版本下载安装教程(附详细图文)

文章简介 在当今快节奏的信息化时代&#xff0c;简洁高效的写作工具成为了每位内容创作者的必需品。而Typora&#xff0c;这款备受推崇的 Markdown 编辑器&#xff0c;正是为此而生。它采用无缝设计&#xff0c;去除了模式切换、预览窗口等干扰&#xff0c;带来真正的实时预览…...

将一个变量声明为全局变量比如:flag1=false;然后通过jQuery使用js一个方法,将它设置为不可修改

方法 1&#xff1a;使用 Object.defineProperty 通过 Object.defineProperty 将全局变量设置为只读属性。 // 声明全局变量 var flag1 false;// 使用 Object.defineProperty 将其设置为不可修改 Object.defineProperty(window, flag1, {configurable: false, // 不允许删除属…...

找不到qt5core.dll无法运用软件的解决办法

在运行某些软件或游戏时&#xff0c;部分用户会遇到电脑显示由于找不到qt5core.dll&#xff0c;无法继续执行代码的问题&#xff0c;下面就给大家分享几种简单的解决方法&#xff0c;轻松恢复软件正常运行。 导致qt5core.dll缺失的原因 qt5core.dll是 Qt 应用程序框架的一部分…...

集线器,交换机,路由器,mac地址和ip地址知识记录总结

一篇很不错的视频简介 基本功能 从使用方面来说&#xff0c;都是为了网络传输的标识&#xff0c;和机器确定访问对象 集线器、交换机和路由器 常听到路由器和集线器&#xff0c;下面是区别&#xff1a; 集线器 集线器&#xff1a;一个简单的物理扩展接口数量的物理硬件。…...

Javascript算法——回溯算法(组合问题)

相关资料来自《代码随想录》&#xff0c;版权归原作者所有&#xff0c;只是学习记录 回溯 回溯模板 void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择&#xff1a;本层集合中元素&#xff08;树中节点孩子的数量就是集合的大小&#xff09;) {处理节点…...