C语言实现贪吃蛇项目(2)

news2024/10/5 18:26:49

先来看看效果:

20240420_212115


文章目录:

  • 3.项目实现
  • 3.0宽字符的打印
    • 3.01本地化操作
      • setlocale函数
      • 宽字符的打印
  • 3.1贪吃蛇结构的创建和维护
    • 3.11贪吃蛇结构的创建
    • 3.12贪吃蛇的维护
  • 3.2初始化游戏
    • 3.21.打印欢迎界面、隐藏光标和设置窗口大小
    • 3.22.绘制地图
    • 3.23.创建蛇
    • 3.24.创建食物
  • 3.3 游戏运行逻辑
    • 3.31打印游戏旁的提示
    • 3.32按键检测
    • 3.33蛇的移动
    • 3.34检测是否撞墙或自己
  • 3.4 游戏结束(善后工作)
  • 4.项目代码

3.项目实现

3.0宽字符的打印

3.01本地化操作

先要进行本地化,然后才能进行宽字符的打印
<locale.h>提供的函数⽤于控制C标准库中对于不同的地区会产⽣不⼀样⾏为的部分。
在标准中,依赖地区的部分有以下⼏项:
• 数字量的格式
• 货币量的格式
• 字符集
• ⽇期和时间的表⽰形式
在这里插入图片描述
每个类项的详细说明,请参考:https://learn.microsoft.com/zh-cn/cpp/c-runtime-library/reference/setlocale-wsetlocale?view=msvc-170

setlocale函数

在这里插入图片描述

setlocale 函数⽤于修改当前地区,可以针对⼀个类项修改,也可以针对所有类项
C标准给第⼆个参数仅定义了2种可能取值:“C”(正常模式)和""(本地模式)
我们来看看示例:
在这里插入图片描述
上面是C语言默认的环境,下面就是我们适配本地的环境了

宽字符的打印

宽字符的字⾯量必须加上前缀“L”,否则C语⾔会把字⾯量当作窄字符类型处理。前缀“L”在单引号前⾯,表⽰宽字符,对应wprintf() 的占位符为 %lc ;在双引号前⾯,表⽰宽字符串,对应wprintf() 的占位符为%ls
在这里插入图片描述
所以在进行坐标判断是要注意是否为2的倍数

3.1贪吃蛇结构的创建和维护

3.11贪吃蛇结构的创建

我们的贪吃蛇使用链表进行维护我们先进行声明
在这里插入图片描述这里将结构体指针重命名为pSnakeNode方便后续书写

3.12贪吃蛇的维护

我们创建一个结构体来方便维护我们的程序(这里有枚举的方法)在这里插入图片描述

然后我们创建贪吃蛇
在这里插入图片描述

3.2初始化游戏

在这里插入图片描述
初始化游戏我们要做什么:
1.打印欢迎界面、隐藏光标和设置窗口大小
2.绘制地图
3.创建蛇
4.创建食物

3.21.打印欢迎界面、隐藏光标和设置窗口大小

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/0a07334e9292
这些上一篇文章已经写过了就不多赘述了在这里插入图片描述
光标定位也是前一篇写过的,直接拿过来使用即可
在这里插入图片描述
这里就是定位光标位置然后打印信息
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后就是清屏然后在打印
在这里插入图片描述

3.22.绘制地图

在这里插入图片描述

这里创建一个27行58列的地图,也可以根据自己的情况来
在这里插入图片描述
这里要注意光标的定位,通过循环就可以将地图绘制出来了
在这里插入图片描述

3.23.创建蛇

在这里插入图片描述
循环申请空间然后进行初始化,这里是从尾巴向头部申请空间的(蛇初始向右移动)
在这里插入图片描述
然后用头插法将其串起来
在这里插入图片描述
在这里插入图片描述
写成代码就是上面的方式
接着就是将蛇的身体打印出来,用循环遍历就行
在这里插入图片描述
在这里插入图片描述

并且将蛇的属性进行初始的设置
在这里插入图片描述

3.24.创建食物

