C语言——贪吃蛇

一. 游戏效果


贪吃蛇


二. 游戏背景

贪吃蛇是久负盛名的游戏,它也和俄罗斯⽅块,扫雷等游戏位列经典游戏的⾏列。
贪吃蛇起源于1977年的投币式墙壁游戏《Blockade》,后移植到各种平台上。具体如下:
起源。1977年,投币式墙壁游戏生产商Gremlin推出了经典的街机游戏《Blockade》,两名玩家要分别控制角色移动,角色会不停长大,而且走过的地方会变成围栏,谁先碰到围栏或碰到自己的身体就算输。
贪吃蛇最初的版本是像素版本,后来又衍生出3D版本、多人对战版本等2 3。
 

三、游戏开发日志

四、知识准备 

本次实现贪吃蛇会使⽤到的⼀些Win32 API知识,那么就学习⼀下

4.1 Win32 API

Windows 这个多作业系统除了协调应⽤程序的执⾏、分配内存、管理资源之外, 它同时也是⼀个很⼤的服务中⼼,调⽤这个服务中⼼的各种服务(每⼀种服务就是⼀个函数),可以帮应⽤程式达到开启 视窗、描绘图形、使⽤周边设备等⽬的,由于这些函数服务的对象是应⽤程序(Application), 所以便 称之为 Application Programming Interface, 简称 API 函数。 WIN32 API也就是Microsoft Windows 32位平台的应⽤程序编程接⼝。

4.2 控制台程序

平常我们运⾏起来的⿊框程序其实就是控制台程序。
我们可以使⽤cmd命令来设置控制台窗⼝的⻓宽,也可以通过命令设置控制台窗⼝的名字:
设置控制台窗⼝的⼤⼩,30⾏,100列,设置控制台窗⼝的名字。
//控制台窗口设置
system("mode con cols=100 lines=35");
system("title 贪吃蛇");

4.3  控制台屏幕上的坐标COORD

COORD 是Windows API中定义的⼀种结构,表⽰⼀个字符在控制台屏幕上的坐标

typedef struct _COORD {
SHORT X;
SHORT Y;
} COORD, *PCOORD;

4.4  GetStdHandle

GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备(标准输⼊、标准输出或标准错误)中取得⼀个句柄(⽤来标识不同设备的数值),使⽤这个句柄可以操作设备。
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

4.5 GetConsoleCursorInfo

检索有关指定控制台屏幕缓冲区的光标⼤⼩和可⻅性的信息
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息

4.5.1  CONSOLE_CURSOR_INFO