由于食物要放到指针中,所以要传参数
在这里插入图片描述
要注意:
x坐标必须是2的倍数
⻝物的坐标不能和蛇⾝每个节点的坐标重复
在这里插入图片描述
在这里插入图片描述
接下来判断是否为2的倍数
在这里插入图片描述
判断x和y不能和蛇的身体冲突
在这里插入图片描述
申请食物的空间,并将其打印出来在这里插入图片描述

在这里插入图片描述

3.3 游戏运行逻辑

在这里插入图片描述

3.31打印游戏旁的提示

在这里插入图片描述
在这里插入图片描述

3.32按键检测

这里暂停为循环睡眠,然后在输入空格跳出循环
加减速要注意有判断,不能一直减速
在这里插入图片描述

3.33蛇的移动

要不断检测按键来移动蛇的位置
由于要改变游戏的状态所以也要传指针
在这里插入图片描述
我们的移动采用的方法是:将未接点释放,头节点进行头插
在这里插入图片描述
先创建节点,然后将移动的下一个坐标写出来,要注意x一次加2

检测下一个坐标是否是食物
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
然后是这两个函数的处理:
在这里插入图片描述
在这里插入图片描述
用头插法将食物节点插入,释放下一个位置的节点,打印蛇,最后创建食物
在这里插入图片描述
将下一个节点头插到蛇头,然后将尾接点用空格覆盖掉,最后将尾接点释放掉
在这里插入图片描述

3.34检测是否撞墙或自己

在这里插入图片描述
在这里插入图片描述
检测头的横纵坐标是否为墙
在这里插入图片描述
检测头的坐标是否为身体

3.4 游戏结束(善后工作)

打印结束信息:
在这里插入图片描述
在这里插入图片描述
然后释放节点
在这里插入图片描述
接下来就可以将游戏循环起来
在这里插入图片描述

这里要注意输入y后按回车getchar会读取\n,如果想将\n清理掉,就要在调用getchar
在更改一下:
在这里插入图片描述

4.项目代码

snake.h

#pragma once
#include <locale.h>
#include <stdio.h>
#include <Windows.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
//wchar_t ch1 = L'●';
//wchar_t ch2 = L'★';

void SetPos(short x, short y);
//类型的声明
//蛇身的节点类型
typedef struct SnakeNode
{
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode;
typedef struct SnakeNode* pSnakeNode;

enum DIRECTION //记录方向(枚举)
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
};
//蛇的状态(正常,撞墙,撞到自己,正常退出)
enum GAME_STATUS
{
	OK, //正常
	KILL_BY_WALL, //撞墙
	KILL_BY_SELF, //撞到自己
	END_NORMAL  //正常退出
};
typedef struct Snake
{
	pSnakeNode _pSnake;//指向蛇头变量
	pSnakeNode _pFood;//指向食物的指针
	enum DIRECTION _dir;//记录方向(枚举)
	enum GAME_STATUS _status;//游戏的状态
	int _food_weight; //食物的分数
	int _score;//总成绩
	int _sleep_time;//休息时间(时间越短,速度越快)
}Snake;
typedef Snake* pSnake;

void GameStart(pSnake ps);//初始化游戏

void WelcomeToGame();//欢迎界面

void GreateMap();//绘制地图

void InitSnake(pSnake ps);//创建蛇

void CreateFood(pSnake ps); //创建食物

void GameRun(pSnake ps);//游戏运行逻辑

void SnackMove(pSnake ps);//蛇的移动

int NextIsFood(pSnakeNode pn, pSnake ps);//检测下一个坐标是否是食物

void EatFood(pSnakeNode pn, pSnake ps);
void NoFood(pSnakeNode pn, pSnake ps);

//检测是否撞墙或自己
void KillByWall(pSnake ps);
void KillBySelf(pSnake ps);

void GameEend(pSnake ps);

snake.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "snake.h"

void SetPos(short x, short y)
{
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	COORD pos = { x,y };
	SetConsoleCursorPosition(houtput, pos);
}

void WelcomeToGame()
{
	SetPos(40, 13);
	printf("欢迎来到贪吃蛇小游戏\n");
	SetPos(42, 20);
	system("pause");
	system("cls");
	SetPos(27, 13);
	wprintf(L"用↑.↓.←.→分别控制蛇的移动,F3为加速,F4为减速\n");
	SetPos(27, 14);
	printf("加速能获得更高的分数\n");
	SetPos(40, 20);
	system("pause");
	system("cls");
}

#define WALL L'□'
#define BODY L'●'
void GreateMap()
{
	//上
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", WALL);
	}
	//下
	SetPos(0, 26);
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	for (int i = 1; i < 26; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	//右
	for (int i = 1; i < 26; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}
	
}

#define POS_X 24
#define POS_Y 5
void InitSnake(pSnake ps)
{
	pSnakeNode cur = NULL;
	for (int i = 0; i < 5; i++)
	{
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("malloc");
			exit(1);
		}
		cur->next = NULL;
		cur->x = POS_X + i * 2;
		cur->y = POS_Y;

		//头插法将其串起来
		if (ps->_pSnake == NULL)
		{
			ps->_pSnake = cur;
		}
		else
		{
			cur->next = ps->_pSnake;
			ps->_pSnake = cur;
		}
	}

	cur = ps->_pSnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	
	//设置蛇的属性
	ps->_dir = RIGHT;
	ps->_score = 0;
	ps->_food_weight = 10;
	ps->_sleep_time = 200;//毫秒
	ps->_status = OK;
}

#define FOOD L'★'
void CreateFood(pSnake ps)
{
	int x = 0;
	int y = 0;
again:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 24 + 1;
	} while (x % 2 != 0);

	//x和y不能和蛇的身体冲突
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		if (x == cur->x && y == cur->y)
		{
			goto again;
		}
		cur = cur->next;
	}
	//创建食物
	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pFood == NULL)
	{
		perror ("pFood::malloc");
		exit(1);
	}
	pFood->x = x;
	pFood->y = y;
	pFood->next = NULL;
	
	SetPos(x, y);
	wprintf(L"%lc", FOOD);
	ps->_pFood = pFood;
}

void GameStart(pSnake ps)
{
	//1.打印欢迎界面、隐藏光标和设置窗口大小

	//设置窗口大小
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");
	//隐藏光标
	//获得标准输出设备
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//创建光标信息结构体
	CONSOLE_CURSOR_INFO cursor_info = { 0 };

	//获取光标的信息,放在cursor_info中
	GetConsoleCursorInfo(houtput, &cursor_info);

	//修改光标占比
	//cursor_info.dwSize = 100;
	cursor_info.bVisible = false;

	//设置光标信息
	SetConsoleCursorInfo(houtput, &cursor_info);

	//1.打印欢迎界面
	WelcomeToGame();
	//2.绘制地图
	GreateMap();
	//3.创建蛇
	InitSnake(ps);
	//4.创建食物
	CreateFood(ps);
}


void PrintHelpInfo()
{
	SetPos(64, 15);
	wprintf(L"%ls\n", L"不能穿墙,不能咬到自己");
	SetPos(64, 16);
	wprintf(L"%ls\n", L"用↑.↓.←.→分别控制蛇的移动.");
	SetPos(64, 17);
	wprintf(L"%ls\n", L"F3 为加速,F4为减速");
	SetPos(64, 18);
	wprintf(L"%ls\n", L"ESC :退出游戏.空格:暂停游戏.");
}

#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&1)?1:0)

void Pause()
{
	while (1)
	{
		Sleep(200);
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
	}
}

int NextIsFood(pSnakeNode pn, pSnake ps)
{
	assert(ps->_pFood);
	return (pn->x == ps->_pFood->x && pn->y == ps->_pFood->y);
}

void EatFood(pSnakeNode pn, pSnake ps)
{
	//用头插法将食物节点插入
	ps->_pFood->next = ps->_pSnake;
	ps->_pSnake = ps->_pFood;

	//释放下一个位置的节点
	free(pn);
	pn = NULL;

	//打印蛇
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	ps->_score += ps->_food_weight;
	CreateFood(ps);
}