这个结构体,包含有关控制台游标的信息
typedef struct _CONSOLE_CURSOR_INFO {
DWORD dwSize;
BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
(1) dwSize,由光标填充的字符单元格的百分⽐。 此值介于1到100之间。 光标外观会变化,范围从完全填充单元格到单元底部的⽔平线条。
(2)  bVisible,游标的可⻅性。 如果光标可⻅,则此成员为 TRUE。
CursorInfo.bVisible = false; //隐藏控制台光标

5.6  SetConsoleCursorInfo

设置指定控制台屏幕缓冲区的光标的⼤⼩和可⻅性。

HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//影藏光标操作
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态

5.7  SetConsoleCursorPosition

设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调 ⽤SetConsoleCursorPosition函数将光标位置设置到指定的位置。

COORD pos = { 10, 5};
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos
SetConsoleCursorPosition(hOutput, pos);
温馨提示:

这里我们可以将SetPos:封装⼀个设置光标位置的函数

//设置光标的坐标
void SetPos(short x, short y)
{
COORD pos = { x, y };
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos
SetConsoleCursorPosition(hOutput, pos);
}

5.8 GetAsyncKeyState

获取按键情况,将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。
返回值是short类型,在上⼀次调⽤ GetAsyncKeyState 函数后,如果 返回的16位的short数据中,最⾼位是1,说明按键的状态是按下,如果最⾼是0,说明按键的状态是抬起;如果最低位被置为1则说明,该按键被按过,否则为0。
如果我们要判断⼀个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1.
我们这里定义一个宏:
 #define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

5.9 宽字符打印

在游戏地图上,我们打印墙体使⽤宽字符:□,打印蛇使⽤宽字符●,打印⻝物使⽤宽字符■
普通的字符是占⼀个字节的,这类宽字符是俩个字节。

5.9.1  setlocale函数

char* setlocale (int category, const char* locale);

setlocale 函数⽤于修改当前地区,可以针对⼀个类项修改,也可以针对所有类项。 setlocale 的第⼀个参数如果是LC_ALL,就会影响所有的类项。

C标准给第⼆个参数仅定义了2种可能取值:"C"和"  "。
在任意程序执⾏开始,都会隐藏式执⾏调⽤:
 setlocale(LC_ALL, "C");

当地区设置为"C"时,库函数按正常⽅式执⾏.

当地区设置为"  "时,这种模式下程序会适应本地环境。⽐如:切换到我们的本地模式后就⽀
持宽字符(汉字)的输出等。
setlocale(LC_ALL, " ");//切换到本地环境

宽字符的打印如下:

#include <stdio.h>
#include<locale.h>
int main() {
setlocale(LC_ALL, "");
wchar_t ch1 = L'●';
wprintf(L"%c\n", ch1);
return 0;
}

五、游戏实现

5.1 游戏逻辑主体

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"
#include<locale.h>
#include<stdio.h>
int main()
{char ch;setlocale(LC_ALL, "");//切换到本地环境,支持宽字符srand((unsigned int)time(NULL));do{Snake snake = { 0 };GameInit(&snake);GameRun(&snake);GameOver(&snake);printf("小垃圾还要再来吗?是:Y,否:N\n");ch = getchar();getchar();} while (ch == 'y' || ch == 'Y');return 0;
}

5.2  游戏初始化实现

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"#define  WALL  L'□'
#define  BODY  L'●'
#define  FOOD  L'█'
void SetPos(short x, short y)
{COORD pos = { x, y };HANDLE hOutput = NULL;//获取标准输出的句柄(⽤来标识不同设备的数值)hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posSetConsoleCursorPosition(hOutput, pos);
}
void  WelcomeToGame()
{SetPos(40, 13);printf("欢迎来到贪吃蛇小游戏");SetPos(42, 22);system("pause");//会显示按任意键继续的信息,按任意键后继续执行下一步system("cls");SetPos(25, 12);printf("↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速\n");SetPos(42, 22);system("pause");//会显示按任意键继续的信息,按任意键后继续执行下一步system("cls");/*getchar();*/}
void CreateMap()
{SetPos(0,0);int i = 0;for(i=0;i<58;i=i+2){wprintf(L"%c", WALL);}SetPos(0, 26);for (i = 0; i < 58; i = i + 2){wprintf(L"%c", WALL);}for (i = 1; i <26; i++){SetPos(0, i);wprintf(L"%c", WALL);}for (i = 1; i < 26; i++){SetPos(56, i);wprintf(L"%c", WALL);}/*getchar();*/
}void InitSnake(Snake* snake)
{//初始化蛇身for (int i = 0; i < 5; i++){SnakeNode* node = (SnakeNode*)malloc(sizeof(SnakeNode));node->x = 24 + 2 * i;node->y = 15;node->next = NULL;if (snake->pSnake== NULL){snake->pSnake= node;}else{node->next = snake->pSnake;snake->pSnake = node;}}SnakeNode* cur = snake->pSnake;while (cur){int x = cur->x;int y = cur->y;SetPos(x, y);wprintf(L"%c", BODY);cur = cur->next;}snake->status = RUN;snake->dir = RIGHT;snake->FoodWeight = 10;snake->pFood = NULL;snake->score = 0;snake->SleepTime = 200;
}
void CreateFood(Snake* snake)
{int x = 0;int y = 0;again:do{x = rand() % 53 + 2;y = rand() % 24 + 1;} while (x % 2 != 0);SnakeNode* cur = snake->pSnake;do{if (x == cur->x && y == cur->y){goto again;}cur = cur->next;} while (cur);SnakeNode* food = (SnakeNode*)malloc(sizeof(SnakeNode));food->x = x;food->y = y;snake->pFood = food;SetPos(x, y);wprintf(L"%c", FOOD);/*getchar();*/}void  GameInit(Snake* snake)
{//控制台窗口设置system("mode con cols=100 lines=35");system("title 贪吃蛇");//隐藏光标HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息CursorInfo.bVisible = false; //隐藏控制台光标SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态//打印欢迎界面WelcomeToGame();//创建地图CreateMap();//初始化贪吃蛇InitSnake(snake);//创建食物CreateFood(snake);
}

5.3 游戏运行实现

void PrintHelpInfo()
{SetPos(60, 13);printf("1. 不能穿墙不能咬到自己\n");SetPos(60, 14);printf("2.↑.↓.←.→分别控制蛇的移动\n");SetPos(60, 15);printf("3. F3为加速,F4为减速\n");SetPos(60, 16);printf("4. ESC-退出,空格-暂停\n");
}void Pause()
{while (1){Sleep(200);if (KEY_PRESS(VK_SPACE) == 1){break;}}}
void EatFood(Snake* snake, SnakeNode* snakenext)
{snakenext->next = snake->pSnake;snake->pSnake = snakenext;SnakeNode* cur = snake->pSnake;while (cur){SetPos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;}free(snake->pFood);snake->pFood = NULL;CreateFood(snake);}
void NoEatFood(Snake* snake, SnakeNode* snakenext)
{snakenext->next = snake->pSnake;snake->pSnake = snakenext;SnakeNode* cur = snake->pSnake;while (cur->next->next){SetPos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;}SetPos(cur->x, cur->y);wprintf(L"%lc", BODY);SetPos(cur->next->x, cur->next->y);printf("  ");free(cur->next);cur->next = NULL;
}void SnakeMove(Snake* snake)
{SnakeNode* snakenext = (SnakeNode*)malloc(sizeof(SnakeNode));if (snake->dir == UP){snakenext->x = snake->pSnake->x;snakenext->y = snake->pSnake->y -1;snakenext->next = NULL;}else if (snake->dir == DOWN){snakenext->x = snake->pSnake->x;snakenext->y = snake->pSnake->y +1;snakenext->next = NULL;}if (snake->dir == LEFT){snakenext->x = snake->pSnake->x-2;snakenext->y = snake->pSnake->y ;snakenext->next = NULL;}if (snake->dir == RIGHT){snakenext->x = snake->pSnake->x + 2;snakenext->y = snake->pSnake->y;snakenext->next = NULL;}//判断下一个节点是否为食物if (snakenext->x == snake->pFood->x && snakenext->y == snake->pFood->y){EatFood(snake,snakenext);//吃食物snake->score += snake->FoodWeight;}else{NoEatFood(snake,snakenext);//不吃食物}//碰撞检测if (snake->pSnake->x == 0 || snake->pSnake->x == 56 || snake->pSnake->y == 0 || snake->pSnake->y == 26){snake->status = KILL_BY_WALL;}SnakeNode* cur = snake->pSnake->next;while (cur){if (cur->x == snake->pSnake->x && cur->y == snake->pSnake->y){snake->status = KILL_BY_SELF;}cur = cur->next;}
}void GameRun(Snake* snake)
{//打印帮助信息PrintHelpInfo();do{SetPos(60, 8);printf("游戏总得分:%5d ", snake->score);SetPos(60, 9);printf("每个食物的分数:%2d ", snake->FoodWeight);if (KEY_PRESS(VK_UP) == 1&&snake->dir!=DOWN){snake->dir = UP;}else if (KEY_PRESS(VK_DOWN) == 1 && snake->dir != UP){snake->dir = DOWN;}else if (KEY_PRESS(VK_RIGHT) == 1 && snake->dir != LEFT){snake->dir = RIGHT;}else if (KEY_PRESS(VK_LEFT) == 1 && snake->dir != RIGHT){snake->dir =LEFT;}else if (KEY_PRESS(VK_F3) == 1){if (snake->SleepTime>=80){snake->SleepTime -= 30;snake->FoodWeight += 2;}}else if (KEY_PRESS(VK_F4) == 1){if (snake->SleepTime<=320){snake->SleepTime += 30;snake->FoodWeight -= 2;}}else if (KEY_PRESS(VK_ESCAPE) == 1){snake->status = OVER;break;}else if (KEY_PRESS(VK_SPACE) == 1){Pause();}SnakeMove(snake);Sleep(snake->SleepTime);//每一帧停0.2s} while (snake->status==RUN);
}

5.4 游戏结束实现

void GameOver(Snake* snake)
{switch (snake->status){case KILL_BY_WALL:SetPos(20, 15);printf("你已经撞墙了,小垃圾!\n");break;case KILL_BY_SELF:SetPos(20, 15);printf("你已经自杀了,小垃圾!\n");break;case  OVER:SetPos(20, 15);printf("你退出游戏了,小垃圾!\n");break;}SetPos(0, 29);}

六、 完整代码

6.1 snake.h

#define _CRT_SECURE_NO_WARNINGS
#include<stdbool.h>
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<time.h>
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
enum direction{UP = 1,  DOWN, LEFT, RIGHT};//蛇的方向
enum game_status{RUN,KILL_BY_WALL,KILL_BY_SELF,OVER};typedef struct  SnakeNode
{int x;int y;struct SnakeNode* next;
}SnakeNode;typedef struct Snake
{SnakeNode* pSnake;//指向贪吃蛇头节点SnakeNode* pFood;//指向食物的节点int  score;int FoodWeight;int SleepTime;enum direction dir;//蛇的方向enum  game_status status;//游戏状态
}Snake;
void  GameInit(Snake*snake);
void  WelcomeToGame();
void CreateMap();
void InitSnake(Snake* snake);
void CreateFood(Snake* snake);
void GameRun(Snake* snake);
void PrintHelpInfo();
void Pause();
void SnakeMove(Snake* snake);void EatFood(Snake* snake,SnakeNode*snakenext);
void NoEatFood(Snake* snake, SnakeNode* snakenext);
void GameOver(Snake* snake);

6.2 snake.c

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"#define  WALL  L'□'
#define  BODY  L'●'
#define  FOOD  L'█'
void SetPos(short x, short y)
{COORD pos = { x, y };HANDLE hOutput = NULL;//获取标准输出的句柄(⽤来标识不同设备的数值)hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posSetConsoleCursorPosition(hOutput, pos);
}
void  WelcomeToGame()
{SetPos(40, 13);printf("欢迎来到贪吃蛇小游戏");SetPos(42, 22);system("pause");//会显示按任意键继续的信息,按任意键后继续执行下一步system("cls");SetPos(25, 12);printf("↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速\n");SetPos(42, 22);system("pause");//会显示按任意键继续的信息,按任意键后继续执行下一步system("cls");/*getchar();*/}
void CreateMap()
{SetPos(0,0);int i = 0;for(i=0;i<58;i=i+2){wprintf(L"%c", WALL);}SetPos(0, 26);for (i = 0; i < 58; i = i + 2){wprintf(L"%c", WALL);}for (i = 1; i <26; i++){SetPos(0, i);wprintf(L"%c", WALL);}for (i = 1; i < 26; i++){SetPos(56, i);wprintf(L"%c", WALL);}/*getchar();*/
}void InitSnake(Snake* snake)
{//初始化蛇身for (int i = 0; i < 5; i++){SnakeNode* node = (SnakeNode*)malloc(sizeof(SnakeNode));node->x = 24 + 2 * i;node->y = 15;node->next = NULL;if (snake->pSnake== NULL){snake->pSnake= node;}else{node->next = snake->pSnake;snake->pSnake = node;}}SnakeNode* cur = snake->pSnake;while (cur){int x = cur->x;int y = cur->y;SetPos(x, y);wprintf(L"%c", BODY);cur = cur->next;}snake->status = RUN;snake->dir = RIGHT;snake->FoodWeight = 10;snake->pFood = NULL;snake->score = 0;snake->SleepTime = 200;
}
void CreateFood(Snake* snake)
{int x = 0;int y = 0;again:do{x = rand() % 53 + 2;y = rand() % 24 + 1;} while (x % 2 != 0);SnakeNode* cur = snake->pSnake;do{if (x == cur->x && y == cur->y){goto again;}cur = cur->next;} while (cur);SnakeNode* food = (SnakeNode*)malloc(sizeof(SnakeNode));food->x = x;food->y = y;snake->pFood = food;SetPos(x, y);wprintf(L"%c", FOOD);/*getchar();*/}void  GameInit(Snake* snake)
{//控制台窗口设置system("mode con cols=100 lines=35");system("title 贪吃蛇");//隐藏光标HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息CursorInfo.bVisible = false; //隐藏控制台光标SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态//打印欢迎界面WelcomeToGame();//创建地图CreateMap();//初始化贪吃蛇InitSnake(snake);//创建食物CreateFood(snake);
}void PrintHelpInfo()
{SetPos(60, 13);printf("1. 不能穿墙不能咬到自己\n");SetPos(60, 14);printf("2.↑.↓.←.→分别控制蛇的移动\n");SetPos(60, 15);printf("3. F3为加速,F4为减速\n");SetPos(60, 16);printf("4. ESC-退出,空格-暂停\n");
}void Pause()
{while (1){Sleep(200);if (KEY_PRESS(VK_SPACE) == 1){break;}}}
void EatFood(Snake* snake, SnakeNode* snakenext)
{snakenext->next = snake->pSnake;snake->pSnake = snakenext;SnakeNode* cur = snake->pSnake;while (cur){SetPos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;}free(snake->pFood);snake->pFood = NULL;CreateFood(snake);}
void NoEatFood(Snake* snake, SnakeNode* snakenext)
{snakenext->next = snake->pSnake;snake->pSnake = snakenext;SnakeNode* cur = snake->pSnake;while (cur->next->next){SetPos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;}SetPos(cur->x, cur->y);wprintf(L"%lc", BODY);SetPos(cur->next->x, cur->next->y);printf("  ");free(cur->next);cur->next = NULL;
}void SnakeMove(Snake* snake)
{SnakeNode* snakenext = (SnakeNode*)malloc(sizeof(SnakeNode));if (snake->dir == UP){snakenext->x = snake->pSnake->x;snakenext->y = snake->pSnake->y -1;snakenext->next = NULL;}else if (snake->dir == DOWN){snakenext->x = snake->pSnake->x;snakenext->y = snake->pSnake->y +1;snakenext->next = NULL;}if (snake->dir == LEFT){snakenext->x = snake->pSnake->x-2;snakenext->y = snake->pSnake->y ;snakenext->next = NULL;}if (snake->dir == RIGHT){snakenext->x = snake->pSnake->x + 2;snakenext->y = snake->pSnake->y;snakenext->next = NULL;}//判断下一个节点是否为食物if (snakenext->x == snake->pFood->x && snakenext->y == snake->pFood->y){EatFood(snake,snakenext);//吃食物snake->score += snake->FoodWeight;}else{NoEatFood(snake,snakenext);//不吃食物}//碰撞检测if (snake->pSnake->x == 0 || snake->pSnake->x == 56 || snake->pSnake->y == 0 || snake->pSnake->y == 26){snake->status = KILL_BY_WALL;}SnakeNode* cur = snake->pSnake->next;while (cur){if (cur->x == snake->pSnake->x && cur->y == snake->pSnake->y){snake->status = KILL_BY_SELF;}cur = cur->next;}
}void GameRun(Snake* snake)
{//打印帮助信息PrintHelpInfo();do{SetPos(60, 8);printf("游戏总得分:%5d ", snake->score);SetPos(60, 9);printf("每个食物的分数:%2d ", snake->FoodWeight);if (KEY_PRESS(VK_UP) == 1&&snake->dir!=DOWN){snake->dir = UP;}else if (KEY_PRESS(VK_DOWN) == 1 && snake->dir != UP){snake->dir = DOWN;}else if (KEY_PRESS(VK_RIGHT) == 1 && snake->dir != LEFT){snake->dir = RIGHT;}else if (KEY_PRESS(VK_LEFT) == 1 && snake->dir != RIGHT){snake->dir =LEFT;}else if (KEY_PRESS(VK_F3) == 1){if (snake->SleepTime>=80){snake->SleepTime -= 30;snake->FoodWeight += 2;}}else if (KEY_PRESS(VK_F4) == 1){if (snake->SleepTime<=320){snake->SleepTime += 30;snake->FoodWeight -= 2;}}else if (KEY_PRESS(VK_ESCAPE) == 1){snake->status = OVER;break;}else if (KEY_PRESS(VK_SPACE) == 1){Pause();}SnakeMove(snake);Sleep(snake->SleepTime);//每一帧停0.2s} while (snake->status==RUN);
}void GameOver(Snake* snake)
{switch (snake->status){case KILL_BY_WALL:SetPos(20, 15);printf("你已经撞墙了,小垃圾!\n");break;case KILL_BY_SELF:SetPos(20, 15);printf("你已经自杀了,小垃圾!\n");break;case  OVER:SetPos(20, 15);printf("你退出游戏了,小垃圾!\n");break;}SetPos(0, 29);}

6.3 text.c

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"
#include<locale.h>
#include<stdio.h>
int main()
{char ch;setlocale(LC_ALL, "");//切换到本地环境,支持宽字符srand((unsigned int)time(NULL));do{Snake snake = { 0 };GameInit(&snake);GameRun(&snake);GameOver(&snake);printf("小垃圾还要再来吗?是:Y,否:N\n");ch = getchar();getchar();} while (ch == 'y' || ch == 'Y');return 0;
}

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

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

相关文章

UE地形系统材质混合实现和Shader生成分析(UE5 5.2)

前言 随着电脑和手机硬件性能越来越高&#xff0c;游戏越来越追求大世界&#xff0c;而大世界非常核心的一环是地形系统&#xff0c;地形系统两大构成因素&#xff1a;高度和多材质混合&#xff0c;此篇文章介绍下UE4/UE5 地形的材质混合方案----基于WeightMap混合。 材质层 …

4 Paimon数据湖之Hive Catalog的使用

更多Paimon数据湖内容请关注&#xff1a;https://edu.51cto.com/course/35051.html Paimon提供了两种类型的Catalog&#xff1a;Filesystem Catalog和Hive Catalog。 Filesystem Catalog&#xff1a;会把元数据信息存储到文件系统里面。Hive Catalog&#xff1a;则会把元数据…

【Excel】函数sumif范围中符合指定条件的值求和

SUMIF函数是Excel常用函数。使用 SUMIF 函数可以对报表范围中符合指定条件的值求和。 Excel中sumif函数的用法是根据指定条件对若干单元格、区域或引用求和。 sumif函数语法是&#xff1a;SUMIF(range&#xff0c;criteria&#xff0c;sum_range) sumif函数的参数如下&#xff…

java 继承和多态 (图文搭配,万字详解!!)

目录 1.继承 1.1 为什么需要继承 1.2 继承概念 1.3 继承的语法 1.4 父类成员访问 1.4.1 子类中访问父类的成员变量 1.4.2 子类中访问父类的成员方法 1.5 super关键字 1.6 子类构造方法 1.7 super和this 1.8 再谈初始化 1.9 protected 关键字 1.10 继承方式 1.11 f…

记录一次某某虚拟机的逆向

导语 学了一段时间的XPosed&#xff0c;发现XPosed真的好强&#xff0c;只要技术强&#xff0c;什么操作都能实现... 这次主要记录一下我对这款应用的逆向思路 apk检查 使用MT管理器检查apk的加壳情况 发现是某数字的免费版本 直接使用frida-dexdump 脱下来后备用 应用分…

Linux如何修改主机名(hostname)(亲测可用)

文章目录 背景Linux如何修改主机名&#xff08;hostname&#xff09;方法方法1. 使用 hostnamectl 命令示例 2. 编辑 /etc/hostname 文件注意事项 背景 我创建虚拟机的时候没设置主机名&#xff0c;现在显示localhost&#xff0c;有点尴尬&#x1f605;&#xff1a; 需要重新设…

虚幻5.3打包Windows失败

缺失UnrealGame二进制文件。 必须使用集成开发环境编译该UE项目。或者借助虚幻编译工具使用命令行命令进行编译 解决办法&#xff1a; 1.依次点击平台-项目启动程序 2.点击后面的按钮进行设置 3.稍等后&#xff0c;打包后的程序即可运行&#xff0c;之后就可以愉快的打包了

抖音短视频账号矩阵系统、短视频矩阵源码+无人直播源码开发可打包

抖音短视频账号矩阵系统、短视频矩阵源码无人直播源码开发可打包 矩阵系统源码主要有三种框架&#xff1a;Spring、Struts和Hibernate。Spring框架是一个全栈式的Java应用程序开发框架&#xff0c;提供了IOC容器、AOP、事务管理等功能。Struts框架是一个MVC架构的Web应用程序框…

推荐系统笔记--Swing模型的原理

1--Swing模型的引入 在 Item CF 召回中&#xff0c;物品的相似度是基于其受众的交集来衡量的&#xff0c;但当受众的交集局限在一个小圈子时&#xff0c;就会误将两个不相似的物品定义为相似&#xff1b; Swing 模型引入用户的重合度来判断两个用户是否属于一个小圈子&#xff…

基于springboot乐器视频学习网站设计与实现(源码齐全可用)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。你想解决的问题&#xff0c;今天给大家介绍…

虚拟机CentOS 8 重启后不能上网

情况说明&#xff1a;原本虚拟机是可以上网的&#xff0c;然后嘚一下&#xff0c;重启后&#xff0c;连接不上网络&#xff0c;完了&#xff0c;上网查找一堆质料&#xff0c;我的连接方式是桥接模式&#xff08;复制物理网络连接状态&#xff09;。 好&#xff0c;有人说是vmn…

02MyBatisPlus条件构造器,自定义SQL,Service接口

一、条件构造器 1.MyBatis支持各种复杂的where条件&#xff0c;满足开发的需求 Wrapper是条件构造器&#xff0c;构建复杂的where查询 AbstractWrapper有构造where条件的所有方法&#xff0c;QueryWrapper继承后并有自己的select指定查询字段。UpdateWrapper有指定更新的字段的…

用 winget 在 Windows 上安装 kubectl

目录 kubectl 是什么&#xff1f; 安装 kubectl 以管理员身份打开 PowerShell 使用 winget 安装 kubectl 测试一下&#xff0c;确保安装的是最新版本 导航到你的 home 目录&#xff1a; 验证 kubectl 配置 kubectl 是什么&#xff1f; kubectl 是 Kubernetes 的命令行工…

STM32 寄存器配置笔记——GPIO配置输出

一、概述 本文主要介绍GPIO 作为输出时的寄存器配置。包括时钟配置&#xff0c;输出模式配置。以STM32F10xxx系列为例&#xff0c;配置PA8、PD2端口作为输出&#xff0c;输出高/低电平。 二、配置流程 1&#xff09;GPIO外设时钟 通过查找STM32F10xxx中文参考手册得知&#xf…

江西开放大学引领学习新时代:电大搜题助力学子迈向成功

江西开放大学&#xff08;简称江西电大&#xff09;一直以来致力于为学子提供灵活便捷的学习服务。近年来&#xff0c;携手电大搜题微信公众号&#xff0c;江西开放大学以其卓越的教学质量和创新的教学手段&#xff0c;为广大学子开启了一扇通向成功的大门。 作为一家知名的远…

asp.net 在线音乐网站系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net 在线音乐网站系统是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语言 开发 asp.net 在线音乐网站系统1 应用…

数据结构—内部排序(上)

文章目录 8.内部排序(上)(1).排序基础#1.为什么是内部排序#2.排序的稳定性 (2).冒泡排序#1.算法思想#2.代码实现#3.稳定性与时间复杂度分析 (3).选择排序#1.算法思想#2.代码实现#3.稳定性与时间复杂度分析 (4).插入排序#1.算法思想#2.代码实现#3.稳定性与时间复杂度分析 (5).希…

advanced-css: No.1

本套教程学习来自视频&#xff1a;https://www.bilibili.com/video/BV1n94y1o7yS/?p7&spm_id_frompageDriver&vd_sourceb79be8283df9418cb45941cc0bd583c6 案例 实现效果图 代码 HTML: <!DOCTYPE html> <html lang"en"><head><meta c…

Docker - 安装

Docker安装 Docker的基本组成 镜像&#xff08;image&#xff09;: ​ Docker镜像就好比是一个模板&#xff0c;可以通过这个模板来创建容器服务&#xff0c;tomcat镜像 -> run -> tomcat01容器&#xff08;提供服务器&#xff09;&#xff0c;通过这个镜像可以创建多个…

Git基本概念和使用方式

Git 是一种版本控制系统&#xff0c;用于管理文件版本的变化。以下是其基本概念和使用方式&#xff1a; 仓库&#xff08;repository&#xff09;&#xff1a;Git 存储代码的地方&#xff0c;可以理解为一个项目的文件夹。提交&#xff08;commit&#xff09;&#xff1a;Git …