void NoFood(pSnakeNode pn, pSnake ps)
{
	//将下一个节点头插到蛇头
	pn->next = ps->_pSnake;
	ps->_pSnake = pn;
	
	//最后将尾接点用空格覆盖掉
	pSnakeNode cur = ps->_pSnake;
	while (cur->next->next != NULL)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	SetPos(cur->next->x, cur->next->y);
	printf("  ");

	//将未接点释放掉
	free(cur->next);
	cur->next = NULL;
}

void KillByWall(pSnake ps)
{
	if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 ||
		ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
	{
		ps->_status = KILL_BY_WALL;
	}
}

void KillBySelf(pSnake ps)
{
	pSnakeNode cur = ps->_pSnake->next;
	while (cur)
	{
		if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
		{
			ps->_status = KILL_BY_SELF;
			break;
		}
		cur = cur->next;
	}
}
void SnackMove(pSnake ps)
{
	//创建一个节点来表示蛇即将到的下一个节点
	pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pNextNode == NULL)
	{
		perror("SnackMove()::malloc()");
		exit(1);
	}
	switch (ps->_dir)
	{
	case UP:
		pNextNode->x = ps->_pSnake->x;
		pNextNode->y = ps->_pSnake->y - 1;
		break;
	case DOWN:
		pNextNode->x = ps->_pSnake->x;
		pNextNode->y = ps->_pSnake->y + 1;
		break;
	case LEFT:
		pNextNode->x = ps->_pSnake->x - 2;
		pNextNode->y = ps->_pSnake->y;
		break;
	case RIGHT:
		pNextNode->x = ps->_pSnake->x + 2;
		pNextNode->y = ps->_pSnake->y;
		break;
	}
	//检测下一个坐标是否是食物
	if (NextIsFood(pNextNode,ps))
	{
		EatFood(pNextNode,ps);
	}
	else
	{
		NoFood(pNextNode, ps);
	}

	//检测蛇是否撞到墙或自己
	KillByWall(ps);
	KillBySelf(ps);
}


void GameRun(pSnake ps)
{
	//打印帮助信息
	PrintHelpInfo();
	do
	{
		SetPos(64, 10);
		printf("总分数:%d", ps->_score);
		SetPos(64, 11);
		printf("当前食物分数:%2d", ps->_food_weight);
		if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)
		{
			ps->_dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)
		{
			ps->_dir = DOWN;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
		{
			ps->_dir = RIGHT;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
		{
			ps->_dir = LEFT;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			//暂停
			Pause();
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			//正常退出
			ps->_status = END_NORMAL;
		}
		else if (KEY_PRESS(VK_F4))
		{
			//加速
			if (ps->_sleep_time > 80)
			{
				ps->_sleep_time -= 30;
				ps->_food_weight += 2;
			}
		}
		else if (KEY_PRESS(VK_F3))
		{
			//减速
			if (ps->_food_weight > 2)
			{
				ps->_sleep_time += 30;
				ps->_food_weight -= 2;
			}
		}
		SnackMove(ps);//蛇的移动
		Sleep(ps->_sleep_time);
	} while (ps->_status == OK);

}

void GameEend(pSnake ps)
{
	SetPos(24, 12);
	switch (ps->_status)
	{
	case END_NORMAL:
		printf("您主动结束游戏\n");
		break;
	case KILL_BY_WALL:
		printf("您撞到了墙上,游戏结束\n");
		break;
	case KILL_BY_SELF:
		printf("您撞到了自己,游戏结束\n");
		break;
	}
	
	//释放节点
	pSnakeNode cur = ps->_pSnake;
	pSnakeNode del = ps->_pSnake;
	while (cur)
	{
		del = cur;
		cur = cur->next;
		free(del);
	}
	SetPos(0,27);
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "snake.h"

void Test()
{
	int ch = 0;
	do
	{
		system("cls");
		//创建贪吃蛇
		Snake snake = { 0 };
		//初始化游戏
		GameStart(&snake);

		运行游戏
		GameRun(&snake);
		结束游戏(善后工作)
		GameEend(&snake);
		SetPos(20, 15);
		printf("再来一局吗?(y/n):");
		ch = getchar();
		while (getchar() != '\n');
	} while (ch == 'Y' || ch == 'y');
	SetPos(0, 27);
}

int main()
{
	setlocale(LC_ALL, "");//适配本地化
	srand((unsigned int)time(NULL));
	Test();//游戏的测试逻辑
	return 0;
}


好了本篇文章就结束了,希望大家可以动手自己去写一下,还是会有一些收获的

到这里C语言的语法部分就差不多了,后续我会继续写C语言数据结构的文章了,C语言语法部分看看可能会补充一部分,还是看看时间吧,大家加油!!!

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

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

相关文章

java中File类和输入输出流的用法

目录 针对文件系统进行操作 针对文件内容进行操作 java针对文件操作可以分为两种&#xff1a;1&#xff09;针对文件系统进行操作&#xff0c;如创建文件&#xff0c;删除文件&#xff0c;创建目录&#xff0c;重命名文件等。 2&#xff09;针对文件内容进行操作&#xff0c…

SQLite R*Tree 模块(三十三)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLite FTS3 和 FTS4 扩展(三十二) 下一篇:SQLite轻量级会话扩展&#xff08;三十四&#xff09; 1. 概述 R-Tree 是一个特殊的 专为执行范围查询而设计的索引。R-树最常见的是 用于地理空间系统&#xff0c;其中…

第 2 章:FFmpeg简介

2.1 历史 历史 一些相关术语介绍&#xff1a; 容器&#xff08;Container&#xff09;格式&#xff1a;一种文件封装格式&#xff0c;里边主要包含了流&#xff0c;一般会使用一个特定的后缀名标识&#xff0c;例如.mov、.avi、.wav等。流 &#xff08;Stream&#xff09;&am…

稀碎从零算法笔记Day54-LeetCode:39. 组合总和

题型&#xff1a;数组、树、DFS、回溯 链接&#xff1a;39. 组合总和 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数…

Tomcat核心组件深度解析

Server组件 Service组件 连接器Connector组件 容器Container组件

C语言系列文章 | 初识C语言

首先分为几个方面来和各位读者介绍C语言&#xff0c;并在之后的学习过程中不断地和各位读者去分享我学习的经历。 坐好&#xff0c;发车咯~目录如下&#xff1a;1. C语言是什么&#xff1f;2. C语言的历史和辉煌3. 编译器的选择VS20224. VS项目和源⽂件、头⽂件介绍5. 第⼀…

前端css中table表格的属性使用

前端css中table表格的属性使用 一、前言二、常见的表格属性1.边框的样式2.布局和对齐3.间距和填充4.背景和颜色5.字体的样式6.边框的圆角 三、简单的表格&#xff0c;例子11.源码12.源码1效果截图 四、给表格添加动画效果&#xff0c;例子21.源码22.源码2的运行效果 五、结语六…

Vue 指令、计算属性、侦听器

目录 指令 指令修饰符 按键修饰符 ​编辑 v-model修饰符 事件修饰符 v-bind对于样式操作的增强 操作class 对象 数组 操作style v-model应用于其他表单元素 computed计算属性 概念 基础语法 ​编辑 计算属性vs方法 computed计算属性 作用 语法 缓存特性 m…

图像处理的魔法师:Pillow 库探秘

文章目录 图像处理的魔法师&#xff1a;Pillow 库探秘第一部分&#xff1a;背景介绍第二部分&#xff1a;库是什么&#xff1f;第三部分&#xff1a;如何安装这个库&#xff1f;第四部分&#xff1a;库函数使用方法第五部分&#xff1a;场景应用第六部分&#xff1a;常见Bug及解…

Unity射击游戏开发教程:(2)实例化和销毁游戏对象

现在我们有了“飞船”,我们可以在屏幕上移动它,现在我们需要发射一些激光!与宇宙飞船一样,我们将让事情变得简单并使用 Unity 自己的基本形状。舱体的效果很好,所以我们来创建一个。 我们保存了有关位置、旋转和缩放的信息。我们想要缩小这个对象,假设每个轴上缩小到 0.2…

使用PHP开发体育赛事直播平台,有这些缺点和优点

"东莞梦幻网络科技"作为体育直播平台开发领域的领导者&#xff0c;选择使用PHP开发体育赛事直播平台的现成源码&#xff0c;为什么会选择该语言&#xff0c;背后的选择理由可以从该技术的优点和缺点中找到答案。 一、优点1、易学易用与快速开发&#xff1a;PHP语言语…

git远程分支强制覆盖本地分支

目录 第一章、问题1.1&#xff09;报错提示&#xff1a;没有为分支主机或分支配置被跟踪的分支1.2&#xff09;报错分析与解决 第二章、2.1&#xff09;本地误删代码后想要git pull拉取覆盖&#xff1a;失败2.2&#xff09;报错分析和解决 友情提醒: 先看文章目录&#xff0c;…

Java多线程-API

常见API一览 Thread t1 new Thread(() -> {System.out.println("我是线程t1");System.out.println("Hello, World!"); }); t1.start(); // 获取线程名称 getName() // 线程名称默认是Thread-0, Thread-1, ... System.out.println(t1.getName());// 通过…

SpringCloud系列(8)--将服务提供者Provider注册进Eureka Server

前言&#xff1a;上一章节我们介绍了Eureka服务端的安装与配置&#xff0c;本章节则介绍关于微服务如何入职Eureka Server Eureka架构原理图 1、修改provider-payment8001子模块的pom.xml文件&#xff0c;引入Eureka Clinet的依赖&#xff0c;然后reolad一下&#xff0c;下载依…

windows驱动开发-WDM框架(一)

在前面的文章中解释过&#xff0c;NT5.0之后windows确定了新的架构Windows Driver Model (WDM)&#xff0c;在Vista之后又推出了Windows Driver Framework(WDF)&#xff0c;这两个都属于驱动程序框架&#xff0c;那么它们的之间的关系是怎样的&#xff1f; WDF是对WDM进行的封…

学习空间转换-3D转换

1.什么是空间转换&#xff1f; 使用的是transform属性实现元素在空间内的位移&#xff0c;旋转&#xff0c;缩放等效果。 空间&#xff1a;是从坐标轴角度定义的。x,y,z三条坐标轴构成的一个立体空间&#xff0c;Z轴位置与视线方向相同。 所以空间转换也被叫做3D转换 语法&a…

本地消息表模式保障分布式系统最终一致性

系统架构说明 状态转换说明 订单表消息表process_queue库存系统return_queue说明成功失败///订单库回滚成功成功失败//订单系统重发消息成功成功成功失败/Broker自动重试&#xff0c;注意接口幂等成功成功成功库存不足退回/Broker通知回掉&#xff0c;订单/消息作废成功成功成…

【做一名健康的CSDNer】《脱单恋爱秘籍》 —— 让爱情不再是难题

在这个快节奏的数字时代&#xff0c;程序员们以其独特的智慧和专业技能&#xff0c;为世界带来了翻天覆地的变化。然而&#xff0c;当代码和逻辑成为日常&#xff0c;爱情和人际关系的编程似乎变得复杂起来。为了帮助程序员们在爱情的道路上也能取得成功&#xff0c;我们精心打…

统一SQL-支持unpivot列转行

统一SQL介绍 https://www.light-pg.com/docs/LTSQL/current/index.html 源和目标 源数据库&#xff1a;Oracle 目标数据库&#xff1a;TDSQL-MySQL 操作目标 在Oracle中&#xff0c;可以使用unpivot将列转换成行&#xff0c;在TDSQL-MySQL中没有对应的功能&#xff0c;由…

Jenkins和gitlab实现CICD

1 背景 在开发TracerBackend服务的时候&#xff0c;每次更改代码之后需要推送到gitlab&#xff0c;然后ssh登录到Ubuntu的服务器上部署新的代码。服务成功启动之后&#xff0c;在本地执行测试用例&#xff0c;觉得这一套操作流程还是挺复杂的。想起公司的代码发布流程&#xf…